@atproto/ozone 0.1.53 → 0.1.54

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.
Files changed (133) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/api/index.d.ts.map +1 -1
  3. package/dist/api/index.js +6 -0
  4. package/dist/api/index.js.map +1 -1
  5. package/dist/api/moderation/queryStatuses.d.ts.map +1 -1
  6. package/dist/api/moderation/queryStatuses.js +6 -1
  7. package/dist/api/moderation/queryStatuses.js.map +1 -1
  8. package/dist/api/setting/listOptions.d.ts +4 -0
  9. package/dist/api/setting/listOptions.d.ts.map +1 -0
  10. package/dist/api/setting/listOptions.js +38 -0
  11. package/dist/api/setting/listOptions.js.map +1 -0
  12. package/dist/api/setting/removeOptions.d.ts +4 -0
  13. package/dist/api/setting/removeOptions.d.ts.map +1 -0
  14. package/dist/api/setting/removeOptions.js +55 -0
  15. package/dist/api/setting/removeOptions.js.map +1 -0
  16. package/dist/api/setting/upsertOption.d.ts +4 -0
  17. package/dist/api/setting/upsertOption.d.ts.map +1 -0
  18. package/dist/api/setting/upsertOption.js +109 -0
  19. package/dist/api/setting/upsertOption.js.map +1 -0
  20. package/dist/context.d.ts +3 -0
  21. package/dist/context.d.ts.map +1 -1
  22. package/dist/context.js +6 -0
  23. package/dist/context.js.map +1 -1
  24. package/dist/db/migrations/20241018T205730722Z-setting.d.ts +4 -0
  25. package/dist/db/migrations/20241018T205730722Z-setting.d.ts.map +1 -0
  26. package/dist/db/migrations/20241018T205730722Z-setting.js +26 -0
  27. package/dist/db/migrations/20241018T205730722Z-setting.js.map +1 -0
  28. package/dist/db/migrations/20241026T205730722Z-add-hosting-status-to-subject-status.d.ts +4 -0
  29. package/dist/db/migrations/20241026T205730722Z-add-hosting-status-to-subject-status.d.ts.map +1 -0
  30. package/dist/db/migrations/20241026T205730722Z-add-hosting-status-to-subject-status.js +57 -0
  31. package/dist/db/migrations/20241026T205730722Z-add-hosting-status-to-subject-status.js.map +1 -0
  32. package/dist/db/migrations/index.d.ts +2 -0
  33. package/dist/db/migrations/index.d.ts.map +1 -1
  34. package/dist/db/migrations/index.js +3 -1
  35. package/dist/db/migrations/index.js.map +1 -1
  36. package/dist/db/schema/index.d.ts +2 -1
  37. package/dist/db/schema/index.d.ts.map +1 -1
  38. package/dist/db/schema/moderation_event.d.ts +1 -1
  39. package/dist/db/schema/moderation_event.d.ts.map +1 -1
  40. package/dist/db/schema/moderation_subject_status.d.ts +6 -0
  41. package/dist/db/schema/moderation_subject_status.d.ts.map +1 -1
  42. package/dist/db/schema/setting.d.ts +21 -0
  43. package/dist/db/schema/setting.d.ts.map +1 -0
  44. package/dist/db/schema/setting.js +5 -0
  45. package/dist/db/schema/setting.js.map +1 -0
  46. package/dist/lexicon/index.d.ts +11 -0
  47. package/dist/lexicon/index.d.ts.map +1 -1
  48. package/dist/lexicon/index.js +32 -1
  49. package/dist/lexicon/index.js.map +1 -1
  50. package/dist/lexicon/lexicons.d.ts +359 -0
  51. package/dist/lexicon/lexicons.d.ts.map +1 -1
  52. package/dist/lexicon/lexicons.js +402 -3
  53. package/dist/lexicon/lexicons.js.map +1 -1
  54. package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts +58 -2
  55. package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts.map +1 -1
  56. package/dist/lexicon/types/tools/ozone/moderation/defs.js +50 -0
  57. package/dist/lexicon/types/tools/ozone/moderation/defs.js.map +1 -1
  58. package/dist/lexicon/types/tools/ozone/moderation/emitEvent.d.ts +1 -1
  59. package/dist/lexicon/types/tools/ozone/moderation/emitEvent.d.ts.map +1 -1
  60. package/dist/lexicon/types/tools/ozone/moderation/queryStatuses.d.ts +10 -0
  61. package/dist/lexicon/types/tools/ozone/moderation/queryStatuses.d.ts.map +1 -1
  62. package/dist/lexicon/types/tools/ozone/setting/defs.d.ts +20 -0
  63. package/dist/lexicon/types/tools/ozone/setting/defs.d.ts.map +1 -0
  64. package/dist/lexicon/types/tools/ozone/setting/defs.js +15 -0
  65. package/dist/lexicon/types/tools/ozone/setting/defs.js.map +1 -0
  66. package/dist/lexicon/types/tools/ozone/setting/listOptions.d.ts +43 -0
  67. package/dist/lexicon/types/tools/ozone/setting/listOptions.d.ts.map +1 -0
  68. package/dist/lexicon/types/tools/ozone/setting/listOptions.js +3 -0
  69. package/dist/lexicon/types/tools/ozone/setting/listOptions.js.map +1 -0
  70. package/dist/lexicon/types/tools/ozone/setting/removeOptions.d.ts +40 -0
  71. package/dist/lexicon/types/tools/ozone/setting/removeOptions.d.ts.map +1 -0
  72. package/dist/lexicon/types/tools/ozone/setting/removeOptions.js +3 -0
  73. package/dist/lexicon/types/tools/ozone/setting/removeOptions.js.map +1 -0
  74. package/dist/lexicon/types/tools/ozone/setting/upsertOption.d.ts +45 -0
  75. package/dist/lexicon/types/tools/ozone/setting/upsertOption.d.ts.map +1 -0
  76. package/dist/lexicon/types/tools/ozone/setting/upsertOption.js +3 -0
  77. package/dist/lexicon/types/tools/ozone/setting/upsertOption.js.map +1 -0
  78. package/dist/mod-service/index.d.ts +19 -2
  79. package/dist/mod-service/index.d.ts.map +1 -1
  80. package/dist/mod-service/index.js +37 -1
  81. package/dist/mod-service/index.js.map +1 -1
  82. package/dist/mod-service/status.d.ts +2 -22
  83. package/dist/mod-service/status.d.ts.map +1 -1
  84. package/dist/mod-service/status.js +91 -1
  85. package/dist/mod-service/status.js.map +1 -1
  86. package/dist/mod-service/types.d.ts +19 -1
  87. package/dist/mod-service/types.d.ts.map +1 -1
  88. package/dist/mod-service/views.d.ts.map +1 -1
  89. package/dist/mod-service/views.js +36 -1
  90. package/dist/mod-service/views.js.map +1 -1
  91. package/dist/setting/service.d.ts +33 -0
  92. package/dist/setting/service.d.ts.map +1 -0
  93. package/dist/setting/service.js +101 -0
  94. package/dist/setting/service.js.map +1 -0
  95. package/package.json +6 -6
  96. package/src/api/index.ts +6 -0
  97. package/src/api/moderation/queryStatuses.ts +10 -0
  98. package/src/api/setting/listOptions.ts +44 -0
  99. package/src/api/setting/removeOptions.ts +63 -0
  100. package/src/api/setting/upsertOption.ts +142 -0
  101. package/src/context.ts +8 -0
  102. package/src/db/migrations/20241018T205730722Z-setting.ts +27 -0
  103. package/src/db/migrations/20241026T205730722Z-add-hosting-status-to-subject-status.ts +57 -0
  104. package/src/db/migrations/index.ts +2 -0
  105. package/src/db/schema/index.ts +3 -1
  106. package/src/db/schema/moderation_event.ts +3 -0
  107. package/src/db/schema/moderation_subject_status.ts +6 -0
  108. package/src/db/schema/setting.ts +24 -0
  109. package/src/lexicon/index.ts +46 -0
  110. package/src/lexicon/lexicons.ts +412 -3
  111. package/src/lexicon/types/tools/ozone/moderation/defs.ts +130 -0
  112. package/src/lexicon/types/tools/ozone/moderation/emitEvent.ts +3 -0
  113. package/src/lexicon/types/tools/ozone/moderation/queryStatuses.ts +10 -0
  114. package/src/lexicon/types/tools/ozone/setting/defs.ts +37 -0
  115. package/src/lexicon/types/tools/ozone/setting/listOptions.ts +53 -0
  116. package/src/lexicon/types/tools/ozone/setting/removeOptions.ts +49 -0
  117. package/src/lexicon/types/tools/ozone/setting/upsertOption.ts +58 -0
  118. package/src/mod-service/index.ts +52 -0
  119. package/src/mod-service/status.ts +114 -2
  120. package/src/mod-service/types.ts +25 -0
  121. package/src/mod-service/views.ts +45 -2
  122. package/src/setting/service.ts +148 -0
  123. package/tests/__snapshots__/get-record.test.ts.snap +8 -0
  124. package/tests/__snapshots__/get-records.test.ts.snap +4 -0
  125. package/tests/__snapshots__/get-repo.test.ts.snap +4 -0
  126. package/tests/__snapshots__/get-repos.test.ts.snap +4 -0
  127. package/tests/__snapshots__/moderation-events.test.ts.snap +4 -0
  128. package/tests/__snapshots__/moderation-statuses.test.ts.snap +24 -0
  129. package/tests/__snapshots__/settings.test.ts.snap +52 -0
  130. package/tests/record-and-account-events.test.ts +185 -0
  131. package/tests/settings.test.ts +310 -0
  132. package/tsconfig.build.tsbuildinfo +1 -1
  133. package/tsconfig.tests.tsbuildinfo +1 -1
