@atproto/bsync 0.0.18 → 0.0.20
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/CHANGELOG.md +13 -0
- package/LICENSE.txt +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +1 -0
- package/dist/context.js.map +1 -1
- package/dist/db/index.js +17 -7
- package/dist/db/index.js.map +1 -1
- package/dist/db/migrations/20250527T022203400Z-add-operation.d.ts +4 -0
- package/dist/db/migrations/20250527T022203400Z-add-operation.d.ts.map +1 -0
- package/dist/db/migrations/20250527T022203400Z-add-operation.js +21 -0
- package/dist/db/migrations/20250527T022203400Z-add-operation.js.map +1 -0
- package/dist/db/migrations/index.d.ts +1 -0
- package/dist/db/migrations/index.d.ts.map +1 -1
- package/dist/db/migrations/index.js +19 -8
- package/dist/db/migrations/index.js.map +1 -1
- package/dist/db/schema/index.d.ts +2 -1
- package/dist/db/schema/index.d.ts.map +1 -1
- package/dist/db/schema/operation.d.ts +18 -0
- package/dist/db/schema/operation.d.ts.map +1 -0
- package/dist/db/schema/operation.js +6 -0
- package/dist/db/schema/operation.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/proto/bsync_connect.d.ts +19 -1
- package/dist/proto/bsync_connect.d.ts.map +1 -1
- package/dist/proto/bsync_connect.js +18 -0
- package/dist/proto/bsync_connect.js.map +1 -1
- package/dist/proto/bsync_pb.d.ts +150 -0
- package/dist/proto/bsync_pb.d.ts.map +1 -1
- package/dist/proto/bsync_pb.js +401 -1
- package/dist/proto/bsync_pb.js.map +1 -1
- package/dist/routes/add-mute-operation.d.ts.map +1 -1
- package/dist/routes/add-notif-operation.d.ts.map +1 -1
- package/dist/routes/auth.d.ts.map +1 -1
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +4 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/put-operation.d.ts +6 -0
- package/dist/routes/put-operation.d.ts.map +1 -0
- package/dist/routes/put-operation.js +72 -0
- package/dist/routes/put-operation.js.map +1 -0
- package/dist/routes/scan-mute-operations.d.ts.map +1 -1
- package/dist/routes/scan-notif-operations.d.ts.map +1 -1
- package/dist/routes/scan-operations.d.ts +6 -0
- package/dist/routes/scan-operations.d.ts.map +1 -0
- package/dist/routes/scan-operations.js +59 -0
- package/dist/routes/scan-operations.js.map +1 -0
- package/dist/routes/util.d.ts.map +1 -1
- package/package.json +2 -2
- package/proto/bsync.proto +40 -0
- package/src/context.ts +2 -0
- package/src/db/migrations/20250527T022203400Z-add-operation.ts +20 -0
- package/src/db/migrations/index.ts +1 -0
- package/src/db/schema/index.ts +3 -1
- package/src/db/schema/operation.ts +20 -0
- package/src/index.ts +5 -0
- package/src/proto/bsync_connect.ts +22 -0
- package/src/proto/bsync_pb.ts +355 -0
- package/src/routes/index.ts +4 -0
- package/src/routes/put-operation.ts +101 -0
- package/src/routes/scan-operations.ts +67 -0
- package/tests/operations.test.ts +295 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.tests.tsbuildinfo +1 -1
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const connect_1 = require("@connectrpc/connect");
|
|
4
|
+
const kysely_1 = require("kysely");
|
|
5
|
+
const syntax_1 = require("@atproto/syntax");
|
|
6
|
+
const operation_1 = require("../db/schema/operation");
|
|
7
|
+
const bsync_pb_1 = require("../proto/bsync_pb");
|
|
8
|
+
const auth_1 = require("./auth");
|
|
9
|
+
const util_1 = require("./util");
|
|
10
|
+
exports.default = (ctx) => ({
|
|
11
|
+
async putOperation(req, handlerCtx) {
|
|
12
|
+
(0, auth_1.authWithApiKey)(ctx, handlerCtx);
|
|
13
|
+
const { db } = ctx;
|
|
14
|
+
const op = validateOp(req);
|
|
15
|
+
const id = await db.transaction(async (txn) => {
|
|
16
|
+
return putOp(txn, op);
|
|
17
|
+
});
|
|
18
|
+
return new bsync_pb_1.PutOperationResponse({
|
|
19
|
+
operation: {
|
|
20
|
+
id: String(id),
|
|
21
|
+
collection: op.collection,
|
|
22
|
+
actorDid: op.actorDid,
|
|
23
|
+
rkey: op.rkey,
|
|
24
|
+
method: op.method,
|
|
25
|
+
payload: op.payload,
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
const putOp = async (db, op) => {
|
|
31
|
+
const { ref } = db.db.dynamic;
|
|
32
|
+
const { id } = await db.db
|
|
33
|
+
.insertInto('operation')
|
|
34
|
+
.values({
|
|
35
|
+
collection: op.collection,
|
|
36
|
+
actorDid: op.actorDid,
|
|
37
|
+
rkey: op.rkey,
|
|
38
|
+
method: op.method,
|
|
39
|
+
payload: op.payload,
|
|
40
|
+
})
|
|
41
|
+
.returning('id')
|
|
42
|
+
.executeTakeFirstOrThrow();
|
|
43
|
+
await (0, kysely_1.sql) `notify ${ref(operation_1.createOperationChannel)}`.execute(db.db); // emitted transactionally
|
|
44
|
+
return id;
|
|
45
|
+
};
|
|
46
|
+
const validateOp = (req) => {
|
|
47
|
+
try {
|
|
48
|
+
(0, syntax_1.ensureValidNsid)(req.collection);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
throw new connect_1.ConnectError('operation collection is invalid NSID', connect_1.Code.InvalidArgument);
|
|
52
|
+
}
|
|
53
|
+
if (!(0, util_1.isValidDid)(req.actorDid)) {
|
|
54
|
+
throw new connect_1.ConnectError('operation actor_did is invalid DID', connect_1.Code.InvalidArgument);
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
(0, syntax_1.ensureValidRecordKey)(req.rkey);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
throw new connect_1.ConnectError('operation rkey is required', connect_1.Code.InvalidArgument);
|
|
61
|
+
}
|
|
62
|
+
if (req.method !== bsync_pb_1.Method.CREATE &&
|
|
63
|
+
req.method !== bsync_pb_1.Method.UPDATE &&
|
|
64
|
+
req.method !== bsync_pb_1.Method.DELETE) {
|
|
65
|
+
throw new connect_1.ConnectError('operation method is invalid', connect_1.Code.InvalidArgument);
|
|
66
|
+
}
|
|
67
|
+
if (req.method === bsync_pb_1.Method.DELETE && req.payload.length > 0) {
|
|
68
|
+
throw new connect_1.ConnectError('cannot specify a payload when method is DELETE', connect_1.Code.InvalidArgument);
|
|
69
|
+
}
|
|
70
|
+
return req;
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=put-operation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"put-operation.js","sourceRoot":"","sources":["../../src/routes/put-operation.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scan-mute-operations.d.ts","sourceRoot":"","sources":["../../src/routes/scan-mute-operations.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEvC,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"scan-mute-operations.d.ts","sourceRoot":"","sources":["../../src/routes/scan-mute-operations.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEvC,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;yBAKhC,KAAK,UAAU,KAAG,OAAO,CAAC,WAAW,CAAC,OAAO,OAAO,CAAC,CAAC;AAAtE,wBAuDE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scan-notif-operations.d.ts","sourceRoot":"","sources":["../../src/routes/scan-notif-operations.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEvC,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"scan-notif-operations.d.ts","sourceRoot":"","sources":["../../src/routes/scan-notif-operations.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEvC,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;yBAKhC,KAAK,UAAU,KAAG,OAAO,CAAC,WAAW,CAAC,OAAO,OAAO,CAAC,CAAC;AAAtE,wBAsDE"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ServiceImpl } from '@connectrpc/connect';
|
|
2
|
+
import { AppContext } from '../context';
|
|
3
|
+
import { Service } from '../proto/bsync_connect';
|
|
4
|
+
declare const _default: (ctx: AppContext) => Partial<ServiceImpl<typeof Service>>;
|
|
5
|
+
export default _default;
|
|
6
|
+
//# sourceMappingURL=scan-operations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan-operations.d.ts","sourceRoot":"","sources":["../../src/routes/scan-operations.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEvC,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;yBAKhC,KAAK,UAAU,KAAG,OAAO,CAAC,WAAW,CAAC,OAAO,OAAO,CAAC,CAAC;AAAtE,wBAyDE"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const node_events_1 = require("node:events");
|
|
4
|
+
const operation_1 = require("../db/schema/operation");
|
|
5
|
+
const bsync_pb_1 = require("../proto/bsync_pb");
|
|
6
|
+
const auth_1 = require("./auth");
|
|
7
|
+
const util_1 = require("./util");
|
|
8
|
+
exports.default = (ctx) => ({
|
|
9
|
+
async scanOperations(req, handlerCtx) {
|
|
10
|
+
(0, auth_1.authWithApiKey)(ctx, handlerCtx);
|
|
11
|
+
const { db, events } = ctx;
|
|
12
|
+
const limit = req.limit || 1000;
|
|
13
|
+
const cursor = (0, util_1.validCursor)(req.cursor);
|
|
14
|
+
const nextOpPromise = (0, node_events_1.once)(events, operation_1.createOperationChannel, {
|
|
15
|
+
signal: (0, util_1.combineSignals)(ctx.shutdown, AbortSignal.timeout(ctx.cfg.service.longPollTimeoutMs)),
|
|
16
|
+
});
|
|
17
|
+
nextOpPromise.catch(() => null); // ensure timeout is always handled
|
|
18
|
+
const nextOpPageQb = db.db
|
|
19
|
+
.selectFrom('operation')
|
|
20
|
+
.selectAll()
|
|
21
|
+
.where('id', '>', cursor ?? -1)
|
|
22
|
+
.orderBy('id', 'asc')
|
|
23
|
+
.limit(limit);
|
|
24
|
+
let ops = await nextOpPageQb.execute();
|
|
25
|
+
if (!ops.length) {
|
|
26
|
+
// if there were no ops on the page, wait for an event then try again.
|
|
27
|
+
try {
|
|
28
|
+
await nextOpPromise;
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
ctx.shutdown.throwIfAborted();
|
|
32
|
+
return new bsync_pb_1.ScanOperationsResponse({
|
|
33
|
+
operations: [],
|
|
34
|
+
cursor: req.cursor,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
ops = await nextOpPageQb.execute();
|
|
38
|
+
if (!ops.length) {
|
|
39
|
+
return new bsync_pb_1.ScanOperationsResponse({
|
|
40
|
+
operations: [],
|
|
41
|
+
cursor: req.cursor,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const lastOp = ops[ops.length - 1];
|
|
46
|
+
return new bsync_pb_1.ScanOperationsResponse({
|
|
47
|
+
operations: ops.map((op) => ({
|
|
48
|
+
id: op.id.toString(),
|
|
49
|
+
collection: op.collection,
|
|
50
|
+
actorDid: op.actorDid,
|
|
51
|
+
rkey: op.rkey,
|
|
52
|
+
method: op.method,
|
|
53
|
+
payload: op.payload,
|
|
54
|
+
})),
|
|
55
|
+
cursor: lastOp.id.toString(),
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
//# sourceMappingURL=scan-operations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan-operations.js","sourceRoot":"","sources":["../../src/routes/scan-operations.ts"],"names":[],"mappings":";;AAAA,6CAAkC;AAGlC,sDAA+D;AAE/D,gDAA0D;AAC1D,iCAAuC;AACvC,iCAAoD;AAEpD,kBAAe,CAAC,GAAe,EAAwC,EAAE,CAAC,CAAC;IACzE,KAAK,CAAC,cAAc,CAAC,GAAG,EAAE,UAAU;QAClC,IAAA,qBAAc,EAAC,GAAG,EAAE,UAAU,CAAC,CAAA;QAC/B,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,GAAG,CAAA;QAC1B,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,IAAI,IAAI,CAAA;QAC/B,MAAM,MAAM,GAAG,IAAA,kBAAW,EAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QACtC,MAAM,aAAa,GAAG,IAAA,kBAAI,EAAC,MAAM,EAAE,kCAAsB,EAAE;YACzD,MAAM,EAAE,IAAA,qBAAc,EACpB,GAAG,CAAC,QAAQ,EACZ,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,CACvD;SACF,CAAC,CAAA;QACF,aAAa,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAA,CAAC,mCAAmC;QAEnE,MAAM,YAAY,GAAG,EAAE,CAAC,EAAE;aACvB,UAAU,CAAC,WAAW,CAAC;aACvB,SAAS,EAAE;aACX,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,IAAI,CAAC,CAAC,CAAC;aAC9B,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;aACpB,KAAK,CAAC,KAAK,CAAC,CAAA;QAEf,IAAI,GAAG,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,CAAA;QAEtC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;YAChB,sEAAsE;YACtE,IAAI,CAAC;gBACH,MAAM,aAAa,CAAA;YACrB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,GAAG,CAAC,QAAQ,CAAC,cAAc,EAAE,CAAA;gBAC7B,OAAO,IAAI,iCAAsB,CAAC;oBAChC,UAAU,EAAE,EAAE;oBACd,MAAM,EAAE,GAAG,CAAC,MAAM;iBACnB,CAAC,CAAA;YACJ,CAAC;YACD,GAAG,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,CAAA;YAClC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;gBAChB,OAAO,IAAI,iCAAsB,CAAC;oBAChC,UAAU,EAAE,EAAE;oBACd,MAAM,EAAE,GAAG,CAAC,MAAM;iBACnB,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;QAED,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;QAElC,OAAO,IAAI,iCAAsB,CAAC;YAChC,UAAU,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;gBAC3B,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,EAAE;gBACpB,UAAU,EAAE,EAAE,CAAC,UAAU;gBACzB,QAAQ,EAAE,EAAE,CAAC,QAAQ;gBACrB,IAAI,EAAE,EAAE,CAAC,IAAI;gBACb,MAAM,EAAE,EAAE,CAAC,MAAM;gBACjB,OAAO,EAAE,EAAE,CAAC,OAAO;aACpB,CAAC,CAAC;YACH,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE;SAC7B,CAAC,CAAA;IACJ,CAAC;CACF,CAAC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/routes/util.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/routes/util.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,WAAW,GAAI,QAAQ,MAAM,KAAG,MAAM,GAAG,IAOrD,CAAA;AAED,eAAO,MAAM,cAAc,GAAI,GAAG,WAAW,EAAE,GAAG,WAAW,gBAa5D,CAAA;AAED,eAAO,MAAM,UAAU,GAAI,KAAK,MAAM,YAUrC,CAAA;AAED,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,YAOvC,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/bsync",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.20",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Sychronizing service for app.bsky App View (Bluesky API)",
|
|
6
6
|
"keywords": [
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"pg": "^8.10.0",
|
|
28
28
|
"pino-http": "^8.2.1",
|
|
29
29
|
"typed-emitter": "^2.1.0",
|
|
30
|
-
"@atproto/common": "^0.4.
|
|
30
|
+
"@atproto/common": "^0.4.11",
|
|
31
31
|
"@atproto/syntax": "^0.4.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
package/proto/bsync.proto
CHANGED
|
@@ -67,6 +67,44 @@ message ScanNotifOperationsResponse {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
|
|
70
|
+
enum Method {
|
|
71
|
+
METHOD_UNSPECIFIED = 0;
|
|
72
|
+
METHOD_CREATE = 1;
|
|
73
|
+
METHOD_UPDATE = 2;
|
|
74
|
+
METHOD_DELETE = 3;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
message Operation {
|
|
78
|
+
string id = 1;
|
|
79
|
+
string actor_did = 2;
|
|
80
|
+
string collection = 3;
|
|
81
|
+
string rkey = 4;
|
|
82
|
+
Method method = 5;
|
|
83
|
+
bytes payload = 6;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
message PutOperationRequest {
|
|
87
|
+
string collection = 1;
|
|
88
|
+
string actor_did = 2;
|
|
89
|
+
string rkey = 3;
|
|
90
|
+
Method method = 4;
|
|
91
|
+
bytes payload = 5;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
message PutOperationResponse {
|
|
95
|
+
Operation operation = 1;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
message ScanOperationsRequest {
|
|
99
|
+
string cursor = 1;
|
|
100
|
+
int32 limit = 2;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
message ScanOperationsResponse {
|
|
104
|
+
repeated Operation operations = 1;
|
|
105
|
+
string cursor = 2;
|
|
106
|
+
}
|
|
107
|
+
|
|
70
108
|
|
|
71
109
|
// Ping
|
|
72
110
|
message PingRequest {}
|
|
@@ -79,6 +117,8 @@ service Service {
|
|
|
79
117
|
rpc ScanMuteOperations(ScanMuteOperationsRequest) returns (ScanMuteOperationsResponse);
|
|
80
118
|
rpc AddNotifOperation(AddNotifOperationRequest) returns (AddNotifOperationResponse);
|
|
81
119
|
rpc ScanNotifOperations(ScanNotifOperationsRequest) returns (ScanNotifOperationsResponse);
|
|
120
|
+
rpc PutOperation(PutOperationRequest) returns (PutOperationResponse);
|
|
121
|
+
rpc ScanOperations(ScanOperationsRequest) returns (ScanOperationsResponse);
|
|
82
122
|
// Ping
|
|
83
123
|
rpc Ping(PingRequest) returns (PingResponse);
|
|
84
124
|
}
|
package/src/context.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { ServerConfig } from './config'
|
|
|
4
4
|
import { Database } from './db'
|
|
5
5
|
import { createMuteOpChannel } from './db/schema/mute_op'
|
|
6
6
|
import { createNotifOpChannel } from './db/schema/notif_op'
|
|
7
|
+
import { createOperationChannel } from './db/schema/operation'
|
|
7
8
|
|
|
8
9
|
export type AppContextOptions = {
|
|
9
10
|
db: Database
|
|
@@ -43,4 +44,5 @@ export class AppContext {
|
|
|
43
44
|
export type AppEvents = {
|
|
44
45
|
[createMuteOpChannel]: () => void
|
|
45
46
|
[createNotifOpChannel]: () => void
|
|
47
|
+
[createOperationChannel]: () => void
|
|
46
48
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Kysely, sql } from 'kysely'
|
|
2
|
+
|
|
3
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
4
|
+
await db.schema
|
|
5
|
+
.createTable('operation')
|
|
6
|
+
.addColumn('id', 'bigserial', (col) => col.primaryKey())
|
|
7
|
+
.addColumn('collection', 'varchar', (col) => col.notNull())
|
|
8
|
+
.addColumn('actorDid', 'varchar', (col) => col.notNull())
|
|
9
|
+
.addColumn('rkey', 'varchar', (col) => col.notNull())
|
|
10
|
+
.addColumn('method', 'int2', (col) => col.notNull())
|
|
11
|
+
.addColumn('payload', sql`bytea`)
|
|
12
|
+
.addColumn('createdAt', 'timestamptz', (col) =>
|
|
13
|
+
col.notNull().defaultTo(sql`CURRENT_TIMESTAMP`),
|
|
14
|
+
)
|
|
15
|
+
.execute()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
19
|
+
await db.schema.dropTable('operation').execute()
|
|
20
|
+
}
|
package/src/db/schema/index.ts
CHANGED
|
@@ -3,11 +3,13 @@ import * as muteItem from './mute_item'
|
|
|
3
3
|
import * as muteOp from './mute_op'
|
|
4
4
|
import * as notifItem from './notif_item'
|
|
5
5
|
import * as notifOp from './notif_op'
|
|
6
|
+
import * as op from './operation'
|
|
6
7
|
|
|
7
8
|
export type DatabaseSchemaType = muteItem.PartialDB &
|
|
8
9
|
muteOp.PartialDB &
|
|
9
10
|
notifItem.PartialDB &
|
|
10
|
-
notifOp.PartialDB
|
|
11
|
+
notifOp.PartialDB &
|
|
12
|
+
op.PartialDB
|
|
11
13
|
|
|
12
14
|
export type DatabaseSchema = Kysely<DatabaseSchemaType>
|
|
13
15
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { GeneratedAlways } from 'kysely'
|
|
2
|
+
import { Method } from '../../proto/bsync_pb'
|
|
3
|
+
|
|
4
|
+
export type OperationMethod = Method.CREATE | Method.UPDATE | Method.DELETE
|
|
5
|
+
|
|
6
|
+
export interface Operation {
|
|
7
|
+
id: GeneratedAlways<number>
|
|
8
|
+
collection: string
|
|
9
|
+
actorDid: string
|
|
10
|
+
rkey: string
|
|
11
|
+
method: OperationMethod
|
|
12
|
+
payload: Uint8Array
|
|
13
|
+
createdAt: GeneratedAlways<Date>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const tableName = 'operation'
|
|
17
|
+
|
|
18
|
+
export type PartialDB = { [tableName]: Operation }
|
|
19
|
+
|
|
20
|
+
export const createOperationChannel = 'operation_create' // used with listen/notify
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { ServerConfig } from './config'
|
|
|
6
6
|
import { AppContext, AppContextOptions } from './context'
|
|
7
7
|
import { createMuteOpChannel } from './db/schema/mute_op'
|
|
8
8
|
import { createNotifOpChannel } from './db/schema/notif_op'
|
|
9
|
+
import { createOperationChannel } from './db/schema/operation'
|
|
9
10
|
import { dbLogger, loggerMiddleware } from './logger'
|
|
10
11
|
import routes from './routes'
|
|
11
12
|
|
|
@@ -92,6 +93,7 @@ export class BsyncService {
|
|
|
92
93
|
// if these error, unhandled rejection should cause process to exit
|
|
93
94
|
conn.query(`listen ${createMuteOpChannel}`)
|
|
94
95
|
conn.query(`listen ${createNotifOpChannel}`)
|
|
96
|
+
conn.query(`listen ${createOperationChannel}`)
|
|
95
97
|
conn.on('notification', (notif) => {
|
|
96
98
|
if (notif.channel === createMuteOpChannel) {
|
|
97
99
|
this.ctx.events.emit(createMuteOpChannel)
|
|
@@ -99,6 +101,9 @@ export class BsyncService {
|
|
|
99
101
|
if (notif.channel === createNotifOpChannel) {
|
|
100
102
|
this.ctx.events.emit(createNotifOpChannel)
|
|
101
103
|
}
|
|
104
|
+
if (notif.channel === createOperationChannel) {
|
|
105
|
+
this.ctx.events.emit(createOperationChannel)
|
|
106
|
+
}
|
|
102
107
|
})
|
|
103
108
|
}
|
|
104
109
|
}
|
|
@@ -10,10 +10,14 @@ import {
|
|
|
10
10
|
AddNotifOperationResponse,
|
|
11
11
|
PingRequest,
|
|
12
12
|
PingResponse,
|
|
13
|
+
PutOperationRequest,
|
|
14
|
+
PutOperationResponse,
|
|
13
15
|
ScanMuteOperationsRequest,
|
|
14
16
|
ScanMuteOperationsResponse,
|
|
15
17
|
ScanNotifOperationsRequest,
|
|
16
18
|
ScanNotifOperationsResponse,
|
|
19
|
+
ScanOperationsRequest,
|
|
20
|
+
ScanOperationsResponse,
|
|
17
21
|
} from './bsync_pb'
|
|
18
22
|
import { MethodKind } from '@bufbuild/protobuf'
|
|
19
23
|
|
|
@@ -61,6 +65,24 @@ export const Service = {
|
|
|
61
65
|
O: ScanNotifOperationsResponse,
|
|
62
66
|
kind: MethodKind.Unary,
|
|
63
67
|
},
|
|
68
|
+
/**
|
|
69
|
+
* @generated from rpc bsync.Service.PutOperation
|
|
70
|
+
*/
|
|
71
|
+
putOperation: {
|
|
72
|
+
name: 'PutOperation',
|
|
73
|
+
I: PutOperationRequest,
|
|
74
|
+
O: PutOperationResponse,
|
|
75
|
+
kind: MethodKind.Unary,
|
|
76
|
+
},
|
|
77
|
+
/**
|
|
78
|
+
* @generated from rpc bsync.Service.ScanOperations
|
|
79
|
+
*/
|
|
80
|
+
scanOperations: {
|
|
81
|
+
name: 'ScanOperations',
|
|
82
|
+
I: ScanOperationsRequest,
|
|
83
|
+
O: ScanOperationsResponse,
|
|
84
|
+
kind: MethodKind.Unary,
|
|
85
|
+
},
|
|
64
86
|
/**
|
|
65
87
|
* Ping
|
|
66
88
|
*
|