@atproto/bsync 0.0.0 → 0.0.2-next.0
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 +16 -0
- package/LICENSE.txt +1 -1
- package/buf.gen.yaml +2 -3
- package/dist/client.d.ts +2 -1
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +17 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +53 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +5 -1
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +54 -0
- package/dist/context.js.map +1 -0
- package/dist/db/index.d.ts +1 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +216 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/migrations/20240108T220751294Z-init.d.ts +1 -0
- package/dist/db/migrations/20240108T220751294Z-init.d.ts.map +1 -0
- package/dist/db/migrations/20240108T220751294Z-init.js +28 -0
- package/dist/db/migrations/20240108T220751294Z-init.js.map +1 -0
- package/dist/db/migrations/index.d.ts +1 -0
- package/dist/db/migrations/index.d.ts.map +1 -0
- package/dist/db/migrations/index.js +31 -0
- package/dist/db/migrations/index.js.map +1 -0
- package/dist/db/migrations/provider.d.ts +1 -0
- package/dist/db/migrations/provider.d.ts.map +1 -0
- package/dist/db/migrations/provider.js +18 -0
- package/dist/db/migrations/provider.js.map +1 -0
- package/dist/db/schema/index.d.ts +1 -0
- package/dist/db/schema/index.d.ts.map +1 -0
- package/dist/db/schema/index.js +3 -0
- package/dist/db/schema/index.js.map +1 -0
- package/dist/db/schema/mute_item.d.ts +1 -0
- package/dist/db/schema/mute_item.d.ts.map +1 -0
- package/dist/db/schema/mute_item.js +5 -0
- package/dist/db/schema/mute_item.js.map +1 -0
- package/dist/db/schema/mute_op.d.ts +2 -1
- package/dist/db/schema/mute_op.d.ts.map +1 -0
- package/dist/db/schema/mute_op.js +6 -0
- package/dist/db/schema/mute_op.js.map +1 -0
- package/dist/db/types.d.ts +1 -0
- package/dist/db/types.d.ts.map +1 -0
- package/dist/db/types.js +3 -0
- package/dist/db/types.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +130 -76448
- package/dist/index.js.map +1 -7
- package/dist/logger.d.ts +1 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +25 -0
- package/dist/logger.js.map +1 -0
- package/dist/{gen → proto}/bsync_connect.d.ts +18 -1
- package/dist/proto/bsync_connect.d.ts.map +1 -0
- package/dist/proto/bsync_connect.js +49 -0
- package/dist/proto/bsync_connect.js.map +1 -0
- package/dist/{gen → proto}/bsync_pb.d.ts +75 -0
- package/dist/proto/bsync_pb.d.ts.map +1 -0
- package/dist/proto/bsync_pb.js +461 -0
- package/dist/proto/bsync_pb.js.map +1 -0
- package/dist/routes/add-mute-operation.d.ts +2 -1
- package/dist/routes/add-mute-operation.d.ts.map +1 -0
- package/dist/routes/add-mute-operation.js +136 -0
- package/dist/routes/add-mute-operation.js.map +1 -0
- package/dist/routes/auth.d.ts +1 -0
- package/dist/routes/auth.d.ts.map +1 -0
- package/dist/routes/auth.js +17 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/index.d.ts +1 -0
- package/dist/routes/index.d.ts.map +1 -0
- package/dist/routes/index.js +21 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/scan-mute-operations.d.ts +2 -1
- package/dist/routes/scan-mute-operations.d.ts.map +1 -0
- package/dist/routes/scan-mute-operations.js +80 -0
- package/dist/routes/scan-mute-operations.js.map +1 -0
- package/jest.config.js +3 -3
- package/package.json +9 -9
- package/src/client.ts +1 -1
- package/src/context.ts +5 -1
- package/src/db/index.ts +33 -31
- package/src/db/schema/mute_op.ts +1 -1
- package/src/index.ts +17 -2
- package/src/{gen → proto}/bsync_connect.ts +1 -1
- package/src/routes/add-mute-operation.ts +2 -2
- package/src/routes/index.ts +1 -1
- package/src/routes/scan-mute-operations.ts +22 -3
- package/tests/mutes.test.ts +1 -1
- package/tsconfig.build.json +6 -2
- package/tsconfig.json +3 -10
- package/tsconfig.tests.json +7 -0
- package/babel.config.js +0 -3
- package/build.js +0 -18
- /package/src/{gen → proto}/bsync_pb.ts +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const kysely_1 = require("kysely");
|
|
7
|
+
const bsync_connect_1 = require("../proto/bsync_connect");
|
|
8
|
+
const add_mute_operation_1 = __importDefault(require("./add-mute-operation"));
|
|
9
|
+
const scan_mute_operations_1 = __importDefault(require("./scan-mute-operations"));
|
|
10
|
+
exports.default = (ctx) => (router) => {
|
|
11
|
+
return router.service(bsync_connect_1.Service, {
|
|
12
|
+
...(0, add_mute_operation_1.default)(ctx),
|
|
13
|
+
...(0, scan_mute_operations_1.default)(ctx),
|
|
14
|
+
async ping() {
|
|
15
|
+
const { db } = ctx;
|
|
16
|
+
await (0, kysely_1.sql) `select 1`.execute(db.db);
|
|
17
|
+
return {};
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/routes/index.ts"],"names":[],"mappings":";;;;;AAAA,mCAA4B;AAE5B,0DAAgD;AAEhD,8EAAmD;AACnD,kFAAuD;AAEvD,kBAAe,CAAC,GAAe,EAAE,EAAE,CAAC,CAAC,MAAqB,EAAE,EAAE;IAC5D,OAAO,MAAM,CAAC,OAAO,CAAC,uBAAO,EAAE;QAC7B,GAAG,IAAA,4BAAgB,EAAC,GAAG,CAAC;QACxB,GAAG,IAAA,8BAAkB,EAAC,GAAG,CAAC;QAC1B,KAAK,CAAC,IAAI;YACR,MAAM,EAAE,EAAE,EAAE,GAAG,GAAG,CAAA;YAClB,MAAM,IAAA,YAAG,EAAA,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;YAClC,OAAO,EAAE,CAAA;QACX,CAAC;KACF,CAAC,CAAA;AACJ,CAAC,CAAA"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ServiceImpl } from '@connectrpc/connect';
|
|
2
|
-
import { Service } from '../
|
|
2
|
+
import { Service } from '../proto/bsync_connect';
|
|
3
3
|
import AppContext from '../context';
|
|
4
4
|
declare const _default: (ctx: AppContext) => Partial<ServiceImpl<typeof Service>>;
|
|
5
5
|
export default _default;
|
|
6
|
+
//# sourceMappingURL=scan-mute-operations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan-mute-operations.d.ts","sourceRoot":"","sources":["../../src/routes/scan-mute-operations.ts"],"names":[],"mappings":"AACA,OAAO,EAAsB,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACrE,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAEhD,OAAO,UAAU,MAAM,YAAY,CAAA;8BAId,UAAU,KAAG,QAAQ,YAAY,cAAc,CAAC,CAAC;AAAtE,wBAuDE"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const node_events_1 = require("node:events");
|
|
4
|
+
const connect_1 = require("@connectrpc/connect");
|
|
5
|
+
const bsync_pb_1 = require("../proto/bsync_pb");
|
|
6
|
+
const mute_op_1 = require("../db/schema/mute_op");
|
|
7
|
+
const auth_1 = require("./auth");
|
|
8
|
+
exports.default = (ctx) => ({
|
|
9
|
+
async scanMuteOperations(req, handlerCtx) {
|
|
10
|
+
(0, auth_1.authWithApiKey)(ctx, handlerCtx);
|
|
11
|
+
const { db, events } = ctx;
|
|
12
|
+
const limit = req.limit || 1000;
|
|
13
|
+
const cursor = validCursor(req.cursor);
|
|
14
|
+
const nextMuteOpPromise = (0, node_events_1.once)(events, mute_op_1.createMuteOpChannel, {
|
|
15
|
+
signal: combineSignals(ctx.shutdown, AbortSignal.timeout(ctx.cfg.service.longPollTimeoutMs)),
|
|
16
|
+
});
|
|
17
|
+
nextMuteOpPromise.catch(() => null); // ensure timeout is always handled
|
|
18
|
+
const nextMuteOpPageQb = db.db
|
|
19
|
+
.selectFrom('mute_op')
|
|
20
|
+
.selectAll()
|
|
21
|
+
.where('id', '>', cursor ?? -1)
|
|
22
|
+
.orderBy('id', 'asc')
|
|
23
|
+
.limit(limit);
|
|
24
|
+
let ops = await nextMuteOpPageQb.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 nextMuteOpPromise;
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
ctx.shutdown.throwIfAborted();
|
|
32
|
+
return new bsync_pb_1.ScanMuteOperationsResponse({
|
|
33
|
+
operations: [],
|
|
34
|
+
cursor: req.cursor,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
ops = await nextMuteOpPageQb.execute();
|
|
38
|
+
if (!ops.length) {
|
|
39
|
+
return new bsync_pb_1.ScanMuteOperationsResponse({
|
|
40
|
+
operations: [],
|
|
41
|
+
cursor: req.cursor,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const lastOp = ops[ops.length - 1];
|
|
46
|
+
return new bsync_pb_1.ScanMuteOperationsResponse({
|
|
47
|
+
operations: ops.map((op) => ({
|
|
48
|
+
id: op.id.toString(),
|
|
49
|
+
type: op.type,
|
|
50
|
+
actorDid: op.actorDid,
|
|
51
|
+
subject: op.subject,
|
|
52
|
+
})),
|
|
53
|
+
cursor: lastOp.id.toString(),
|
|
54
|
+
});
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
const validCursor = (cursor) => {
|
|
58
|
+
if (cursor === '')
|
|
59
|
+
return null;
|
|
60
|
+
const int = parseInt(cursor, 10);
|
|
61
|
+
if (isNaN(int) || int < 0) {
|
|
62
|
+
throw new connect_1.ConnectError('invalid cursor', connect_1.Code.InvalidArgument);
|
|
63
|
+
}
|
|
64
|
+
return int;
|
|
65
|
+
};
|
|
66
|
+
const combineSignals = (a, b) => {
|
|
67
|
+
const controller = new AbortController();
|
|
68
|
+
for (const signal of [a, b]) {
|
|
69
|
+
if (signal.aborted) {
|
|
70
|
+
controller.abort();
|
|
71
|
+
return signal;
|
|
72
|
+
}
|
|
73
|
+
signal.addEventListener('abort', () => controller.abort(signal.reason), {
|
|
74
|
+
// @ts-ignore https://github.com/DefinitelyTyped/DefinitelyTyped/pull/68625
|
|
75
|
+
signal: controller.signal,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return controller.signal;
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=scan-mute-operations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan-mute-operations.js","sourceRoot":"","sources":["../../src/routes/scan-mute-operations.ts"],"names":[],"mappings":"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"}
|
package/jest.config.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/bsync",
|
|
3
|
-
"version": "0.0.0",
|
|
3
|
+
"version": "0.0.2-next.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Sychronizing service for app.bsky App View (Bluesky API)",
|
|
6
6
|
"keywords": [
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"directory": "packages/bsync"
|
|
15
15
|
},
|
|
16
16
|
"main": "dist/index.js",
|
|
17
|
+
"types": "dist/index.d.ts",
|
|
17
18
|
"dependencies": {
|
|
18
19
|
"@bufbuild/protobuf": "^1.5.0",
|
|
19
20
|
"@connectrpc/connect": "^1.1.4",
|
|
@@ -25,25 +26,24 @@
|
|
|
25
26
|
"pino": "^8.15.0",
|
|
26
27
|
"pino-http": "^8.2.1",
|
|
27
28
|
"typed-emitter": "^2.1.0",
|
|
28
|
-
"@atproto/common": "^0.3.
|
|
29
|
-
"@atproto/syntax": "^0.1.
|
|
29
|
+
"@atproto/common": "^0.3.4-next.0",
|
|
30
|
+
"@atproto/syntax": "^0.2.1-next.0"
|
|
30
31
|
},
|
|
31
32
|
"devDependencies": {
|
|
32
33
|
"@bufbuild/buf": "^1.28.1",
|
|
33
34
|
"@bufbuild/protoc-gen-es": "^1.5.0",
|
|
34
35
|
"@connectrpc/protoc-gen-connect-es": "^1.1.4",
|
|
35
|
-
"@types/pg": "^8.6.6"
|
|
36
|
+
"@types/pg": "^8.6.6",
|
|
37
|
+
"jest": "^28.1.2",
|
|
38
|
+
"ts-node": "^10.8.2"
|
|
36
39
|
},
|
|
37
40
|
"scripts": {
|
|
38
|
-
"build": "
|
|
39
|
-
"postbuild": "tsc --build tsconfig.build.json",
|
|
40
|
-
"update-main-to-dist": "node ../../update-main-to-dist.js packages/bsync",
|
|
41
|
+
"build": "tsc --build tsconfig.build.json",
|
|
41
42
|
"start": "node --enable-source-maps dist/bin.js",
|
|
42
43
|
"test": "../dev-infra/with-test-db.sh jest",
|
|
43
44
|
"test:log": "tail -50 test.log | pino-pretty",
|
|
44
45
|
"test:updateSnapshot": "jest --updateSnapshot",
|
|
45
46
|
"migration:create": "ts-node ./bin/migration-create.ts",
|
|
46
47
|
"buf:gen": "buf generate proto"
|
|
47
|
-
}
|
|
48
|
-
"types": "dist/index.d.ts"
|
|
48
|
+
}
|
|
49
49
|
}
|
package/src/client.ts
CHANGED
package/src/context.ts
CHANGED
|
@@ -7,21 +7,25 @@ import { EventEmitter } from 'stream'
|
|
|
7
7
|
export type AppContextOptions = {
|
|
8
8
|
db: Database
|
|
9
9
|
cfg: ServerConfig
|
|
10
|
+
shutdown: AbortSignal
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
export class AppContext {
|
|
13
14
|
db: Database
|
|
14
15
|
cfg: ServerConfig
|
|
16
|
+
shutdown: AbortSignal
|
|
15
17
|
events: TypedEventEmitter<AppEvents>
|
|
16
18
|
|
|
17
19
|
constructor(opts: AppContextOptions) {
|
|
18
20
|
this.db = opts.db
|
|
19
21
|
this.cfg = opts.cfg
|
|
22
|
+
this.shutdown = opts.shutdown
|
|
20
23
|
this.events = new EventEmitter() as TypedEventEmitter<AppEvents>
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
static async fromConfig(
|
|
24
27
|
cfg: ServerConfig,
|
|
28
|
+
shutdown: AbortSignal,
|
|
25
29
|
overrides?: Partial<AppContextOptions>,
|
|
26
30
|
): Promise<AppContext> {
|
|
27
31
|
const db = new Database({
|
|
@@ -31,7 +35,7 @@ export class AppContext {
|
|
|
31
35
|
poolMaxUses: cfg.db.poolMaxUses,
|
|
32
36
|
poolIdleTimeoutMs: cfg.db.poolIdleTimeoutMs,
|
|
33
37
|
})
|
|
34
|
-
return new AppContext({ db, cfg, ...overrides })
|
|
38
|
+
return new AppContext({ db, cfg, shutdown, ...overrides })
|
|
35
39
|
}
|
|
36
40
|
}
|
|
37
41
|
|
package/src/db/index.ts
CHANGED
|
@@ -34,41 +34,43 @@ export class Database {
|
|
|
34
34
|
if (instances) {
|
|
35
35
|
this.db = instances.db
|
|
36
36
|
this.pool = instances.pool
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
} else {
|
|
38
|
+
// else create a pool & connect
|
|
39
|
+
const { schema, url } = opts
|
|
40
|
+
const pool =
|
|
41
|
+
opts.pool ??
|
|
42
|
+
new PgPool({
|
|
43
|
+
connectionString: url,
|
|
44
|
+
max: opts.poolSize,
|
|
45
|
+
maxUses: opts.poolMaxUses,
|
|
46
|
+
idleTimeoutMillis: opts.poolIdleTimeoutMs,
|
|
47
|
+
})
|
|
39
48
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const pool =
|
|
43
|
-
opts.pool ??
|
|
44
|
-
new PgPool({
|
|
45
|
-
connectionString: url,
|
|
46
|
-
max: opts.poolSize,
|
|
47
|
-
maxUses: opts.poolMaxUses,
|
|
48
|
-
idleTimeoutMillis: opts.poolIdleTimeoutMs,
|
|
49
|
-
})
|
|
49
|
+
// Select count(*) and other pg bigints as js integer
|
|
50
|
+
pgTypes.setTypeParser(pgTypes.builtins.INT8, (n) => parseInt(n, 10))
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
// Setup schema usage, primarily for test parallelism (each test suite runs in its own pg schema)
|
|
53
|
+
if (schema && !/^[a-z_]+$/i.test(schema)) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`Postgres schema must only contain [A-Za-z_]: ${schema}`,
|
|
56
|
+
)
|
|
57
|
+
}
|
|
53
58
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
pool.on('error', onPoolError)
|
|
60
|
+
pool.on('connect', (client) => {
|
|
61
|
+
client.on('error', onClientError)
|
|
62
|
+
if (schema) {
|
|
63
|
+
// Shared objects such as extensions will go in the public schema
|
|
64
|
+
client.query(`SET search_path TO "${schema}",public;`)
|
|
65
|
+
}
|
|
66
|
+
})
|
|
58
67
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
client.query(`SET search_path TO "${schema}",public;`)
|
|
65
|
-
}
|
|
66
|
-
})
|
|
68
|
+
this.pool = pool
|
|
69
|
+
this.db = new Kysely<DatabaseSchemaType>({
|
|
70
|
+
dialect: new PostgresDialect({ pool }),
|
|
71
|
+
})
|
|
72
|
+
}
|
|
67
73
|
|
|
68
|
-
this.pool = pool
|
|
69
|
-
this.db = new Kysely<DatabaseSchemaType>({
|
|
70
|
-
dialect: new PostgresDialect({ pool }),
|
|
71
|
-
})
|
|
72
74
|
this.migrator = new Migrator({
|
|
73
75
|
db: this.db,
|
|
74
76
|
migrationTableSchema: opts.schema,
|
|
@@ -165,7 +167,7 @@ const onClientError = (err: Error) => dbLogger.error({ err }, 'db client error')
|
|
|
165
167
|
// -------
|
|
166
168
|
|
|
167
169
|
class LeakyTxPlugin implements KyselyPlugin {
|
|
168
|
-
private txOver
|
|
170
|
+
private txOver = false
|
|
169
171
|
|
|
170
172
|
endTx() {
|
|
171
173
|
this.txOver = true
|
package/src/db/schema/mute_op.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -19,7 +19,7 @@ export class BsyncService {
|
|
|
19
19
|
public server: http.Server
|
|
20
20
|
private ac: AbortController
|
|
21
21
|
private terminator: HttpTerminator
|
|
22
|
-
private dbStatsInterval
|
|
22
|
+
private dbStatsInterval?: NodeJS.Timeout
|
|
23
23
|
|
|
24
24
|
constructor(opts: {
|
|
25
25
|
ctx: AppContext
|
|
@@ -36,20 +36,28 @@ export class BsyncService {
|
|
|
36
36
|
cfg: ServerConfig,
|
|
37
37
|
overrides?: Partial<AppContextOptions>,
|
|
38
38
|
): Promise<BsyncService> {
|
|
39
|
-
const ctx = await AppContext.fromConfig(cfg, overrides)
|
|
40
39
|
const ac = new AbortController()
|
|
40
|
+
const ctx = await AppContext.fromConfig(cfg, ac.signal, overrides)
|
|
41
41
|
const handler = connectNodeAdapter({
|
|
42
42
|
routes: routes(ctx),
|
|
43
43
|
shutdownSignal: ac.signal,
|
|
44
44
|
})
|
|
45
45
|
const server = http.createServer((req, res) => {
|
|
46
46
|
loggerMiddleware(req, res)
|
|
47
|
+
if (isHealth(req.url)) {
|
|
48
|
+
res.statusCode = 200
|
|
49
|
+
res.setHeader('content-type', 'application/json')
|
|
50
|
+
return res.end(JSON.stringify({ version: cfg.service.version }))
|
|
51
|
+
}
|
|
47
52
|
handler(req, res)
|
|
48
53
|
})
|
|
49
54
|
return new BsyncService({ ctx, server, ac })
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
async start(): Promise<http.Server> {
|
|
58
|
+
if (this.dbStatsInterval) {
|
|
59
|
+
throw new Error(`${this.constructor.name} already started`)
|
|
60
|
+
}
|
|
53
61
|
this.dbStatsInterval = setInterval(() => {
|
|
54
62
|
dbLogger.info(
|
|
55
63
|
{
|
|
@@ -72,6 +80,7 @@ export class BsyncService {
|
|
|
72
80
|
await this.terminator.terminate()
|
|
73
81
|
await this.ctx.db.close()
|
|
74
82
|
clearInterval(this.dbStatsInterval)
|
|
83
|
+
this.dbStatsInterval = undefined
|
|
75
84
|
}
|
|
76
85
|
|
|
77
86
|
async setupAppEvents() {
|
|
@@ -89,3 +98,9 @@ export class BsyncService {
|
|
|
89
98
|
}
|
|
90
99
|
|
|
91
100
|
export default BsyncService
|
|
101
|
+
|
|
102
|
+
const isHealth = (urlStr: string | undefined) => {
|
|
103
|
+
if (!urlStr) return false
|
|
104
|
+
const url = new URL(urlStr, 'http://host')
|
|
105
|
+
return url.pathname === '/_health'
|
|
106
|
+
}
|
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
ensureValidDid,
|
|
7
7
|
} from '@atproto/syntax'
|
|
8
8
|
import { Code, ConnectError, ServiceImpl } from '@connectrpc/connect'
|
|
9
|
-
import { Service } from '../
|
|
10
|
-
import { AddMuteOperationResponse, MuteOperation_Type } from '../
|
|
9
|
+
import { Service } from '../proto/bsync_connect'
|
|
10
|
+
import { AddMuteOperationResponse, MuteOperation_Type } from '../proto/bsync_pb'
|
|
11
11
|
import AppContext from '../context'
|
|
12
12
|
import { createMuteOpChannel } from '../db/schema/mute_op'
|
|
13
13
|
import { authWithApiKey } from './auth'
|
package/src/routes/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { sql } from 'kysely'
|
|
2
2
|
import { ConnectRouter } from '@connectrpc/connect'
|
|
3
|
-
import { Service } from '../
|
|
3
|
+
import { Service } from '../proto/bsync_connect'
|
|
4
4
|
import AppContext from '../context'
|
|
5
5
|
import addMuteOperation from './add-mute-operation'
|
|
6
6
|
import scanMuteOperations from './scan-mute-operations'
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { once } from 'node:events'
|
|
2
2
|
import { Code, ConnectError, ServiceImpl } from '@connectrpc/connect'
|
|
3
|
-
import { Service } from '../
|
|
4
|
-
import { ScanMuteOperationsResponse } from '../
|
|
3
|
+
import { Service } from '../proto/bsync_connect'
|
|
4
|
+
import { ScanMuteOperationsResponse } from '../proto/bsync_pb'
|
|
5
5
|
import AppContext from '../context'
|
|
6
6
|
import { createMuteOpChannel } from '../db/schema/mute_op'
|
|
7
7
|
import { authWithApiKey } from './auth'
|
|
@@ -13,7 +13,10 @@ export default (ctx: AppContext): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
13
13
|
const limit = req.limit || 1000
|
|
14
14
|
const cursor = validCursor(req.cursor)
|
|
15
15
|
const nextMuteOpPromise = once(events, createMuteOpChannel, {
|
|
16
|
-
signal:
|
|
16
|
+
signal: combineSignals(
|
|
17
|
+
ctx.shutdown,
|
|
18
|
+
AbortSignal.timeout(ctx.cfg.service.longPollTimeoutMs),
|
|
19
|
+
),
|
|
17
20
|
})
|
|
18
21
|
nextMuteOpPromise.catch(() => null) // ensure timeout is always handled
|
|
19
22
|
|
|
@@ -31,6 +34,7 @@ export default (ctx: AppContext): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
31
34
|
try {
|
|
32
35
|
await nextMuteOpPromise
|
|
33
36
|
} catch (err) {
|
|
37
|
+
ctx.shutdown.throwIfAborted()
|
|
34
38
|
return new ScanMuteOperationsResponse({
|
|
35
39
|
operations: [],
|
|
36
40
|
cursor: req.cursor,
|
|
@@ -67,3 +71,18 @@ const validCursor = (cursor: string): number | null => {
|
|
|
67
71
|
}
|
|
68
72
|
return int
|
|
69
73
|
}
|
|
74
|
+
|
|
75
|
+
const combineSignals = (a: AbortSignal, b: AbortSignal) => {
|
|
76
|
+
const controller = new AbortController()
|
|
77
|
+
for (const signal of [a, b]) {
|
|
78
|
+
if (signal.aborted) {
|
|
79
|
+
controller.abort()
|
|
80
|
+
return signal
|
|
81
|
+
}
|
|
82
|
+
signal.addEventListener('abort', () => controller.abort(signal.reason), {
|
|
83
|
+
// @ts-ignore https://github.com/DefinitelyTyped/DefinitelyTyped/pull/68625
|
|
84
|
+
signal: controller.signal,
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
return controller.signal
|
|
88
|
+
}
|
package/tests/mutes.test.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
createClient,
|
|
9
9
|
envToCfg,
|
|
10
10
|
} from '../src'
|
|
11
|
-
import { MuteOperation, MuteOperation_Type } from '../src/
|
|
11
|
+
import { MuteOperation, MuteOperation_Type } from '../src/proto/bsync_pb'
|
|
12
12
|
|
|
13
13
|
describe('mutes', () => {
|
|
14
14
|
let bsync: BsyncService
|
package/tsconfig.build.json
CHANGED
package/tsconfig.json
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"rootDir": "./src",
|
|
5
|
-
"outDir": "./dist",
|
|
6
|
-
"emitDeclarationOnly": true
|
|
7
|
-
},
|
|
8
|
-
"module": "nodenext",
|
|
9
|
-
"include": ["./src", "__tests__/**/**.ts"],
|
|
2
|
+
"include": [],
|
|
10
3
|
"references": [
|
|
11
|
-
{ "path": "
|
|
12
|
-
{ "path": "
|
|
4
|
+
{ "path": "./tsconfig.build.json" },
|
|
5
|
+
{ "path": "./tsconfig.tests.json" }
|
|
13
6
|
]
|
|
14
7
|
}
|
package/babel.config.js
DELETED
package/build.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
const { nodeExternalsPlugin } = require('esbuild-node-externals')
|
|
2
|
-
|
|
3
|
-
const buildShallow =
|
|
4
|
-
process.argv.includes('--shallow') || process.env.ATP_BUILD_SHALLOW === 'true'
|
|
5
|
-
|
|
6
|
-
require('esbuild').build({
|
|
7
|
-
logLevel: 'info',
|
|
8
|
-
entryPoints: ['src/index.ts'],
|
|
9
|
-
bundle: true,
|
|
10
|
-
sourcemap: true,
|
|
11
|
-
outdir: 'dist',
|
|
12
|
-
platform: 'node',
|
|
13
|
-
external: [
|
|
14
|
-
// Referenced in pg driver, but optional and we don't use it
|
|
15
|
-
'pg-native',
|
|
16
|
-
],
|
|
17
|
-
plugins: buildShallow ? [nodeExternalsPlugin()] : [],
|
|
18
|
-
})
|
|
File without changes
|