@@ -0,0 +1,33 @@
1
+ import Database from '../db';
2
+ import { Selectable } from 'kysely';
3
+ import { Option } from '../lexicon/types/tools/ozone/setting/defs';
4
+ import { Setting, SettingScope } from '../db/schema/setting';
5
+ import { Member } from '../db/schema/member';
6
+ export type SettingServiceCreator = (db: Database) => SettingService;
7
+ export declare class SettingService {
8
+ db: Database;
9
+ constructor(db: Database);
10
+ static creator(): (db: Database) => SettingService;
11
+ query({ limit, scope, did, cursor, prefix, keys, }: {
12
+ limit: number;
13
+ scope?: 'personal' | 'instance';
14
+ did?: string;
15
+ cursor?: string;
16
+ prefix?: string;
17
+ keys?: string[];
18
+ }): Promise<{
19
+ options: Selectable<Setting>[];
20
+ cursor?: string;
21
+ }>;
22
+ upsert(option: Omit<Setting, 'id' | 'createdAt' | 'updatedAt'> & {
23
+ createdAt: Date;
24
+ updatedAt: Date;
25
+ }): Promise<void>;
26
+ removeOptions(keys: string[], filters: {
27
+ did?: string;
28
+ scope: SettingScope;
29
+ managerRole: Member['role'][];
30
+ }): Promise<void>;
31
+ view(setting: Selectable<Setting>): Option;
32
+ }
33
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/setting/service.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,OAAO,CAAA;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAA;AAClE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAI5C,MAAM,MAAM,qBAAqB,GAAG,CAAC,EAAE,EAAE,QAAQ,KAAK,cAAc,CAAA;AAEpE,qBAAa,cAAc;IACN,EAAE,EAAE,QAAQ;gBAAZ,EAAE,EAAE,QAAQ;IAE/B,MAAM,CAAC,OAAO,SACA,QAAQ;IAGhB,KAAK,CAAC,EACV,KAAW,EACX,KAAK,EACL,GAAG,EACH,MAAM,EACN,MAAM,EACN,IAAI,GACL,EAAE;QACD,KAAK,EAAE,MAAM,CAAA;QACb,KAAK,CAAC,EAAE,UAAU,GAAG,UAAU,CAAA;QAC/B,GAAG,CAAC,EAAE,MAAM,CAAA;QACZ,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;KAChB,GAAG,OAAO,CAAC;QACV,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,CAAA;QAC9B,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,CAAC;IAiCI,MAAM,CACV,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,WAAW,GAAG,WAAW,CAAC,GAAG;QACxD,SAAS,EAAE,IAAI,CAAA;QACf,SAAS,EAAE,IAAI,CAAA;KAChB,GACA,OAAO,CAAC,IAAI,CAAC;IAgBV,aAAa,CACjB,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE;QACP,GAAG,CAAC,EAAE,MAAM,CAAA;QACZ,KAAK,EAAE,YAAY,CAAA;QACnB,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,CAAA;KAC9B,GACA,OAAO,CAAC,IAAI,CAAC;IAyBhB,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,MAAM;CA2B3C"}
@@ -0,0 +1,101 @@
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
+ exports.SettingService = void 0;
7
+ const node_assert_1 = __importDefault(require("node:assert"));
8
+ const xrpc_server_1 = require("@atproto/xrpc-server");
9
+ class SettingService {
10
+ constructor(db) {
11
+ Object.defineProperty(this, "db", {
12
+ enumerable: true,
13
+ configurable: true,
14
+ writable: true,
15
+ value: db
16
+ });
17
+ }
18
+ static creator() {
19
+ return (db) => new SettingService(db);
20
+ }
21
+ async query({ limit = 100, scope, did, cursor, prefix, keys, }) {
22
+ let builder = this.db.db.selectFrom('setting').selectAll();
23
+ if (prefix) {
24
+ builder = builder.where('key', 'like', `${prefix}%`);
25
+ }
26
+ else if (keys?.length) {
27
+ builder = builder.where('key', 'in', keys);
28
+ }
29
+ if (scope) {
30
+ builder = builder.where('scope', '=', scope);
31
+ }
32
+ if (did) {
33
+ builder = builder.where('did', '=', did);
34
+ }
35
+ if (cursor) {
36
+ const cursorId = parseInt(cursor, 10);
37
+ if (isNaN(cursorId)) {
38
+ throw new xrpc_server_1.InvalidRequestError('invalid cursor');
39
+ }
40
+ builder = builder.where('id', '<', cursorId);
41
+ }
42
+ const options = await builder.orderBy('id', 'desc').limit(limit).execute();
43
+ return {
44
+ options,
45
+ cursor: options[options.length - 1]?.id.toString(),
46
+ };
47
+ }
48
+ async upsert(option) {
49
+ await this.db.db
50
+ .insertInto('setting')
51
+ .values(option)
52
+ .onConflict((oc) => {
53
+ return oc.columns(['key', 'scope', 'did']).doUpdateSet({
54
+ value: option.value,
55
+ updatedAt: option.updatedAt,
56
+ description: option.description,
57
+ managerRole: option.managerRole,
58
+ lastUpdatedBy: option.lastUpdatedBy,
59
+ });
60
+ })
61
+ .execute();
62
+ }
63
+ async removeOptions(keys, filters) {
64
+ if (!keys.length)
65
+ return;
66
+ if (filters.scope === 'personal') {
67
+ (0, node_assert_1.default)(filters.did, 'did is required for personal scope');
68
+ }
69
+ let qb = this.db.db
70
+ .deleteFrom('setting')
71
+ .where('key', 'in', keys)
72
+ .where('scope', '=', filters.scope);
73
+ if (filters.managerRole.length) {
74
+ qb = qb.where('managerRole', 'in', filters.managerRole);
75
+ }
76
+ else {
77
+ qb = qb.where('managerRole', 'is', null);
78
+ }
79
+ if (filters.did) {
80
+ qb = qb.where('did', '=', filters.did);
81
+ }
82
+ await qb.execute();
83
+ }
84
+ view(setting) {
85
+ const { key, value, did, description, createdAt, createdBy, updatedAt, lastUpdatedBy, managerRole, scope, } = setting;
86
+ return {
87
+ key,
88
+ value,
89
+ did,
90
+ scope,
91
+ createdBy,
92
+ lastUpdatedBy,
93
+ managerRole: managerRole || undefined,
94
+ description: description || undefined,
95
+ createdAt: createdAt.toISOString(),
96
+ updatedAt: updatedAt.toISOString(),
97
+ };
98
+ }
99
+ }
100
+ exports.SettingService = SettingService;
101
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/setting/service.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/ozone",
3
- "version": "0.1.53",
3
+ "version": "0.1.54",
4
4
  "license": "MIT",
