@atproto/ozone 0.0.11 → 0.0.13
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 +17 -0
- package/dist/api/label/queryLabels.d.ts +3 -0
- package/dist/api/label/subscribeLabels.d.ts +3 -0
- package/dist/config/config.d.ts +3 -0
- package/dist/config/env.d.ts +3 -0
- package/dist/context.d.ts +3 -0
- package/dist/db/index.js +3 -1
- package/dist/db/index.js.map +2 -2
- package/dist/db/schema/label.d.ts +4 -0
- package/dist/index.js +875 -454
- package/dist/index.js.map +3 -3
- package/dist/lexicon/index.d.ts +2 -0
- package/dist/lexicon/lexicons.d.ts +27 -0
- package/dist/lexicon/types/app/bsky/actor/defs.d.ts +1 -1
- package/dist/lexicon/types/com/atproto/admin/updateAccountPassword.d.ts +26 -0
- package/dist/logger.d.ts +1 -0
- package/dist/mod-service/util.d.ts +3 -0
- package/dist/sequencer/index.d.ts +2 -0
- package/dist/sequencer/outbox.d.ts +16 -0
- package/dist/sequencer/sequencer.d.ts +33 -0
- package/package.json +10 -10
- package/src/api/admin/emitModerationEvent.ts +16 -10
- package/src/api/index.ts +4 -0
- package/src/api/label/queryLabels.ts +58 -0
- package/src/api/label/subscribeLabels.ts +25 -0
- package/src/api/temp/fetchLabels.ts +2 -4
- package/src/config/config.ts +6 -0
- package/src/config/env.ts +6 -0
- package/src/context.ts +12 -0
- package/src/db/migrations/20231219T205730722Z-init.ts +7 -1
- package/src/db/schema/label.ts +7 -0
- package/src/index.ts +2 -0
- package/src/lexicon/index.ts +12 -0
- package/src/lexicon/lexicons.ts +32 -1
- package/src/lexicon/types/app/bsky/actor/defs.ts +2 -0
- package/src/lexicon/types/com/atproto/admin/updateAccountPassword.ts +39 -0
- package/src/logger.ts +2 -0
- package/src/mod-service/index.ts +73 -72
- package/src/mod-service/status.ts +3 -0
- package/src/mod-service/util.ts +17 -0
- package/src/mod-service/views.ts +2 -5
- package/src/sequencer/index.ts +2 -0
- package/src/sequencer/outbox.ts +122 -0
- package/src/sequencer/sequencer.ts +143 -0
- package/tests/__snapshots__/moderation-events.test.ts.snap +53 -75
- package/tests/__snapshots__/moderation.test.ts.snap +4 -4
- package/tests/moderation-appeals.test.ts +19 -7
- package/tests/moderation-events.test.ts +7 -7
- package/tests/moderation-statuses.test.ts +2 -2
- package/tests/moderation.test.ts +14 -13
- package/tests/query-labels.test.ts +163 -0
- package/tests/repo-search.test.ts +0 -1
- package/tests/sequencer.test.ts +222 -0
- package/tests/server.test.ts +2 -0
package/dist/lexicon/index.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ import * as ComAtprotoAdminSearchRepos from './types/com/atproto/admin/searchRep
|
|
|
20
20
|
import * as ComAtprotoAdminSendEmail from './types/com/atproto/admin/sendEmail';
|
|
21
21
|
import * as ComAtprotoAdminUpdateAccountEmail from './types/com/atproto/admin/updateAccountEmail';
|
|
22
22
|
import * as ComAtprotoAdminUpdateAccountHandle from './types/com/atproto/admin/updateAccountHandle';
|
|
23
|
+
import * as ComAtprotoAdminUpdateAccountPassword from './types/com/atproto/admin/updateAccountPassword';
|
|
23
24
|
import * as ComAtprotoAdminUpdateCommunicationTemplate from './types/com/atproto/admin/updateCommunicationTemplate';
|
|
24
25
|
import * as ComAtprotoAdminUpdateSubjectStatus from './types/com/atproto/admin/updateSubjectStatus';
|
|
25
26
|
import * as ComAtprotoIdentityGetRecommendedDidCredentials from './types/com/atproto/identity/getRecommendedDidCredentials';
|
|
@@ -192,6 +193,7 @@ export declare class ComAtprotoAdminNS {
|
|
|
192
193
|
sendEmail<AV extends AuthVerifier>(cfg: ConfigOf<AV, ComAtprotoAdminSendEmail.Handler<ExtractAuth<AV>>, ComAtprotoAdminSendEmail.HandlerReqCtx<ExtractAuth<AV>>>): void;
|
|
193
194
|
updateAccountEmail<AV extends AuthVerifier>(cfg: ConfigOf<AV, ComAtprotoAdminUpdateAccountEmail.Handler<ExtractAuth<AV>>, ComAtprotoAdminUpdateAccountEmail.HandlerReqCtx<ExtractAuth<AV>>>): void;
|
|
194
195
|
updateAccountHandle<AV extends AuthVerifier>(cfg: ConfigOf<AV, ComAtprotoAdminUpdateAccountHandle.Handler<ExtractAuth<AV>>, ComAtprotoAdminUpdateAccountHandle.HandlerReqCtx<ExtractAuth<AV>>>): void;
|
|
196
|
+
updateAccountPassword<AV extends AuthVerifier>(cfg: ConfigOf<AV, ComAtprotoAdminUpdateAccountPassword.Handler<ExtractAuth<AV>>, ComAtprotoAdminUpdateAccountPassword.HandlerReqCtx<ExtractAuth<AV>>>): void;
|
|
195
197
|
updateCommunicationTemplate<AV extends AuthVerifier>(cfg: ConfigOf<AV, ComAtprotoAdminUpdateCommunicationTemplate.Handler<ExtractAuth<AV>>, ComAtprotoAdminUpdateCommunicationTemplate.HandlerReqCtx<ExtractAuth<AV>>>): void;
|
|
196
198
|
updateSubjectStatus<AV extends AuthVerifier>(cfg: ConfigOf<AV, ComAtprotoAdminUpdateSubjectStatus.Handler<ExtractAuth<AV>>, ComAtprotoAdminUpdateSubjectStatus.HandlerReqCtx<ExtractAuth<AV>>>): void;
|
|
197
199
|
}
|
|
@@ -1695,6 +1695,32 @@ export declare const schemaDict: {
|
|
|
1695
1695
|
};
|
|
1696
1696
|
};
|
|
1697
1697
|
};
|
|
1698
|
+
ComAtprotoAdminUpdateAccountPassword: {
|
|
1699
|
+
lexicon: number;
|
|
1700
|
+
id: string;
|
|
1701
|
+
defs: {
|
|
1702
|
+
main: {
|
|
1703
|
+
type: string;
|
|
1704
|
+
description: string;
|
|
1705
|
+
input: {
|
|
1706
|
+
encoding: string;
|
|
1707
|
+
schema: {
|
|
1708
|
+
type: string;
|
|
1709
|
+
required: string[];
|
|
1710
|
+
properties: {
|
|
1711
|
+
did: {
|
|
1712
|
+
type: string;
|
|
1713
|
+
format: string;
|
|
1714
|
+
};
|
|
1715
|
+
password: {
|
|
1716
|
+
type: string;
|
|
1717
|
+
};
|
|
1718
|
+
};
|
|
1719
|
+
};
|
|
1720
|
+
};
|
|
1721
|
+
};
|
|
1722
|
+
};
|
|
1723
|
+
};
|
|
1698
1724
|
ComAtprotoAdminUpdateCommunicationTemplate: {
|
|
1699
1725
|
lexicon: number;
|
|
1700
1726
|
id: string;
|
|
@@ -8205,6 +8231,7 @@ export declare const ids: {
|
|
|
8205
8231
|
ComAtprotoAdminSendEmail: string;
|
|
8206
8232
|
ComAtprotoAdminUpdateAccountEmail: string;
|
|
8207
8233
|
ComAtprotoAdminUpdateAccountHandle: string;
|
|
8234
|
+
ComAtprotoAdminUpdateAccountPassword: string;
|
|
8208
8235
|
ComAtprotoAdminUpdateCommunicationTemplate: string;
|
|
8209
8236
|
ComAtprotoAdminUpdateSubjectStatus: string;
|
|
8210
8237
|
ComAtprotoIdentityGetRecommendedDidCredentials: string;
|
|
@@ -54,7 +54,7 @@ export interface ViewerState {
|
|
|
54
54
|
}
|
|
55
55
|
export declare function isViewerState(v: unknown): v is ViewerState;
|
|
56
56
|
export declare function validateViewerState(v: unknown): ValidationResult;
|
|
57
|
-
export type Preferences = (AdultContentPref | ContentLabelPref | SavedFeedsPref | PersonalDetailsPref | FeedViewPref | ThreadViewPref | InterestsPref | {
|
|
57
|
+
export type Preferences = (AdultContentPref | ContentLabelPref | SavedFeedsPref | PersonalDetailsPref | FeedViewPref | ThreadViewPref | InterestsPref | MutedWordsPref | HiddenPostsPref | {
|
|
58
58
|
$type: string;
|
|
59
59
|
[k: string]: unknown;
|
|
60
60
|
})[];
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { HandlerAuth } from '@atproto/xrpc-server';
|
|
3
|
+
export interface QueryParams {
|
|
4
|
+
}
|
|
5
|
+
export interface InputSchema {
|
|
6
|
+
did: string;
|
|
7
|
+
password: string;
|
|
8
|
+
[k: string]: unknown;
|
|
9
|
+
}
|
|
10
|
+
export interface HandlerInput {
|
|
11
|
+
encoding: 'application/json';
|
|
12
|
+
body: InputSchema;
|
|
13
|
+
}
|
|
14
|
+
export interface HandlerError {
|
|
15
|
+
status: number;
|
|
16
|
+
message?: string;
|
|
17
|
+
}
|
|
18
|
+
export type HandlerOutput = HandlerError | void;
|
|
19
|
+
export type HandlerReqCtx<HA extends HandlerAuth = never> = {
|
|
20
|
+
auth: HA;
|
|
21
|
+
params: QueryParams;
|
|
22
|
+
input: HandlerInput;
|
|
23
|
+
req: express.Request;
|
|
24
|
+
res: express.Response;
|
|
25
|
+
};
|
|
26
|
+
export type Handler<HA extends HandlerAuth = never> = (ctx: HandlerReqCtx<HA>) => Promise<HandlerOutput> | HandlerOutput;
|
package/dist/logger.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { subsystemLogger } from '@atproto/common';
|
|
2
2
|
export declare const dbLogger: ReturnType<typeof subsystemLogger>;
|
|
3
|
+
export declare const seqLogger: ReturnType<typeof subsystemLogger>;
|
|
3
4
|
export declare const httpLogger: ReturnType<typeof subsystemLogger>;
|
|
4
5
|
export declare const langLogger: ReturnType<typeof subsystemLogger>;
|
|
5
6
|
export declare const loggerMiddleware: import("pino-http").HttpLogger;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { AsyncBuffer } from '@atproto/common';
|
|
2
|
+
import { Sequencer, LabelsEvt } from './sequencer';
|
|
3
|
+
export type OutboxOpts = {
|
|
4
|
+
maxBufferSize: number;
|
|
5
|
+
};
|
|
6
|
+
export declare class Outbox {
|
|
7
|
+
sequencer: Sequencer;
|
|
8
|
+
private caughtUp;
|
|
9
|
+
lastSeen: number;
|
|
10
|
+
cutoverBuffer: LabelsEvt[];
|
|
11
|
+
outBuffer: AsyncBuffer<LabelsEvt>;
|
|
12
|
+
constructor(sequencer: Sequencer, opts?: Partial<OutboxOpts>);
|
|
13
|
+
events(backfillCursor?: number, signal?: AbortSignal): AsyncGenerator<LabelsEvt>;
|
|
14
|
+
getBackfill(backfillCursor: number): AsyncGenerator<LabelsEvt, void, unknown>;
|
|
15
|
+
}
|
|
16
|
+
export default Outbox;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import TypedEmitter from 'typed-emitter';
|
|
2
|
+
import Database from '../db';
|
|
3
|
+
import { Labels as LabelsEvt } from '../lexicon/types/com/atproto/label/subscribeLabels';
|
|
4
|
+
import { Label as LabelTable } from '../db/schema/label';
|
|
5
|
+
import { Selectable } from 'kysely';
|
|
6
|
+
import { PoolClient } from 'pg';
|
|
7
|
+
export type { Labels as LabelsEvt } from '../lexicon/types/com/atproto/label/subscribeLabels';
|
|
8
|
+
type LabelRow = Selectable<LabelTable>;
|
|
9
|
+
declare const Sequencer_base: new () => SequencerEmitter;
|
|
10
|
+
export declare class Sequencer extends Sequencer_base {
|
|
11
|
+
db: Database;
|
|
12
|
+
lastSeen: number;
|
|
13
|
+
destroyed: boolean;
|
|
14
|
+
pollPromise: Promise<void> | undefined;
|
|
15
|
+
queued: boolean;
|
|
16
|
+
conn: PoolClient | undefined;
|
|
17
|
+
constructor(db: Database, lastSeen?: number);
|
|
18
|
+
start(): Promise<void>;
|
|
19
|
+
destroy(): Promise<void>;
|
|
20
|
+
curr(): Promise<number | null>;
|
|
21
|
+
next(cursor: number): Promise<LabelRow | null>;
|
|
22
|
+
requestLabelRange(opts: {
|
|
23
|
+
earliestId?: number;
|
|
24
|
+
limit?: number;
|
|
25
|
+
}): Promise<LabelsEvt[]>;
|
|
26
|
+
private poll;
|
|
27
|
+
}
|
|
28
|
+
type SequencerEvents = {
|
|
29
|
+
events: (evts: LabelsEvt[]) => void;
|
|
30
|
+
close: () => void;
|
|
31
|
+
};
|
|
32
|
+
export type SequencerEmitter = TypedEmitter<SequencerEvents>;
|
|
33
|
+
export default Sequencer;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/ozone",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.13",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Backend service for moderating the Bluesky network.",
|
|
6
6
|
"keywords": [
|
|
@@ -30,13 +30,13 @@
|
|
|
30
30
|
"pino-http": "^8.2.1",
|
|
31
31
|
"typed-emitter": "^2.1.0",
|
|
32
32
|
"uint8arrays": "3.0.0",
|
|
33
|
-
"@atproto/api": "^0.10.
|
|
33
|
+
"@atproto/api": "^0.10.2",
|
|
34
34
|
"@atproto/common": "^0.3.3",
|
|
35
35
|
"@atproto/crypto": "^0.3.0",
|
|
36
36
|
"@atproto/identity": "^0.3.2",
|
|
37
|
-
"@atproto/lexicon": "^0.3.
|
|
38
|
-
"@atproto/syntax": "^0.
|
|
39
|
-
"@atproto/xrpc-server": "^0.4.
|
|
37
|
+
"@atproto/lexicon": "^0.3.2",
|
|
38
|
+
"@atproto/syntax": "^0.2.0",
|
|
39
|
+
"@atproto/xrpc-server": "^0.4.3"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@did-plc/server": "^0.0.1",
|
|
@@ -46,11 +46,11 @@
|
|
|
46
46
|
"@types/pg": "^8.6.6",
|
|
47
47
|
"@types/qs": "^6.9.7",
|
|
48
48
|
"axios": "^0.27.2",
|
|
49
|
-
"@atproto/api": "^0.10.
|
|
50
|
-
"@atproto/dev-env": "^0.2.
|
|
51
|
-
"@atproto/lex-cli": "^0.3.
|
|
52
|
-
"@atproto/pds": "^0.4.
|
|
53
|
-
"@atproto/xrpc": "^0.4.
|
|
49
|
+
"@atproto/api": "^0.10.2",
|
|
50
|
+
"@atproto/dev-env": "^0.2.34",
|
|
51
|
+
"@atproto/lex-cli": "^0.3.1",
|
|
52
|
+
"@atproto/pds": "^0.4.2",
|
|
53
|
+
"@atproto/xrpc": "^0.4.2"
|
|
54
54
|
},
|
|
55
55
|
"scripts": {
|
|
56
56
|
"codegen": "lex gen-server ./src/lexicon ../../lexicons/com/atproto/*/* ../../lexicons/app/bsky/*/*",
|
|
@@ -27,17 +27,23 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
27
27
|
|
|
28
28
|
// apply access rules
|
|
29
29
|
|
|
30
|
-
// if less than moderator access then can not takedown an account
|
|
31
|
-
if (!access.moderator && isTakedownEvent && subject.isRepo()) {
|
|
32
|
-
throw new AuthRequiredError(
|
|
33
|
-
'Must be a full moderator to perform an account takedown',
|
|
34
|
-
)
|
|
35
|
-
}
|
|
36
30
|
// if less than moderator access then can only take ack and escalation actions
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
31
|
+
if (isTakedownEvent || isReverseTakedownEvent) {
|
|
32
|
+
if (!access.moderator) {
|
|
33
|
+
throw new AuthRequiredError(
|
|
34
|
+
'Must be a full moderator to take this type of action',
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Non admins should not be able to take down feed generators
|
|
39
|
+
if (
|
|
40
|
+
!access.admin &&
|
|
41
|
+
subject.recordPath?.includes('app.bsky.feed.generator/')
|
|
42
|
+
) {
|
|
43
|
+
throw new AuthRequiredError(
|
|
44
|
+
'Must be a full admin to take this type of action on feed generators',
|
|
45
|
+
)
|
|
46
|
+
}
|
|
41
47
|
}
|
|
42
48
|
// if less than moderator access then can not apply labels
|
|
43
49
|
if (!access.moderator && isLabelEvent) {
|
package/src/api/index.ts
CHANGED
|
@@ -8,6 +8,8 @@ import getRepo from './admin/getRepo'
|
|
|
8
8
|
import queryModerationStatuses from './admin/queryModerationStatuses'
|
|
9
9
|
import queryModerationEvents from './admin/queryModerationEvents'
|
|
10
10
|
import getModerationEvent from './admin/getModerationEvent'
|
|
11
|
+
import queryLabels from './label/queryLabels'
|
|
12
|
+
import subscribeLabels from './label/subscribeLabels'
|
|
11
13
|
import fetchLabels from './temp/fetchLabels'
|
|
12
14
|
import createCommunicationTemplate from './admin/createCommunicationTemplate'
|
|
13
15
|
import updateCommunicationTemplate from './admin/updateCommunicationTemplate'
|
|
@@ -27,6 +29,8 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
27
29
|
getModerationEvent(server, ctx)
|
|
28
30
|
queryModerationEvents(server, ctx)
|
|
29
31
|
queryModerationStatuses(server, ctx)
|
|
32
|
+
queryLabels(server, ctx)
|
|
33
|
+
subscribeLabels(server, ctx)
|
|
30
34
|
fetchLabels(server, ctx)
|
|
31
35
|
listCommunicationTemplates(server, ctx)
|
|
32
36
|
createCommunicationTemplate(server, ctx)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Server } from '../../lexicon'
|
|
2
|
+
import AppContext from '../../context'
|
|
3
|
+
import { InvalidRequestError } from '@atproto/xrpc-server'
|
|
4
|
+
import { sql } from 'kysely'
|
|
5
|
+
import { formatLabel } from '../../mod-service/util'
|
|
6
|
+
|
|
7
|
+
export default function (server: Server, ctx: AppContext) {
|
|
8
|
+
server.com.atproto.label.queryLabels(async ({ params }) => {
|
|
9
|
+
const { uriPatterns, sources, limit, cursor } = params
|
|
10
|
+
let builder = ctx.db.db.selectFrom('label').selectAll().limit(limit)
|
|
11
|
+
// if includes '*', then we don't need a where clause
|
|
12
|
+
if (!uriPatterns.includes('*')) {
|
|
13
|
+
builder = builder.where((qb) => {
|
|
14
|
+
// starter where clause that is always false so that we can chain `orWhere`s
|
|
15
|
+
qb = qb.where(sql`1 = 0`)
|
|
16
|
+
for (const pattern of uriPatterns) {
|
|
17
|
+
// if no '*', then we're looking for an exact match
|
|
18
|
+
if (!pattern.includes('*')) {
|
|
19
|
+
qb = qb.orWhere('uri', '=', pattern)
|
|
20
|
+
} else {
|
|
21
|
+
if (pattern.indexOf('*') < pattern.length - 1) {
|
|
22
|
+
throw new InvalidRequestError(`invalid pattern: ${pattern}`)
|
|
23
|
+
}
|
|
24
|
+
const searchPattern = pattern
|
|
25
|
+
.slice(0, -1)
|
|
26
|
+
.replaceAll('%', '') // sanitize search pattern
|
|
27
|
+
.replaceAll('_', '\\_') // escape any underscores
|
|
28
|
+
qb = qb.orWhere('uri', 'like', `${searchPattern}%`)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return qb
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
if (sources && sources.length > 0) {
|
|
35
|
+
builder = builder.where('src', 'in', sources)
|
|
36
|
+
}
|
|
37
|
+
if (cursor) {
|
|
38
|
+
const cursorId = parseInt(cursor, 10)
|
|
39
|
+
if (isNaN(cursorId)) {
|
|
40
|
+
throw new InvalidRequestError('invalid cursor')
|
|
41
|
+
}
|
|
42
|
+
builder = builder.where('id', '>', cursorId)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const res = await builder.execute()
|
|
46
|
+
|
|
47
|
+
const labels = res.map((l) => formatLabel(l))
|
|
48
|
+
const resCursor = res.at(-1)?.id.toString(10)
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
encoding: 'application/json',
|
|
52
|
+
body: {
|
|
53
|
+
cursor: resCursor,
|
|
54
|
+
labels,
|
|
55
|
+
},
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Server } from '../../lexicon'
|
|
2
|
+
import AppContext from '../../context'
|
|
3
|
+
import Outbox from '../../sequencer/outbox'
|
|
4
|
+
import { InvalidRequestError } from '@atproto/xrpc-server'
|
|
5
|
+
|
|
6
|
+
export default function (server: Server, ctx: AppContext) {
|
|
7
|
+
server.com.atproto.label.subscribeLabels(async function* ({
|
|
8
|
+
params,
|
|
9
|
+
signal,
|
|
10
|
+
}) {
|
|
11
|
+
const { cursor } = params
|
|
12
|
+
const outbox = new Outbox(ctx.sequencer)
|
|
13
|
+
|
|
14
|
+
if (cursor !== undefined) {
|
|
15
|
+
const curr = await ctx.sequencer.curr()
|
|
16
|
+
if (cursor > (curr ?? 0)) {
|
|
17
|
+
throw new InvalidRequestError('Cursor in the future.', 'FutureCursor')
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
for await (const evt of outbox.events(cursor, signal)) {
|
|
22
|
+
yield { $type: 'com.atproto.label.subscribeLabels#labels', ...evt }
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Server } from '../../lexicon'
|
|
2
2
|
import AppContext from '../../context'
|
|
3
|
+
import { formatLabel } from '../../mod-service/util'
|
|
3
4
|
import {
|
|
4
5
|
UNSPECCED_TAKEDOWN_BLOBS_LABEL,
|
|
5
6
|
UNSPECCED_TAKEDOWN_LABEL,
|
|
@@ -28,10 +29,7 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
28
29
|
.limit(limit)
|
|
29
30
|
.execute()
|
|
30
31
|
|
|
31
|
-
const labels = labelRes.map((l) => (
|
|
32
|
-
...l,
|
|
33
|
-
cid: l.cid === '' ? undefined : l.cid,
|
|
34
|
-
}))
|
|
32
|
+
const labels = labelRes.map((l) => formatLabel(l))
|
|
35
33
|
|
|
36
34
|
return {
|
|
37
35
|
encoding: 'application/json',
|
package/src/config/config.ts
CHANGED
|
@@ -19,6 +19,9 @@ export const envToCfg = (env: OzoneEnvironment): OzoneConfig => {
|
|
|
19
19
|
const dbCfg: OzoneConfig['db'] = {
|
|
20
20
|
postgresUrl: env.dbPostgresUrl,
|
|
21
21
|
postgresSchema: env.dbPostgresSchema,
|
|
22
|
+
poolSize: env.dbPoolSize,
|
|
23
|
+
poolMaxUses: env.dbPoolMaxUses,
|
|
24
|
+
poolIdleTimeoutMs: env.dbPoolIdleTimeoutMs,
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
assert(env.appviewUrl)
|
|
@@ -67,6 +70,9 @@ export type ServiceConfig = {
|
|
|
67
70
|
export type DatabaseConfig = {
|
|
68
71
|
postgresUrl: string
|
|
69
72
|
postgresSchema?: string
|
|
73
|
+
poolSize?: number
|
|
74
|
+
poolMaxUses?: number
|
|
75
|
+
poolIdleTimeoutMs?: number
|
|
70
76
|
}
|
|
71
77
|
|
|
72
78
|
export type AppviewConfig = {
|
package/src/config/env.ts
CHANGED
|
@@ -13,6 +13,9 @@ export const readEnv = (): OzoneEnvironment => {
|
|
|
13
13
|
pdsDid: envStr('OZONE_PDS_DID'),
|
|
14
14
|
dbPostgresUrl: envStr('OZONE_DB_POSTGRES_URL'),
|
|
15
15
|
dbPostgresSchema: envStr('OZONE_DB_POSTGRES_SCHEMA'),
|
|
16
|
+
dbPoolSize: envInt('OZONE_DB_POOL_SIZE'),
|
|
17
|
+
dbPoolMaxUses: envInt('OZONE_DB_POOL_MAX_USES'),
|
|
18
|
+
dbPoolIdleTimeoutMs: envInt('OZONE_DB_POOL_IDLE_TIMEOUT_MS'),
|
|
16
19
|
didPlcUrl: envStr('OZONE_DID_PLC_URL'),
|
|
17
20
|
adminPassword: envStr('OZONE_ADMIN_PASSWORD'),
|
|
18
21
|
moderatorPassword: envStr('OZONE_MODERATOR_PASSWORD'),
|
|
@@ -33,6 +36,9 @@ export type OzoneEnvironment = {
|
|
|
33
36
|
pdsDid?: string
|
|
34
37
|
dbPostgresUrl?: string
|
|
35
38
|
dbPostgresSchema?: string
|
|
39
|
+
dbPoolSize?: number
|
|
40
|
+
dbPoolMaxUses?: number
|
|
41
|
+
dbPoolIdleTimeoutMs?: number
|
|
36
42
|
didPlcUrl?: string
|
|
37
43
|
adminPassword?: string
|
|
38
44
|
moderatorPassword?: string
|
package/src/context.ts
CHANGED
|
@@ -10,6 +10,7 @@ import * as auth from './auth'
|
|
|
10
10
|
import { BackgroundQueue } from './background'
|
|
11
11
|
import assert from 'assert'
|
|
12
12
|
import { EventPusher } from './daemon'
|
|
13
|
+
import Sequencer from './sequencer/sequencer'
|
|
13
14
|
import {
|
|
14
15
|
CommunicationTemplateService,
|
|
15
16
|
CommunicationTemplateServiceCreator,
|
|
@@ -25,6 +26,7 @@ export type AppContextOptions = {
|
|
|
25
26
|
signingKey: Keypair
|
|
26
27
|
idResolver: IdResolver
|
|
27
28
|
backgroundQueue: BackgroundQueue
|
|
29
|
+
sequencer: Sequencer
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
export class AppContext {
|
|
@@ -38,6 +40,9 @@ export class AppContext {
|
|
|
38
40
|
const db = new Database({
|
|
39
41
|
url: cfg.db.postgresUrl,
|
|
40
42
|
schema: cfg.db.postgresSchema,
|
|
43
|
+
poolSize: cfg.db.poolSize,
|
|
44
|
+
poolMaxUses: cfg.db.poolMaxUses,
|
|
45
|
+
poolIdleTimeoutMs: cfg.db.poolIdleTimeoutMs,
|
|
41
46
|
})
|
|
42
47
|
const signingKey = await Secp256k1Keypair.import(secrets.signingKeyHex)
|
|
43
48
|
const appviewAgent = new AtpAgent({ service: cfg.appview.url })
|
|
@@ -74,6 +79,8 @@ export class AppContext {
|
|
|
74
79
|
plcUrl: cfg.identity.plcUrl,
|
|
75
80
|
})
|
|
76
81
|
|
|
82
|
+
const sequencer = new Sequencer(db)
|
|
83
|
+
|
|
77
84
|
return new AppContext(
|
|
78
85
|
{
|
|
79
86
|
db,
|
|
@@ -85,6 +92,7 @@ export class AppContext {
|
|
|
85
92
|
signingKey,
|
|
86
93
|
idResolver,
|
|
87
94
|
backgroundQueue,
|
|
95
|
+
sequencer,
|
|
88
96
|
...(overrides ?? {}),
|
|
89
97
|
},
|
|
90
98
|
secrets,
|
|
@@ -139,6 +147,10 @@ export class AppContext {
|
|
|
139
147
|
return this.opts.backgroundQueue
|
|
140
148
|
}
|
|
141
149
|
|
|
150
|
+
get sequencer(): Sequencer {
|
|
151
|
+
return this.opts.sequencer
|
|
152
|
+
}
|
|
153
|
+
|
|
142
154
|
get authVerifier() {
|
|
143
155
|
return auth.authVerifier(this.idResolver, { aud: this.cfg.service.did })
|
|
144
156
|
}
|
|
@@ -68,13 +68,19 @@ export async function up(db: Kysely<unknown>): Promise<void> {
|
|
|
68
68
|
// Label
|
|
69
69
|
await db.schema
|
|
70
70
|
.createTable('label')
|
|
71
|
+
.addColumn('id', 'bigserial', (col) => col.primaryKey())
|
|
71
72
|
.addColumn('src', 'varchar', (col) => col.notNull())
|
|
72
73
|
.addColumn('uri', 'varchar', (col) => col.notNull())
|
|
73
74
|
.addColumn('cid', 'varchar', (col) => col.notNull())
|
|
74
75
|
.addColumn('val', 'varchar', (col) => col.notNull())
|
|
75
76
|
.addColumn('neg', 'boolean', (col) => col.notNull())
|
|
76
77
|
.addColumn('cts', 'varchar', (col) => col.notNull())
|
|
77
|
-
.
|
|
78
|
+
.execute()
|
|
79
|
+
await db.schema
|
|
80
|
+
.createIndex('unique_label_idx')
|
|
81
|
+
.unique()
|
|
82
|
+
.on('label')
|
|
83
|
+
.columns(['src', 'uri', 'cid', 'val'])
|
|
78
84
|
.execute()
|
|
79
85
|
await db.schema
|
|
80
86
|
.createIndex('label_uri_index')
|
package/src/db/schema/label.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { Generated, Selectable } from 'kysely'
|
|
2
|
+
|
|
1
3
|
export const tableName = 'label'
|
|
2
4
|
|
|
3
5
|
export interface Label {
|
|
6
|
+
id: Generated<number>
|
|
4
7
|
src: string
|
|
5
8
|
uri: string
|
|
6
9
|
cid: string
|
|
@@ -9,4 +12,8 @@ export interface Label {
|
|
|
9
12
|
cts: string
|
|
10
13
|
}
|
|
11
14
|
|
|
15
|
+
export type LabelRow = Selectable<Label>
|
|
16
|
+
|
|
12
17
|
export type PartialDB = { [tableName]: Label }
|
|
18
|
+
|
|
19
|
+
export const LabelChannel = 'label_channel' // used with notify/listen
|
package/src/index.ts
CHANGED
|
@@ -81,6 +81,7 @@ export class OzoneService {
|
|
|
81
81
|
'background queue stats',
|
|
82
82
|
)
|
|
83
83
|
}, 10000)
|
|
84
|
+
await this.ctx.sequencer.start()
|
|
84
85
|
const server = this.app.listen(this.ctx.cfg.service.port)
|
|
85
86
|
this.server = server
|
|
86
87
|
server.keepAliveTimeout = 90000
|
|
@@ -94,6 +95,7 @@ export class OzoneService {
|
|
|
94
95
|
async destroy(): Promise<void> {
|
|
95
96
|
await this.terminator?.terminate()
|
|
96
97
|
await this.ctx.backgroundQueue.destroy()
|
|
98
|
+
await this.ctx.sequencer.destroy()
|
|
97
99
|
await this.ctx.db.close()
|
|
98
100
|
clearInterval(this.dbStatsInterval)
|
|
99
101
|
}
|
package/src/lexicon/index.ts
CHANGED
|
@@ -30,6 +30,7 @@ import * as ComAtprotoAdminSearchRepos from './types/com/atproto/admin/searchRep
|
|
|
30
30
|
import * as ComAtprotoAdminSendEmail from './types/com/atproto/admin/sendEmail'
|
|
31
31
|
import * as ComAtprotoAdminUpdateAccountEmail from './types/com/atproto/admin/updateAccountEmail'
|
|
32
32
|
import * as ComAtprotoAdminUpdateAccountHandle from './types/com/atproto/admin/updateAccountHandle'
|
|
33
|
+
import * as ComAtprotoAdminUpdateAccountPassword from './types/com/atproto/admin/updateAccountPassword'
|
|
33
34
|
import * as ComAtprotoAdminUpdateCommunicationTemplate from './types/com/atproto/admin/updateCommunicationTemplate'
|
|
34
35
|
import * as ComAtprotoAdminUpdateSubjectStatus from './types/com/atproto/admin/updateSubjectStatus'
|
|
35
36
|
import * as ComAtprotoIdentityGetRecommendedDidCredentials from './types/com/atproto/identity/getRecommendedDidCredentials'
|
|
@@ -444,6 +445,17 @@ export class ComAtprotoAdminNS {
|
|
|
444
445
|
return this._server.xrpc.method(nsid, cfg)
|
|
445
446
|
}
|
|
446
447
|
|
|
448
|
+
updateAccountPassword<AV extends AuthVerifier>(
|
|
449
|
+
cfg: ConfigOf<
|
|
450
|
+
AV,
|
|
451
|
+
ComAtprotoAdminUpdateAccountPassword.Handler<ExtractAuth<AV>>,
|
|
452
|
+
ComAtprotoAdminUpdateAccountPassword.HandlerReqCtx<ExtractAuth<AV>>
|
|
453
|
+
>,
|
|
454
|
+
) {
|
|
455
|
+
const nsid = 'com.atproto.admin.updateAccountPassword' // @ts-ignore
|
|
456
|
+
return this._server.xrpc.method(nsid, cfg)
|
|
457
|
+
}
|
|
458
|
+
|
|
447
459
|
updateCommunicationTemplate<AV extends AuthVerifier>(
|
|
448
460
|
cfg: ConfigOf<
|
|
449
461
|
AV,
|
package/src/lexicon/lexicons.ts
CHANGED
|
@@ -1863,6 +1863,33 @@ export const schemaDict = {
|
|
|
1863
1863
|
},
|
|
1864
1864
|
},
|
|
1865
1865
|
},
|
|
1866
|
+
ComAtprotoAdminUpdateAccountPassword: {
|
|
1867
|
+
lexicon: 1,
|
|
1868
|
+
id: 'com.atproto.admin.updateAccountPassword',
|
|
1869
|
+
defs: {
|
|
1870
|
+
main: {
|
|
1871
|
+
type: 'procedure',
|
|
1872
|
+
description:
|
|
1873
|
+
'Update the password for a user account as an administrator.',
|
|
1874
|
+
input: {
|
|
1875
|
+
encoding: 'application/json',
|
|
1876
|
+
schema: {
|
|
1877
|
+
type: 'object',
|
|
1878
|
+
required: ['did', 'password'],
|
|
1879
|
+
properties: {
|
|
1880
|
+
did: {
|
|
1881
|
+
type: 'string',
|
|
1882
|
+
format: 'did',
|
|
1883
|
+
},
|
|
1884
|
+
password: {
|
|
1885
|
+
type: 'string',
|
|
1886
|
+
},
|
|
1887
|
+
},
|
|
1888
|
+
},
|
|
1889
|
+
},
|
|
1890
|
+
},
|
|
1891
|
+
},
|
|
1892
|
+
},
|
|
1866
1893
|
ComAtprotoAdminUpdateCommunicationTemplate: {
|
|
1867
1894
|
lexicon: 1,
|
|
1868
1895
|
id: 'com.atproto.admin.updateCommunicationTemplate',
|
|
@@ -4840,7 +4867,7 @@ export const schemaDict = {
|
|
|
4840
4867
|
main: {
|
|
4841
4868
|
type: 'query',
|
|
4842
4869
|
description:
|
|
4843
|
-
'Fetch all labels from a labeler created after a certain date.
|
|
4870
|
+
'DEPRECATED: use queryLabels or subscribeLabels instead -- Fetch all labels from a labeler created after a certain date.',
|
|
4844
4871
|
parameters: {
|
|
4845
4872
|
type: 'params',
|
|
4846
4873
|
properties: {
|
|
@@ -5075,6 +5102,8 @@ export const schemaDict = {
|
|
|
5075
5102
|
'lex:app.bsky.actor.defs#feedViewPref',
|
|
5076
5103
|
'lex:app.bsky.actor.defs#threadViewPref',
|
|
5077
5104
|
'lex:app.bsky.actor.defs#interestsPref',
|
|
5105
|
+
'lex:app.bsky.actor.defs#mutedWordsPref',
|
|
5106
|
+
'lex:app.bsky.actor.defs#hiddenPostsPref',
|
|
5078
5107
|
],
|
|
5079
5108
|
},
|
|
5080
5109
|
},
|
|
@@ -8860,6 +8889,8 @@ export const ids = {
|
|
|
8860
8889
|
ComAtprotoAdminSendEmail: 'com.atproto.admin.sendEmail',
|
|
8861
8890
|
ComAtprotoAdminUpdateAccountEmail: 'com.atproto.admin.updateAccountEmail',
|
|
8862
8891
|
ComAtprotoAdminUpdateAccountHandle: 'com.atproto.admin.updateAccountHandle',
|
|
8892
|
+
ComAtprotoAdminUpdateAccountPassword:
|
|
8893
|
+
'com.atproto.admin.updateAccountPassword',
|
|
8863
8894
|
ComAtprotoAdminUpdateCommunicationTemplate:
|
|
8864
8895
|
'com.atproto.admin.updateCommunicationTemplate',
|
|
8865
8896
|
ComAtprotoAdminUpdateSubjectStatus: 'com.atproto.admin.updateSubjectStatus',
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATED CODE - DO NOT MODIFY
|
|
3
|
+
*/
|
|
4
|
+
import express from 'express'
|
|
5
|
+
import { ValidationResult, BlobRef } from '@atproto/lexicon'
|
|
6
|
+
import { lexicons } from '../../../../lexicons'
|
|
7
|
+
import { isObj, hasProp } from '../../../../util'
|
|
8
|
+
import { CID } from 'multiformats/cid'
|
|
9
|
+
import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
|
|
10
|
+
|
|
11
|
+
export interface QueryParams {}
|
|
12
|
+
|
|
13
|
+
export interface InputSchema {
|
|
14
|
+
did: string
|
|
15
|
+
password: string
|
|
16
|
+
[k: string]: unknown
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface HandlerInput {
|
|
20
|
+
encoding: 'application/json'
|
|
21
|
+
body: InputSchema
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface HandlerError {
|
|
25
|
+
status: number
|
|
26
|
+
message?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type HandlerOutput = HandlerError | void
|
|
30
|
+
export type HandlerReqCtx<HA extends HandlerAuth = never> = {
|
|
31
|
+
auth: HA
|
|
32
|
+
params: QueryParams
|
|
33
|
+
input: HandlerInput
|
|
34
|
+
req: express.Request
|
|
35
|
+
res: express.Response
|
|
36
|
+
}
|
|
37
|
+
export type Handler<HA extends HandlerAuth = never> = (
|
|
38
|
+
ctx: HandlerReqCtx<HA>,
|
|
39
|
+
) => Promise<HandlerOutput> | HandlerOutput
|
package/src/logger.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { subsystemLogger } from '@atproto/common'
|
|
|
3
3
|
|
|
4
4
|
export const dbLogger: ReturnType<typeof subsystemLogger> =
|
|
5
5
|
subsystemLogger('ozone:db')
|
|
6
|
+
export const seqLogger: ReturnType<typeof subsystemLogger> =
|
|
7
|
+
subsystemLogger('ozone:sequencer')
|
|
6
8
|
export const httpLogger: ReturnType<typeof subsystemLogger> =
|
|
7
9
|
subsystemLogger('ozone')
|
|
8
10
|
export const langLogger: ReturnType<typeof subsystemLogger> =
|