diff --git a/servers/themes/package-lock.json b/servers/themes/package-lock.json
index ddae93595..bbbcb20c2 100644
--- a/servers/themes/package-lock.json
+++ b/servers/themes/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@notesnook/themes-server",
- "version": "1.0.2",
+ "version": "1.0.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@notesnook/themes-server",
- "version": "1.0.2",
+ "version": "1.0.3",
"license": "ISC",
"dependencies": {
"@notesnook/theme": "file:../../packages/theme",
diff --git a/servers/themes/package.json b/servers/themes/package.json
index 62fe4d014..9efadb5e2 100644
--- a/servers/themes/package.json
+++ b/servers/themes/package.json
@@ -1,6 +1,6 @@
{
"name": "@notesnook/themes-server",
- "version": "1.0.2",
+ "version": "1.0.3",
"description": "A simple rest api for notesnook themes",
"private": "true",
"main": "src/index.ts",
diff --git a/servers/themes/src/api.ts b/servers/themes/src/api.ts
index bd64fcc5b..3fb03f8ea 100644
--- a/servers/themes/src/api.ts
+++ b/servers/themes/src/api.ts
@@ -19,7 +19,7 @@ along with this program. If not, see .
import { z } from "zod";
import { InstallsCounter } from "./constants";
-import { findTheme, getThemes } from "./orama";
+import { findTheme, getThemes, updateTotalInstalls } from "./orama";
import { syncThemes } from "./sync";
import { publicProcedure, router } from "./trpc";
import { THEME_COMPATIBILITY_VERSION } from "@notesnook/theme";
@@ -41,7 +41,12 @@ export const ThemesAPI = router({
const theme = await findTheme(id, compatibilityVersion);
if (!theme) return;
- if (userId) await InstallsCounter.increment(theme.id, userId);
+ if (userId) {
+ updateTotalInstalls(
+ theme,
+ await InstallsCounter.increment(theme.id, userId)
+ );
+ }
return theme;
}),
updateTheme: publicProcedure
diff --git a/servers/themes/src/counter/fs.ts b/servers/themes/src/counter/fs.ts
index 0003036eb..e06ab0e0a 100644
--- a/servers/themes/src/counter/fs.ts
+++ b/servers/themes/src/counter/fs.ts
@@ -30,12 +30,13 @@ export class FsCounter {
}
async increment(key: string, uid: string) {
- await this.mutex.runExclusive(async () => {
+ return await this.mutex.runExclusive(async () => {
const counts = await this.all();
counts[key] = counts[key] || [];
- if (counts[key].includes(uid)) return;
+ if (counts[key].includes(uid)) return counts[key].length;
counts[key].push(uid);
await this.save(counts);
+ return counts[key].length;
});
}
diff --git a/servers/themes/src/counter/kv.ts b/servers/themes/src/counter/kv.ts
index 9b30759c6..378ecf37e 100644
--- a/servers/themes/src/counter/kv.ts
+++ b/servers/themes/src/counter/kv.ts
@@ -28,6 +28,7 @@ type WorkersKVRESTConfig = {
export class KVCounter {
private readonly client: Cloudflare;
private readonly mutex: Mutex;
+ private installs: Record = {};
constructor(private readonly config: WorkersKVRESTConfig) {
this.mutex = new Mutex();
this.client = new Cloudflare({
@@ -36,15 +37,12 @@ export class KVCounter {
}
async increment(key: string, uid: string) {
- await this.mutex.runExclusive(async () => {
- const installs = await readMulti(this.client, this.config, [key]);
- const existing = installs[key] || [];
- await write(
- this.client,
- this.config,
- key,
- Array.from(new Set([...existing, uid]))
- );
+ return await this.mutex.runExclusive(async () => {
+ const existing = this.installs[key] || [];
+ const installsSet = Array.from(new Set([...existing, uid]));
+ await write(this.client, this.config, key, installsSet);
+ this.installs[key] = installsSet;
+ return installsSet.length;
});
}
@@ -54,6 +52,7 @@ export class KVCounter {
for (const [key, value] of Object.entries(installs)) {
result[key] = value.length;
}
+ this.installs = installs;
return result;
}
}
@@ -87,13 +86,8 @@ function write(
key: string,
data: T
) {
- return client.kv.namespaces.bulkUpdate(config.namespaceId, {
+ return client.kv.namespaces.values.update(config.namespaceId, key, {
account_id: config.cfAccountId,
- body: [
- {
- key,
- value: JSON.stringify(data)
- }
- ]
+ value: JSON.stringify(data)
});
}
diff --git a/servers/themes/src/orama.ts b/servers/themes/src/orama.ts
index 744bec650..8f970c97f 100644
--- a/servers/themes/src/orama.ts
+++ b/servers/themes/src/orama.ts
@@ -16,7 +16,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import { Orama, SearchParams, create, search } from "@orama/orama";
+import { Orama, SearchParams, create, search, update } from "@orama/orama";
import { CompiledThemeDefinition, ThemeMetadata } from "./sync";
import { ThemeQuerySchema } from "./schemas";
@@ -30,7 +30,8 @@ export async function initializeDatabase(): Promise {
colorScheme: "string",
compatibilityVersion: "number",
description: "string",
- tags: "string[]"
+ tags: "string[]",
+ totalInstalls: "number"
},
id: "notesnook-themes"
});
@@ -53,6 +54,14 @@ export async function findTheme(
return results.hits[0].document as CompiledThemeDefinition;
}
+export async function updateTotalInstalls(
+ theme: CompiledThemeDefinition,
+ totalInstalls: number
+) {
+ if (!ThemesDatabase) await initializeDatabase();
+ await update(ThemesDatabase!, theme.id, { ...theme, totalInstalls });
+}
+
export async function getThemes(query: (typeof ThemeQuerySchema)["_type"]) {
if (!ThemesDatabase) await initializeDatabase();
@@ -60,6 +69,10 @@ export async function getThemes(query: (typeof ThemeQuerySchema)["_type"]) {
const count = query.limit;
const searchParams: SearchParams = {
+ sortBy: {
+ property: "totalInstalls",
+ order: "DESC"
+ },
where: {
compatibilityVersion: {
eq: query.compatibilityVersion
diff --git a/servers/themes/src/server.ts b/servers/themes/src/server.ts
index d1c0d5dce..63895bfd5 100644
--- a/servers/themes/src/server.ts
+++ b/servers/themes/src/server.ts
@@ -32,6 +32,9 @@ server.listen(PORT, HOST);
console.log(`Server started successfully on: http://${HOST}:${PORT}/`);
syncThemes();
+setInterval(() => {
+ syncThemes();
+}, 1000 * 60 * 60); // every hour
if (import.meta.hot) {
import.meta.hot.on("vite:beforeFullReload", () => {