5
5
  "description": "Backend service for moderating the Bluesky network.",
6
6
  "keywords": [
@@ -32,14 +32,14 @@
32
32
  "structured-headers": "^1.0.1",
33
33
  "typed-emitter": "^2.1.0",
34
34
  "uint8arrays": "3.0.0",
35
- "@atproto/api": "^0.13.14",
35
+ "@atproto/api": "^0.13.15",
36
36
  "@atproto/common": "^0.4.4",
37
- "@atproto/crypto": "^0.4.1",
38
- "@atproto/identity": "^0.4.2",
37
+ "@atproto/crypto": "^0.4.2",
38
+ "@atproto/identity": "^0.4.3",
39
39
  "@atproto/lexicon": "^0.4.2",
40
40
  "@atproto/syntax": "^0.3.0",
41
41
  "@atproto/xrpc": "^0.6.3",
42
- "@atproto/xrpc-server": "^0.7.1"
42
+ "@atproto/xrpc-server": "^0.7.2"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@did-plc/server": "^0.0.1",
@@ -53,7 +53,7 @@
53
53
  "ts-node": "^10.8.2",
54
54
  "typescript": "^5.6.3",
55
55
  "@atproto/lex-cli": "^0.5.1",
56
- "@atproto/pds": "^0.4.69"
56
+ "@atproto/pds": "^0.4.70"
57
57
  },
