@codemem/server 0.22.3 → 0.22.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +42 -10
- package/dist/index.js.map +1 -1
- package/dist/routes/sync.d.ts.map +1 -1
- package/package.json +3 -3
- package/static/app.js +73 -14
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Hono } from "hono";
|
|
|
6
6
|
import { createMiddleware } from "hono/factory";
|
|
7
7
|
import { RateLimiter } from "limiter";
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
|
-
import { count, desc, eq, inArray, isNotNull, max, ne } from "drizzle-orm";
|
|
9
|
+
import { and, count, desc, eq, inArray, isNotNull, max, ne } from "drizzle-orm";
|
|
10
10
|
import { drizzle } from "drizzle-orm/better-sqlite3";
|
|
11
11
|
import { createHash, randomUUID } from "node:crypto";
|
|
12
12
|
import net from "node:net";
|
|
@@ -1880,7 +1880,7 @@ function syncProtocolRoutes(getStore, opts = {}) {
|
|
|
1880
1880
|
const baselineCursor = c.req.query("baseline_cursor") ?? null;
|
|
1881
1881
|
const pageToken = c.req.query("page_token") ?? null;
|
|
1882
1882
|
const rawLimit = Number.parseInt(c.req.query("limit") ?? "200", 10);
|
|
1883
|
-
const limit = Number.isFinite(rawLimit) ? Math.max(1, Math.min(rawLimit,
|
|
1883
|
+
const limit = Number.isFinite(rawLimit) ? Math.max(1, Math.min(rawLimit, 5e3)) : 200;
|
|
1884
1884
|
if (generation == null || !Number.isFinite(generation)) return c.json({ error: "missing_generation" }, 400);
|
|
1885
1885
|
if (!snapshotId) return c.json({ error: "missing_snapshot_id" }, 400);
|
|
1886
1886
|
const result = loadMemorySnapshotPageForPeer(store.db, {
|
|
@@ -2036,7 +2036,7 @@ function syncRoutes(getStore, getSyncRuntimeStatus) {
|
|
|
2036
2036
|
});
|
|
2037
2037
|
const peersMap = {};
|
|
2038
2038
|
for (const peer of peersItems) peersMap[String(peer.peer_device_id)] = peer.status;
|
|
2039
|
-
const
|
|
2039
|
+
const attemptRows = d.select({
|
|
2040
2040
|
peer_device_id: schema.syncAttempts.peer_device_id,
|
|
2041
2041
|
ok: schema.syncAttempts.ok,
|
|
2042
2042
|
error: schema.syncAttempts.error,
|
|
@@ -2044,11 +2044,20 @@ function syncRoutes(getStore, getSyncRuntimeStatus) {
|
|
|
2044
2044
|
finished_at: schema.syncAttempts.finished_at,
|
|
2045
2045
|
ops_in: schema.syncAttempts.ops_in,
|
|
2046
2046
|
ops_out: schema.syncAttempts.ops_out
|
|
2047
|
-
}).from(schema.syncAttempts).orderBy(desc(schema.syncAttempts.finished_at)).limit(25).all()
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2047
|
+
}).from(schema.syncAttempts).orderBy(desc(schema.syncAttempts.finished_at)).limit(25).all();
|
|
2048
|
+
const peerAddressMap = /* @__PURE__ */ new Map();
|
|
2049
|
+
store.db.prepare("SELECT peer_device_id, addresses_json FROM sync_peers").all().forEach((peerRow) => {
|
|
2050
|
+
const addrs = safeJsonList(peerRow.addresses_json);
|
|
2051
|
+
if (addrs.length) peerAddressMap.set(String(peerRow.peer_device_id ?? ""), addrs);
|
|
2052
|
+
});
|
|
2053
|
+
const attemptsItems = attemptRows.map((row) => {
|
|
2054
|
+
const addrs = showDiag ? peerAddressMap.get(String(row.peer_device_id ?? "")) : void 0;
|
|
2055
|
+
return {
|
|
2056
|
+
...row,
|
|
2057
|
+
status: attemptStatus(row),
|
|
2058
|
+
address: addrs?.length ? addrs[0] : null
|
|
2059
|
+
};
|
|
2060
|
+
});
|
|
2052
2061
|
const latestAttemptError = String(attemptsItems[0]?.error || "").trim();
|
|
2053
2062
|
const statusBlock = {
|
|
2054
2063
|
...statusPayload,
|
|
@@ -2118,7 +2127,7 @@ function syncRoutes(getStore, getSyncRuntimeStatus) {
|
|
|
2118
2127
|
const d = drizzle(store.db, { schema });
|
|
2119
2128
|
const includeMerged = queryBool(c.req.query("includeMerged"));
|
|
2120
2129
|
const query = d.select().from(schema.actors);
|
|
2121
|
-
const rows = includeMerged ? query.orderBy(schema.actors.display_name).all() : query.where(ne(schema.actors.status, "merged")).orderBy(schema.actors.display_name).all();
|
|
2130
|
+
const rows = includeMerged ? query.orderBy(schema.actors.display_name).all() : query.where(and(ne(schema.actors.status, "merged"), ne(schema.actors.status, "deactivated"))).orderBy(schema.actors.display_name).all();
|
|
2122
2131
|
return c.json({ items: rows });
|
|
2123
2132
|
}
|
|
2124
2133
|
});
|
|
@@ -2371,7 +2380,7 @@ function syncRoutes(getStore, getSyncRuntimeStatus) {
|
|
|
2371
2380
|
if (!primary || !secondary) return c.json({ error: "actor not found" }, 404);
|
|
2372
2381
|
if (primary.status !== "active") return c.json({ error: "primary actor not active" }, 409);
|
|
2373
2382
|
if (secondary.status !== "active") return c.json({ error: "secondary actor not active" }, 409);
|
|
2374
|
-
if (secondary.is_local) return c.json({ error: "cannot merge local actor" }, 409);
|
|
2383
|
+
if (secondary.is_local && secondary.actor_id === store.actorId) return c.json({ error: "cannot merge this device's own local actor" }, 409);
|
|
2375
2384
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2376
2385
|
const mergedCount = store.db.transaction(() => {
|
|
2377
2386
|
const peerUpdate = d.update(schema.syncPeers).set({ actor_id: primaryActorId }).where(eq(schema.syncPeers.actor_id, secondaryActorId)).run();
|
|
@@ -2385,6 +2394,29 @@ function syncRoutes(getStore, getSyncRuntimeStatus) {
|
|
|
2385
2394
|
})();
|
|
2386
2395
|
return c.json({ merged_count: mergedCount });
|
|
2387
2396
|
});
|
|
2397
|
+
app.post("/api/sync/actors/deactivate", async (c) => {
|
|
2398
|
+
const store = getStore();
|
|
2399
|
+
let body;
|
|
2400
|
+
try {
|
|
2401
|
+
body = await c.req.json();
|
|
2402
|
+
} catch {
|
|
2403
|
+
return c.json({ error: "invalid json" }, 400);
|
|
2404
|
+
}
|
|
2405
|
+
const actorId = String(body.actor_id ?? "").trim();
|
|
2406
|
+
if (!actorId) return c.json({ error: "actor_id required" }, 400);
|
|
2407
|
+
if (actorId === store.actorId) return c.json({ error: "cannot deactivate this device's own local actor" }, 409);
|
|
2408
|
+
const d = drizzle(store.db, { schema });
|
|
2409
|
+
const actor = d.select().from(schema.actors).where(eq(schema.actors.actor_id, actorId)).get();
|
|
2410
|
+
if (!actor) return c.json({ error: "actor not found" }, 404);
|
|
2411
|
+
if (actor.status !== "active") return c.json({ error: "actor not active" }, 409);
|
|
2412
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2413
|
+
d.update(schema.actors).set({
|
|
2414
|
+
status: "deactivated",
|
|
2415
|
+
updated_at: now
|
|
2416
|
+
}).where(eq(schema.actors.actor_id, actorId)).run();
|
|
2417
|
+
d.update(schema.syncPeers).set({ actor_id: null }).where(eq(schema.syncPeers.actor_id, actorId)).run();
|
|
2418
|
+
return c.json({ ok: true });
|
|
2419
|
+
});
|
|
2388
2420
|
app.post("/api/sync/legacy-devices/claim", async (c) => {
|
|
2389
2421
|
const store = getStore();
|
|
2390
2422
|
let body;
|