58
58
  "scripts": {
59
59
  "codegen": "lex gen-server --yes ./src/lexicon ../../lexicons/com/atproto/*/* ../../lexicons/app/bsky/*/* ../../lexicons/chat/bsky/*/* ../../lexicons/tools/ozone/*/*",
package/src/api/index.ts CHANGED
@@ -30,6 +30,9 @@ import upsertSet from './set/upsertSet'
30
30
  import setDeleteValues from './set/deleteValues'
31
31
  import deleteSet from './set/deleteSet'
32
32
  import getRepos from './moderation/getRepos'
33
+ import listOptions from './setting/listOptions'
34
+ import removeOptions from './setting/removeOptions'
35
+ import upsertOption from './setting/upsertOption'
33
36
 
34
37
  export * as health from './health'
35
38
 
@@ -66,5 +69,8 @@ export default function (server: Server, ctx: AppContext) {
66
69
  upsertSet(server, ctx)
67
70
  setDeleteValues(server, ctx)
68
71
  deleteSet(server, ctx)
72
+ upsertOption(server, ctx)
73
+ listOptions(server, ctx)
74
+ removeOptions(server, ctx)
69
75
  return server
70
76
  }
@@ -18,6 +18,11 @@ export default function (server: Server, ctx: AppContext) {
18
18
  reportedBefore,
19
19
  ignoreSubjects,
20
20
  lastReviewedBy,
21
+ hostingDeletedBefore,
22
+ hostingDeletedAfter,
23
+ hostingUpdatedBefore,
24
+ hostingUpdatedAfter,
25
+ hostingStatuses,
21
26
  sortDirection = 'desc',
22
27
  sortField = 'lastReportedAt',
23
28
  includeMuted = false,
@@ -42,6 +47,11 @@ export default function (server: Server, ctx: AppContext) {
42
47
  reportedAfter,
43
48
  reportedBefore,
44
49
  includeMuted,
50
+ hostingDeletedBefore,
51
+ hostingDeletedAfter,
52
+ hostingUpdatedBefore,
53
+ hostingUpdatedAfter,
54
+ hostingStatuses,
45
55
  onlyMuted,
46
56
  ignoreSubjects,
47
57
  sortDirection,
@@ -0,0 +1,44 @@
1
+ import { AuthRequiredError } from '@atproto/xrpc-server'
2
+ import { Server } from '../../lexicon'
3
+ import AppContext from '../../context'
4
+
5
+ export default function (server: Server, ctx: AppContext) {
6
+ server.tools.ozone.setting.listOptions({
7
+ auth: ctx.authVerifier.modOrAdminToken,
8
+ handler: async ({ params, auth }) => {
9
+ const access = auth.credentials
10
+ const db = ctx.db
11
+ const { prefix, scope, keys, limit, cursor } = params
12
+ let did = ctx.cfg.service.did
13
+
14
+ if (scope === 'personal') {
15
+ if (access.type !== 'moderator') {
16
+ throw new AuthRequiredError(
17
+ 'Must use moderator auth to get personal set details',
18
+ )
19
+ }
20
+
21
+ did = access.iss
22
+ }
23
+
24
+ const settingService = ctx.settingService(db)
25
+
26
+ const result = await settingService.query({
27
+ scope: scope === 'personal' ? 'personal' : 'instance',
28
+ did,
29
+ keys,
30
+ prefix,
31
+ limit,
32
+ cursor,
33
+ })
34
+
35
+ return {
36
+ encoding: 'application/json',
37
+ body: {
38
+ options: result.options.map((option) => settingService.view(option)),
39
+ cursor: result.cursor,
40
+ },
41
+ }
42
+ },
43
+ })
44
+ }
@@ -0,0 +1,63 @@
1
+ import { AuthRequiredError } from '@atproto/xrpc-server'
2
+ import { Server } from '../../lexicon'
3
+ import AppContext from '../../context'
4
+ import { Member } from '../../db/schema/member'
5
+
6
+ export default function (server: Server, ctx: AppContext) {
7
+ server.tools.ozone.setting.removeOptions({
8
+ auth: ctx.authVerifier.modOrAdminToken,
9
+ handler: async ({ input, auth }) => {
10
+ const access = auth.credentials
11
+ const db = ctx.db
12
+ const { keys, scope } = input.body
13
+ let did = ctx.cfg.service.did
14
+ let managerRole: Member['role'][] = []
15
+
16
+ if (scope === 'personal') {
17
+ if (access.type !== 'moderator') {
18
+ throw new AuthRequiredError(
19
+ 'Must use moderator auth to delete personal setting',
20
+ )
21
+ }
22
+
23
+ did = access.iss
24
+ }
25
+
26
+ // When attempting to delete an instance setting using admin_token will allow removing any setting
27
+ // otherwise, admins can remove settings that are manageable by all roles
28
+ // moderators can remove settings that are manageable by moderator and triage roles
29
+ // triage can remove settings that are manageable by triage role
30
+ if (scope === 'instance') {
31
+ managerRole = [
32
+ 'tools.ozone.team.defs#roleModerator',
33
+ 'tools.ozone.team.defs#roleTriage',
34
+ 'tools.ozone.team.defs#roleAdmin',
35
+ ]
36
+
37
+ if (access.type !== 'admin_token' && !access.isAdmin) {
38
+ if (access.isModerator) {
39
+ managerRole = [
40
+ 'tools.ozone.team.defs#roleModerator',
41
+ 'tools.ozone.team.defs#roleTriage',
42
+ ]
43
+ } else if (access.isTriage) {
44
+ managerRole = ['tools.ozone.team.defs#roleTriage']
45
+ }
46
+ }
47
+ }
48
+
49
+ const settingService = ctx.settingService(db)
50
+
51
+ await settingService.removeOptions(keys, {
52
+ scope: scope === 'personal' ? 'personal' : 'instance',
53
+ managerRole,
54
+ did,
55
+ })
56
+
57
+ return {
58
+ encoding: 'application/json',
59
+ body: {},
60
+ }
61
+ },
62
+ })
63
+ }
@@ -0,0 +1,142 @@
1
+ import { AuthRequiredError } from '@atproto/xrpc-server'
2
+ import { Server } from '../../lexicon'
3
+ import AppContext from '../../context'
4
+ import { AdminTokenOutput, ModeratorOutput } from '../../auth-verifier'
5
+ import { SettingService } from '../../setting/service'
6
+ import { Member } from '../../db/schema/member'
7
+ import { ToolsOzoneTeamDefs } from '@atproto/api'
8
+ import assert from 'node:assert'
9
+
10
+ export default function (server: Server, ctx: AppContext) {
11
+ server.tools.ozone.setting.upsertOption({
12
+ auth: ctx.authVerifier.modOrAdminToken,
13
+ handler: async ({ input, auth }) => {
14
+ const access = auth.credentials
15
+ const db = ctx.db
16
+ const { key, value, description, managerRole, scope } = input.body
17
+ const serviceDid = ctx.cfg.service.did
18
+ let ownerDid = serviceDid
19
+
20
+ if (scope === 'personal' && access.type !== 'moderator') {
21
+ throw new AuthRequiredError(
22
+ 'Must use moderator auth to create or update a personal setting',
23
+ )
24
+ }
25
+
26
+ // if the caller is using moderator auth and storing personal setting
27
+ // use the caller's DID as the owner
28
+ if (scope === 'personal' && access.type === 'moderator') {
29
+ ownerDid = access.iss
30
+ }
31
+
32
+ const now = new Date()
33
+ const baseOption = {
34
+ key,
35
+ value,
36
+ did: ownerDid,
37
+ createdBy: ownerDid,
38
+ lastUpdatedBy: ownerDid,
39
+ description: description || '',
40
+ createdAt: now,
41
+ updatedAt: now,
42
+ }
43
+
44
+ const settingService = ctx.settingService(db)
45
+ if (scope === 'personal') {
46
+ await settingService.upsert({
47
+ ...baseOption,
48
+ scope: 'personal',
49
+ managerRole: null,
50
+ })
51
+ } else {
52
+ const manageableRoles = getRolesForInstanceOption(access)
53
+ const existingSetting = await getExistingSetting(
54
+ settingService,
55
+ ownerDid,
56
+ key,
57
+ 'instance',
58
+ )
59
+
60
+ if (
61
+ existingSetting?.managerRole &&
62
+ !manageableRoles.includes(existingSetting.managerRole)
63
+ ) {
64
+ throw new AuthRequiredError(`Not permitted to update setting ${key}`)
65
+ }
66
+ await settingService.upsert({
67
+ ...baseOption,
68
+ scope: 'instance',
69
+ managerRole: getManagerRole(managerRole),
70
+ })
71
+ }
72
+
73
+ const newOption = await getExistingSetting(
74
+ settingService,
75
+ ownerDid,
76
+ key,
77
+ scope,
78
+ )
79
+ assert(newOption, 'Failed to get the updated setting')
80
+
81
+ return {
82
+ encoding: 'application/json',
83
+ body: {
84
+ option: settingService.view(newOption),
85
+ },
86
+ }
87
+ },
88
+ })
89
+ }
90
+
91
+ const getExistingSetting = async (
92
+ settingService: SettingService,
93
+ did: string,
94
+ key: string,
95
+ scope: string,
96
+ ) => {
97
+ const result = await settingService.query({
98
+ scope: scope === 'personal' ? 'personal' : 'instance',
99
+ keys: [key],
100
+ limit: 1,
101
+ did,
102
+ })
103
+
104
+ return result.options[0]
105
+ }
106
+
107
+ const getRolesForInstanceOption = (
108
+ access: AdminTokenOutput['credentials'] | ModeratorOutput['credentials'],
109
+ ) => {
110
+ const fullPermission = [
111
+ ToolsOzoneTeamDefs.ROLEADMIN,
112
+ ToolsOzoneTeamDefs.ROLEMODERATOR,
113
+ ToolsOzoneTeamDefs.ROLETRIAGE,
114
+ ]
115
+ if (access.type === 'admin_token') {
116
+ return fullPermission
117
+ }
118
+
119
+ if (access.isAdmin) {
120
+ return fullPermission
121
+ }
122
+
123
+ if (access.isModerator) {
124
+ return [ToolsOzoneTeamDefs.ROLEMODERATOR, ToolsOzoneTeamDefs.ROLETRIAGE]
125
+ }
126
+
127
+ return [ToolsOzoneTeamDefs.ROLETRIAGE]
128
+ }
129
+
130
+ const getManagerRole = (role?: string) => {
131
+ let managerRole: Member['role'] | null = null
132
+
133
+ if (role === ToolsOzoneTeamDefs.ROLEADMIN) {
134
+ managerRole = ToolsOzoneTeamDefs.ROLEADMIN
135
+ } else if (role === ToolsOzoneTeamDefs.ROLEMODERATOR) {
136
+ managerRole = ToolsOzoneTeamDefs.ROLEMODERATOR
137
+ } else if (role === ToolsOzoneTeamDefs.ROLETRIAGE) {
138
+ managerRole = ToolsOzoneTeamDefs.ROLETRIAGE
139
+ }
140
+
141
+ return managerRole
142
+ }
package/src/context.ts CHANGED
@@ -27,6 +27,7 @@ import {
27
27
  parseLabelerHeader,
28
28
  } from './util'
29
29
  import { SetService, SetServiceCreator } from './set/service'
30
+ import { SettingService, SettingServiceCreator } from './setting/service'
30
31
 
31
32
  export type AppContextOptions = {
32
33
  db: Database
@@ -34,6 +35,7 @@ export type AppContextOptions = {
34
35
  modService: ModerationServiceCreator
35
36
  communicationTemplateService: CommunicationTemplateServiceCreator
36
37
  setService: SetServiceCreator
38
+ settingService: SettingServiceCreator
37
39
  teamService: TeamServiceCreator
38
40
  appviewAgent: AtpAgent
39
41
  pdsAgent: AtpAgent | undefined
@@ -120,6 +122,7 @@ export class AppContext {
120
122
  const communicationTemplateService = CommunicationTemplateService.creator()
121
123
  const teamService = TeamService.creator()
122
124
  const setService = SetService.creator()
125
+ const settingService = SettingService.creator()
123
126
 
124
127
  const sequencer = new Sequencer(modService(db))
125
128
 
@@ -137,6 +140,7 @@ export class AppContext {
137
140
  communicationTemplateService,
138
141
  teamService,
139
142
  setService,
143
+ settingService,
140
144
  appviewAgent,
141
145
  pdsAgent,
142
146
  chatAgent,
@@ -190,6 +194,10 @@ export class AppContext {
190
194
  return this.opts.setService
191
195
  }
192
196
 
197
+ get settingService(): SettingServiceCreator {
198
+ return this.opts.settingService
199
+ }
200
+
193
201
  get appviewAgent(): AtpAgent {
194
202
  return this.opts.appviewAgent
195
203
  }
@@ -0,0 +1,27 @@
1
+ import { Kysely, sql } from 'kysely'
2
+
3
+ export async function up(db: Kysely<unknown>): Promise<void> {
4
+ await db.schema
5
+ .createTable('setting')
6
+ .addColumn('id', 'serial', (col) => col.primaryKey())
7
+ .addColumn('key', 'text', (col) => col.notNull())
8
+ .addColumn('did', 'text', (col) => col.notNull())
9
+ .addColumn('value', 'jsonb', (col) => col.notNull())
10
+ .addColumn('description', 'text')
11
+ .addColumn('createdAt', 'timestamptz', (col) =>
12
+ col.defaultTo(sql`now()`).notNull(),
13
+ )
14
+ .addColumn('updatedAt', 'timestamptz', (col) =>
15
+ col.defaultTo(sql`now()`).notNull(),
16
+ )
17
+ .addColumn('managerRole', 'text')
18
+ .addColumn('scope', 'text', (col) => col.notNull())
19
+ .addColumn('createdBy', 'text', (col) => col.notNull())
20
+ .addColumn('lastUpdatedBy', 'text', (col) => col.notNull())
21
+ .addUniqueConstraint('setting_did_scope_key_idx', ['did', 'scope', 'key'])
22
+ .execute()
23
+ }
24
+
25
+ export async function down(db: Kysely<unknown>): Promise<void> {
26
+ await db.schema.dropTable('setting').execute()
27
+ }
@@ -0,0 +1,57 @@
1
+ import { Kysely } from 'kysely'
2
+
3
+ export async function up(db: Kysely<unknown>): Promise<void> {
4
+ await db.schema
5
+ .alterTable('moderation_subject_status')
6
+ .addColumn('hostingStatus', 'varchar', (col) =>
7
+ col.notNull().defaultTo('unknown'),
8
+ )
9
+ .execute()
10
+ await db.schema
11
+ .alterTable('moderation_subject_status')
12
+ .addColumn('hostingDeletedAt', 'varchar')
13
+ .execute()
14
+ await db.schema
15
+ .alterTable('moderation_subject_status')
16
+ .addColumn('hostingUpdatedAt', 'varchar')
17
+ .execute()
18
+ await db.schema
19
+ .alterTable('moderation_subject_status')
20
+ .addColumn('hostingCreatedAt', 'varchar')
21
+ .execute()
22
+ await db.schema
23
+ .alterTable('moderation_subject_status')
24
+ .addColumn('hostingDeactivatedAt', 'varchar')
25
+ .execute()
26
+ await db.schema
27
+ .alterTable('moderation_subject_status')
28
+ .addColumn('hostingReactivatedAt', 'varchar')
29
+ .execute()
30
+ }
31
+
32
+ export async function down(db: Kysely<unknown>): Promise<void> {
33
+ await db.schema
34
+ .alterTable('moderation_subject_status')
35
+ .dropColumn('hostingStatus')
36
+ .execute()
37
+ await db.schema
38
+ .alterTable('moderation_subject_status')
39
+ .dropColumn('hostingDeletedAt')
40
+ .execute()
41
+ await db.schema
42
+ .alterTable('moderation_subject_status')
43
+ .dropColumn('hostingUpdatedAt')
44
+ .execute()
45
+ await db.schema
46
+ .alterTable('moderation_subject_status')
47
+ .dropColumn('hostingCreatedAt')
48
+ .execute()
49
+ await db.schema
50
+ .alterTable('moderation_subject_status')
51
+ .dropColumn('hostingDeactivatedAt')
52
+ .execute()
53
+ await db.schema
54
+ .alterTable('moderation_subject_status')
55
+ .dropColumn('hostingReactivatedAt')
56
+ .execute()
57
+ }
@@ -15,3 +15,5 @@ export * as _20240903T205730722Z from './20240903T205730722Z-add-template-lang'
15
15
  export * as _20240904T205730722Z from './20240904T205730722Z-add-subject-did-index'
16
16
  export * as _20241001T205730722Z from './20241001T205730722Z-subject-status-review-state-index'
17
17
  export * as _20241008T205730722Z from './20241008T205730722Z-sets'
18
+ export * as _20241018T205730722Z from './20241018T205730722Z-setting'
19
+ export * as _20241026T205730722Z from './20241026T205730722Z-add-hosting-status-to-subject-status'
@@ -9,6 +9,7 @@ import * as signingKey from './signing_key'
9
9
  import * as communicationTemplate from './communication_template'
10
10
  import * as set from './ozone_set'
11
11
  import * as member from './member'
12
+ import * as setting from './setting'
12
13
 
13
14
  export type DatabaseSchemaType = modEvent.PartialDB &
14
15
  modSubjectStatus.PartialDB &
@@ -19,7 +20,8 @@ export type DatabaseSchemaType = modEvent.PartialDB &
19
20
  blobPushEvent.PartialDB &
20
21
  communicationTemplate.PartialDB &
21
22
  set.PartialDB &
22
- member.PartialDB
23
+ member.PartialDB &
24
+ setting.PartialDB
23
25
 
24
26
  export type DatabaseSchema = Kysely<DatabaseSchemaType>
25
27
 
@@ -19,6 +19,9 @@ export interface ModerationEvent {
19
19
  | 'tools.ozone.moderation.defs#modEventEmail'
20
20
  | 'tools.ozone.moderation.defs#modEventResolveAppeal'
21
21
  | 'tools.ozone.moderation.defs#modEventTag'
22
+ | 'tools.ozone.moderation.defs#accountEvent'
23
+ | 'tools.ozone.moderation.defs#identityEvent'
24
+ | 'tools.ozone.moderation.defs#recordEvent'
22
25
  subjectType:
23
26
  | 'com.atproto.admin.defs#repoRef'
24
27
  | 'com.atproto.repo.strongRef'
@@ -25,6 +25,12 @@ export interface ModerationSubjectStatus {
25
25
  lastReviewedAt: string | null
26
26
  lastReportedAt: string | null
27
27
  lastAppealedAt: string | null
28
+ hostingUpdatedAt: string | null
29
+ hostingDeletedAt: string | null
30
+ hostingCreatedAt: string | null
31
+ hostingDeactivatedAt: string | null
32
+ hostingReactivatedAt: string | null
33
+ hostingStatus: string | null
28
34
  muteUntil: string | null
29
35
  muteReportingUntil: string | null
30
36
  suspendUntil: string | null