@vielzeug/archive 26.11.11 → 26.11.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/dist/_server/d1.d.ts +23 -0
- package/dist/_server/d1.d.ts.map +1 -0
- package/dist/_server/execution.d.ts +5 -0
- package/dist/_server/execution.d.ts.map +1 -0
- package/dist/_server/http.cjs +2 -0
- package/dist/_server/http.cjs.map +1 -0
- package/dist/{worker → _server}/http.d.ts +6 -11
- package/dist/_server/http.d.ts.map +1 -0
- package/dist/{worker → _server}/http.js +18 -44
- package/dist/_server/http.js.map +1 -0
- package/dist/{worker/_service.cjs → _server/service.cjs} +1 -1
- package/dist/{worker/_service.js.map → _server/service.cjs.map} +1 -1
- package/dist/{worker/_service.d.ts → _server/service.d.ts} +1 -1
- package/dist/_server/service.d.ts.map +1 -0
- package/dist/{worker/_service.js → _server/service.js} +2 -2
- package/dist/_server/service.js.map +1 -0
- package/dist/_server/uuid.cjs +2 -0
- package/dist/_server/uuid.cjs.map +1 -0
- package/dist/_server/uuid.d.ts +3 -0
- package/dist/_server/uuid.d.ts.map +1 -0
- package/dist/_server/uuid.js +5 -0
- package/dist/_server/uuid.js.map +1 -0
- package/dist/auth/_confirmation.cjs +3 -3
- package/dist/auth/_confirmation.cjs.map +1 -1
- package/dist/auth/_confirmation.js +4 -4
- package/dist/auth/_confirmation.js.map +1 -1
- package/dist/auth/config.cjs.map +1 -1
- package/dist/auth/config.d.ts +1 -1
- package/dist/auth/config.js.map +1 -1
- package/dist/auth/errors.cjs +1 -1
- package/dist/auth/errors.cjs.map +1 -1
- package/dist/auth/errors.d.ts +1 -1
- package/dist/auth/errors.d.ts.map +1 -1
- package/dist/auth/errors.js +1 -1
- package/dist/auth/errors.js.map +1 -1
- package/dist/auth/store.cjs.map +1 -1
- package/dist/auth/store.d.ts +1 -1
- package/dist/auth/store.js.map +1 -1
- package/dist/auth/worker.cjs +1 -1
- package/dist/auth/worker.cjs.map +1 -1
- package/dist/auth/worker.d.ts +1 -1
- package/dist/auth/worker.d.ts.map +1 -1
- package/dist/auth/worker.js +153 -152
- package/dist/auth/worker.js.map +1 -1
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/worker/_cors.cjs +2 -0
- package/dist/worker/_cors.cjs.map +1 -0
- package/dist/worker/_cors.d.ts +13 -0
- package/dist/worker/_cors.d.ts.map +1 -0
- package/dist/worker/_cors.js +32 -0
- package/dist/worker/_cors.js.map +1 -0
- package/dist/worker/_pagination.cjs +1 -1
- package/dist/worker/_pagination.cjs.map +1 -1
- package/dist/worker/_pagination.js +18 -18
- package/dist/worker/_pagination.js.map +1 -1
- package/dist/worker/auth.cjs +1 -1
- package/dist/worker/auth.cjs.map +1 -1
- package/dist/worker/auth.d.ts.map +1 -1
- package/dist/worker/auth.js +18 -18
- package/dist/worker/auth.js.map +1 -1
- package/dist/worker/env.cjs +1 -1
- package/dist/worker/env.cjs.map +1 -1
- package/dist/worker/env.d.ts +1 -22
- package/dist/worker/env.d.ts.map +1 -1
- package/dist/worker/env.js +1 -1
- package/dist/worker/env.js.map +1 -1
- package/dist/worker/publications.cjs +1 -1
- package/dist/worker/publications.cjs.map +1 -1
- package/dist/worker/publications.d.ts.map +1 -1
- package/dist/worker/publications.js +263 -262
- package/dist/worker/publications.js.map +1 -1
- package/dist/worker/worker.cjs +1 -1
- package/dist/worker/worker.cjs.map +1 -1
- package/dist/worker/worker.d.ts +2 -4
- package/dist/worker/worker.d.ts.map +1 -1
- package/dist/worker/worker.js +124 -122
- package/dist/worker/worker.js.map +1 -1
- package/dist/worker.d.ts +5 -3
- package/dist/worker.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/worker/_service.cjs.map +0 -1
- package/dist/worker/_service.d.ts.map +0 -1
- package/dist/worker/http.cjs +0 -2
- package/dist/worker/http.cjs.map +0 -1
- package/dist/worker/http.d.ts.map +0 -1
- package/dist/worker/http.js.map +0 -1
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { ApiError as e, badRequest as t, conflict as n, forbidden as r, invalid as i, json as a, noStore as o, notFound as s, queryParams as c, readJsonBody as l } from "../_server/http.js";
|
|
2
|
+
import { UUID_RE as u } from "../_server/uuid.js";
|
|
3
|
+
import { ARCHIVE_LIMITS as d, SESSION_MAX_AGE_SECONDS as f, archiveAliasSchema as p, archiveCollectionMetaSchema as m, archiveHealthSchema as h, archiveIdentitySchema as g, archiveLikeStateSchema as _, archiveLikedIdsSchema as v, archiveLikesPageSchema as y, archiveModerationLogSchema as b, archiveOwnReportsSchema as x, archiveReportsSchema as S, archiveSortSchema as C, isWellFormedText as w, uuidSchema as T } from "../contract.js";
|
|
4
|
+
import { forwardPage as E, forwardPageParams as D, nextCursor as O } from "./_pagination.js";
|
|
5
|
+
import { SQL as k, likedPublicationsSql as A, likesPageSql as j, listPublicationsSql as M, moderationLogSql as N, reportsPageSql as P } from "./sql.js";
|
|
6
|
+
import { s as F } from "@vielzeug/spell";
|
|
6
7
|
// src/worker/publications.ts
|
|
7
|
-
async function databaseCall(
|
|
8
|
+
async function databaseCall(t) {
|
|
8
9
|
try {
|
|
9
|
-
return await
|
|
10
|
-
} catch (
|
|
11
|
-
throw new
|
|
12
|
-
cause:
|
|
10
|
+
return await t();
|
|
11
|
+
} catch (t) {
|
|
12
|
+
throw new e(500, "internal", "The service is not available.", {
|
|
13
|
+
cause: t,
|
|
13
14
|
diagnostic: { kind: "database" }
|
|
14
15
|
});
|
|
15
16
|
}
|
|
@@ -18,7 +19,7 @@ var first = (e, t, ...n) => databaseCall(() => e.DB.prepare(t).bind(...n).first(
|
|
|
18
19
|
let r = await e.DB.prepare(t).bind(...n).all();
|
|
19
20
|
if (!Array.isArray(r.results)) throw Error("Missing database query results");
|
|
20
21
|
return r.results;
|
|
21
|
-
}), run = (e, t, ...n) => databaseCall(() => e.DB.prepare(t).bind(...n).run()), uuid = () => crypto.randomUUID(), now = () => (/* @__PURE__ */ new Date()).toISOString(), recordUnavailable = () => new
|
|
22
|
+
}), run = (e, t, ...n) => databaseCall(() => e.DB.prepare(t).bind(...n).run()), uuid = () => crypto.randomUUID(), now = () => (/* @__PURE__ */ new Date()).toISOString(), recordUnavailable = () => new e(503, "record-unavailable", "The record is not available.", { diagnostic: { kind: "record" } }), rowToEntry = (e, t) => {
|
|
22
23
|
let n;
|
|
23
24
|
try {
|
|
24
25
|
n = JSON.parse(t.payload_json);
|
|
@@ -44,19 +45,19 @@ var first = (e, t, ...n) => databaseCall(() => e.DB.prepare(t).bind(...n).first(
|
|
|
44
45
|
return r.data;
|
|
45
46
|
}, rowToOwnedEntry = (e, t) => {
|
|
46
47
|
let n = rowToEntry(e, t);
|
|
47
|
-
if (!
|
|
48
|
+
if (!T.safeParse(t.client_key).success) throw recordUnavailable();
|
|
48
49
|
return {
|
|
49
50
|
...n,
|
|
50
51
|
clientKey: t.client_key,
|
|
51
52
|
hidden: t.hidden_at !== null
|
|
52
53
|
};
|
|
53
54
|
}, entryById = async (e, t, n) => {
|
|
54
|
-
let r = await first(t,
|
|
55
|
+
let r = await first(t, k.publication, n, e.name);
|
|
55
56
|
return r === null ? null : rowToEntry(e, r);
|
|
56
|
-
}, searchMatchFor = (
|
|
57
|
-
if (
|
|
58
|
-
if (
|
|
59
|
-
let n = (
|
|
57
|
+
}, searchMatchFor = (e) => {
|
|
58
|
+
if (e === null) return;
|
|
59
|
+
if (e.length > d.searchChars) throw t();
|
|
60
|
+
let n = (e.match(/[\p{L}\p{N}]+/gu) ?? []).slice(0, d.searchTerms);
|
|
60
61
|
if (n.length !== 0) return n.map((e) => `"${e}"*`).join(" ");
|
|
61
62
|
}, entryCursorContext = (e) => JSON.stringify([
|
|
62
63
|
e.collection,
|
|
@@ -69,15 +70,15 @@ var first = (e, t, ...n) => databaseCall(() => e.DB.prepare(t).bind(...n).first(
|
|
|
69
70
|
createdAt: e.published_at,
|
|
70
71
|
id: e.id,
|
|
71
72
|
likeCount: e.like_count
|
|
72
|
-
}), I = 16, vocabValues = (e,
|
|
73
|
-
let
|
|
74
|
-
if (
|
|
75
|
-
let
|
|
76
|
-
if (
|
|
77
|
-
return
|
|
73
|
+
}), I = 16, vocabValues = (e, n, r) => {
|
|
74
|
+
let i = e.getAll(n);
|
|
75
|
+
if (i.length > I) throw t();
|
|
76
|
+
let a = [...new Set(i)].sort();
|
|
77
|
+
if (a.some((e) => !r.includes(e))) throw t();
|
|
78
|
+
return a;
|
|
78
79
|
};
|
|
79
|
-
async function listEntries(e,
|
|
80
|
-
let
|
|
80
|
+
async function listEntries(e, n, r) {
|
|
81
|
+
let i = new URL(n.url), s = c(i, [
|
|
81
82
|
"author",
|
|
82
83
|
"cursor",
|
|
83
84
|
"filter",
|
|
@@ -85,76 +86,76 @@ async function listEntries(e, t, n) {
|
|
|
85
86
|
"search",
|
|
86
87
|
"sort",
|
|
87
88
|
"tag"
|
|
88
|
-
], ["filter", "tag"]),
|
|
89
|
-
if (!
|
|
90
|
-
let
|
|
91
|
-
if (
|
|
92
|
-
let
|
|
93
|
-
author:
|
|
89
|
+
], ["filter", "tag"]), l = s.get("sort") ?? "recent", d = C.safeParse(l);
|
|
90
|
+
if (!d.success) throw t();
|
|
91
|
+
let f = vocabValues(s, "filter", e.filters), p = vocabValues(s, "tag", e.tags), m = s.get("author");
|
|
92
|
+
if (m !== null && !u.test(m)) throw t();
|
|
93
|
+
let h = searchMatchFor(s.get("search")), g = entryCursorContext({
|
|
94
|
+
author: m ?? void 0,
|
|
94
95
|
collection: e.name,
|
|
95
|
-
filters:
|
|
96
|
-
searchMatch:
|
|
97
|
-
sort:
|
|
98
|
-
tags:
|
|
99
|
-
}), { cursor:
|
|
100
|
-
author:
|
|
96
|
+
filters: f,
|
|
97
|
+
searchMatch: h,
|
|
98
|
+
sort: d.data,
|
|
99
|
+
tags: p
|
|
100
|
+
}), { cursor: _, limit: v } = D(s, g), y = {
|
|
101
|
+
author: m ?? void 0,
|
|
101
102
|
collection: e.name,
|
|
102
|
-
cursor:
|
|
103
|
-
createdAt:
|
|
104
|
-
id:
|
|
105
|
-
likeCount:
|
|
103
|
+
cursor: _ === null ? void 0 : {
|
|
104
|
+
createdAt: _.createdAt,
|
|
105
|
+
id: _.id,
|
|
106
|
+
likeCount: _.likeCount ?? 0
|
|
106
107
|
},
|
|
107
|
-
filters:
|
|
108
|
-
limit:
|
|
109
|
-
searchMatch:
|
|
110
|
-
sort:
|
|
111
|
-
tags:
|
|
112
|
-
},
|
|
113
|
-
cursor:
|
|
114
|
-
entries:
|
|
108
|
+
filters: f,
|
|
109
|
+
limit: v + 1,
|
|
110
|
+
searchMatch: h,
|
|
111
|
+
sort: d.data,
|
|
112
|
+
tags: p
|
|
113
|
+
}, b = M(y), x = await all(r, b.sql, ...b.parameters), S = x.slice(0, v), w = S.at(-1), T = {
|
|
114
|
+
cursor: x.length > v && w !== void 0 ? O(cursorFromRow(w), g) : null,
|
|
115
|
+
entries: S.map((t) => rowToEntry(e, t))
|
|
115
116
|
};
|
|
116
|
-
return
|
|
117
|
+
return a(200, T, o);
|
|
117
118
|
}
|
|
118
119
|
function collectionMeta(e) {
|
|
119
|
-
let t =
|
|
120
|
+
let t = m.safeParse({
|
|
120
121
|
filters: [...e.filters].sort(),
|
|
121
122
|
name: e.name,
|
|
122
123
|
tags: [...e.tags].sort()
|
|
123
124
|
});
|
|
124
125
|
if (!t.success) throw recordUnavailable();
|
|
125
|
-
return
|
|
126
|
+
return a(200, t.data, o);
|
|
126
127
|
}
|
|
127
128
|
async function getEntry(e, t, n) {
|
|
128
129
|
let r = await entryById(e, t, n);
|
|
129
|
-
if (r === null) throw
|
|
130
|
-
return
|
|
130
|
+
if (r === null) throw s();
|
|
131
|
+
return a(200, r, o);
|
|
131
132
|
}
|
|
132
|
-
async function healthJson(
|
|
133
|
-
let
|
|
133
|
+
async function healthJson(e, t) {
|
|
134
|
+
let n = null, r = null;
|
|
134
135
|
try {
|
|
135
|
-
|
|
136
|
+
n = (await first(t, k.schemaVersion))?.applied ?? 0;
|
|
136
137
|
} catch {
|
|
137
|
-
|
|
138
|
+
r = "database-unavailable";
|
|
138
139
|
}
|
|
139
|
-
|
|
140
|
-
let
|
|
141
|
-
dbSchemaVersion:
|
|
140
|
+
r === null && n !== 1 && (r = "schema-mismatch");
|
|
141
|
+
let i = h.safeParse({
|
|
142
|
+
dbSchemaVersion: n,
|
|
142
143
|
features: {
|
|
143
|
-
likes:
|
|
144
|
-
publishing:
|
|
144
|
+
likes: e.likesEnabled,
|
|
145
|
+
publishing: e.publishingEnabled
|
|
145
146
|
},
|
|
146
|
-
limits:
|
|
147
|
-
reason:
|
|
147
|
+
limits: d,
|
|
148
|
+
reason: r,
|
|
148
149
|
schemaVersion: 1,
|
|
149
|
-
status:
|
|
150
|
+
status: r === null ? "ok" : "degraded"
|
|
150
151
|
});
|
|
151
|
-
if (!
|
|
152
|
-
return
|
|
152
|
+
if (!i.success) throw recordUnavailable();
|
|
153
|
+
return a(r === null ? 200 : 503, i.data, o);
|
|
153
154
|
}
|
|
154
155
|
var identityJson = (e, t) => {
|
|
155
|
-
let n =
|
|
156
|
+
let n = g.safeParse(t);
|
|
156
157
|
if (!n.success) throw recordUnavailable();
|
|
157
|
-
return
|
|
158
|
+
return a(e, n.data, o);
|
|
158
159
|
};
|
|
159
160
|
function getMe(e) {
|
|
160
161
|
return identityJson(200, e?.publisher ? {
|
|
@@ -163,61 +164,61 @@ function getMe(e) {
|
|
|
163
164
|
operator: e.operator
|
|
164
165
|
} : null);
|
|
165
166
|
}
|
|
166
|
-
async function registerMe(
|
|
167
|
-
let
|
|
168
|
-
if (!
|
|
169
|
-
let
|
|
170
|
-
if (
|
|
171
|
-
id:
|
|
172
|
-
name:
|
|
173
|
-
operator:
|
|
167
|
+
async function registerMe(t, r, a) {
|
|
168
|
+
let o = await l(t), s = L.safeParse(o);
|
|
169
|
+
if (!s.success) throw i();
|
|
170
|
+
let c = s.data.name ?? "", u = await first(r, k.publisher, a.subject.issuer, a.subject.subject);
|
|
171
|
+
if (u !== null) return identityJson(200, {
|
|
172
|
+
id: u.id,
|
|
173
|
+
name: u.display_name,
|
|
174
|
+
operator: a.operator
|
|
174
175
|
});
|
|
175
|
-
let
|
|
176
|
-
await run(
|
|
177
|
-
let
|
|
178
|
-
if (
|
|
176
|
+
let d = uuid();
|
|
177
|
+
await run(r, k.insertPublisher, d, a.subject.issuer, a.subject.subject, c, now(), a.subject.issuer, a.subject.subject, Date.now());
|
|
178
|
+
let f = await first(r, k.publisher, a.subject.issuer, a.subject.subject);
|
|
179
|
+
if (f === null) throw await first(r, k.revokedSubject, a.subject.issuer, a.subject.subject, Date.now()) === null ? n() : new e(401, "unauthenticated", "Sign-in is required.");
|
|
179
180
|
return identityJson(201, {
|
|
180
|
-
id:
|
|
181
|
-
name:
|
|
182
|
-
operator:
|
|
181
|
+
id: f.id,
|
|
182
|
+
name: f.display_name,
|
|
183
|
+
operator: a.operator
|
|
183
184
|
});
|
|
184
185
|
}
|
|
185
186
|
async function renameMe(e, t, n) {
|
|
186
|
-
let r = requirePublisherRow(n),
|
|
187
|
-
if (!
|
|
188
|
-
return
|
|
187
|
+
let r = requirePublisherRow(n), a = await l(e), o = R.safeParse(a);
|
|
188
|
+
if (!o.success) throw i();
|
|
189
|
+
return o.data.name !== r.displayName && await run(t, k.renamePublisher, o.data.name, r.id, o.data.name), identityJson(200, {
|
|
189
190
|
id: r.id,
|
|
190
|
-
name:
|
|
191
|
+
name: o.data.name,
|
|
191
192
|
operator: n.operator
|
|
192
193
|
});
|
|
193
194
|
}
|
|
194
195
|
async function deleteProfile(e, t) {
|
|
195
|
-
return t.publisher !== null && await run(e,
|
|
196
|
+
return t.publisher !== null && await run(e, k.deletePublisher, t.publisher.id), new Response(null, {
|
|
196
197
|
headers: { "cache-control": "no-store" },
|
|
197
198
|
status: 204
|
|
198
199
|
});
|
|
199
200
|
}
|
|
200
|
-
async function deleteAccountProfile(e,
|
|
201
|
-
let { issuer:
|
|
202
|
-
return await databaseCall(() => e.DB.batch([e.DB.prepare(
|
|
203
|
-
headers:
|
|
201
|
+
async function deleteAccountProfile(e, t) {
|
|
202
|
+
let { issuer: n, subject: r } = t.subject;
|
|
203
|
+
return await databaseCall(() => e.DB.batch([e.DB.prepare(k.revokeSubject).bind(n, r, Date.now() + (f + 10) * 1e3), e.DB.prepare(k.deleteSubjectPublisher).bind(n, r)])), new Response(null, {
|
|
204
|
+
headers: o,
|
|
204
205
|
status: 204
|
|
205
206
|
});
|
|
206
207
|
}
|
|
207
208
|
async function erasePublisher(e, t, n) {
|
|
208
209
|
let r = requireOperatorRow(t);
|
|
209
|
-
return await databaseCall(() => e.DB.batch([e.DB.prepare(
|
|
210
|
-
headers:
|
|
210
|
+
return await databaseCall(() => e.DB.batch([e.DB.prepare(k.auditErasure).bind(uuid(), r.id, now(), n), e.DB.prepare(k.deletePublisher).bind(n)])), new Response(null, {
|
|
211
|
+
headers: o,
|
|
211
212
|
status: 204
|
|
212
213
|
});
|
|
213
214
|
}
|
|
214
215
|
async function listModerationLog(e, t, n) {
|
|
215
216
|
requireOperatorRow(n);
|
|
216
|
-
let r = "moderation-log", { cursor: i, limit:
|
|
217
|
+
let r = "moderation-log", { cursor: i, limit: s } = E(e, r), c = N({
|
|
217
218
|
cursor: i,
|
|
218
|
-
limit:
|
|
219
|
-
}),
|
|
220
|
-
actions:
|
|
219
|
+
limit: s + 1
|
|
220
|
+
}), l = await all(t, c.sql, ...c.parameters), u = l.slice(0, s), d = u.at(-1), f = b.safeParse({
|
|
221
|
+
actions: u.map((e) => ({
|
|
221
222
|
action: e.action,
|
|
222
223
|
collection: e.collection,
|
|
223
224
|
createdAt: e.created_at,
|
|
@@ -225,198 +226,198 @@ async function listModerationLog(e, t, n) {
|
|
|
225
226
|
operatorId: e.operator_id,
|
|
226
227
|
targetId: e.target_id
|
|
227
228
|
})),
|
|
228
|
-
cursor:
|
|
229
|
-
createdAt:
|
|
230
|
-
id:
|
|
229
|
+
cursor: l.length > s && d !== void 0 ? O({
|
|
230
|
+
createdAt: d.created_at,
|
|
231
|
+
id: d.id
|
|
231
232
|
}, r) : null
|
|
232
233
|
});
|
|
233
|
-
if (!
|
|
234
|
-
return
|
|
234
|
+
if (!f.success) throw recordUnavailable();
|
|
235
|
+
return a(200, f.data, o);
|
|
235
236
|
}
|
|
236
237
|
var requirePublisherRow = (e) => {
|
|
237
|
-
if (e.publisher === null) throw
|
|
238
|
+
if (e.publisher === null) throw r("Register before publishing.");
|
|
238
239
|
return e.publisher;
|
|
239
240
|
}, requireOperatorRow = (e) => {
|
|
240
|
-
if (!e.operator) throw
|
|
241
|
-
if (e.publisher === null) throw
|
|
241
|
+
if (!e.operator) throw r("Operator access is required.");
|
|
242
|
+
if (e.publisher === null) throw r("Register before moderating.");
|
|
242
243
|
return e.publisher;
|
|
243
|
-
}, L =
|
|
244
|
-
async function myPublications(t, n
|
|
245
|
-
if (
|
|
246
|
-
let
|
|
247
|
-
return
|
|
244
|
+
}, L = F.object({ name: p.optional() }), R = F.object({ name: p });
|
|
245
|
+
async function myPublications(e, t, n) {
|
|
246
|
+
if (n.publisher === null) return a(200, { entries: [] }, o);
|
|
247
|
+
let r = await all(t, k.ownPublications, n.publisher.id, e.name, d.maxPublications);
|
|
248
|
+
return a(200, { entries: r.map((t) => rowToOwnedEntry(e, t)) }, o);
|
|
248
249
|
}
|
|
249
|
-
async function myLikes(
|
|
250
|
-
let i = new URL(
|
|
250
|
+
async function myLikes(e, n, r) {
|
|
251
|
+
let i = new URL(e.url).searchParams.get("ids");
|
|
251
252
|
if (i === null) {
|
|
252
|
-
let
|
|
253
|
-
if (r.publisher === null) return
|
|
253
|
+
let t = `likes:${r.publisher?.id ?? "unregistered"}`, { cursor: i, limit: s } = E(e, t);
|
|
254
|
+
if (r.publisher === null) return a(200, {
|
|
254
255
|
cursor: null,
|
|
255
256
|
ids: []
|
|
256
|
-
},
|
|
257
|
-
let
|
|
257
|
+
}, o);
|
|
258
|
+
let c = j(r.publisher.id, {
|
|
258
259
|
cursor: i,
|
|
259
|
-
limit:
|
|
260
|
-
}),
|
|
261
|
-
cursor:
|
|
262
|
-
createdAt:
|
|
263
|
-
id:
|
|
264
|
-
},
|
|
265
|
-
ids:
|
|
260
|
+
limit: s + 1
|
|
261
|
+
}), l = await all(n, c.sql, ...c.parameters), u = l.slice(0, s), d = u.at(-1), f = y.safeParse({
|
|
262
|
+
cursor: l.length > s && d !== void 0 ? O({
|
|
263
|
+
createdAt: d.created_at,
|
|
264
|
+
id: d.publication_id
|
|
265
|
+
}, t) : null,
|
|
266
|
+
ids: u.map((e) => e.publication_id)
|
|
266
267
|
});
|
|
267
|
-
if (!
|
|
268
|
-
return
|
|
268
|
+
if (!f.success) throw recordUnavailable();
|
|
269
|
+
return a(200, f.data, o);
|
|
269
270
|
}
|
|
270
|
-
|
|
271
|
-
let
|
|
272
|
-
if (
|
|
273
|
-
if (r.publisher === null) return
|
|
274
|
-
let
|
|
275
|
-
if (!
|
|
276
|
-
return
|
|
271
|
+
c(new URL(e.url), ["ids"]);
|
|
272
|
+
let s = [...new Set(i.split(","))];
|
|
273
|
+
if (s.length > d.likeStateIds || s.some((e) => !u.test(e))) throw t();
|
|
274
|
+
if (r.publisher === null) return a(200, { ids: [] }, o);
|
|
275
|
+
let l = A(r.publisher.id, s), f = await all(n, l.sql, ...l.parameters), p = new Set(f.map((e) => e.publication_id)), m = { ids: s.filter((e) => p.has(e)) }, h = v.safeParse(m);
|
|
276
|
+
if (!h.success) throw recordUnavailable();
|
|
277
|
+
return a(200, h.data, o);
|
|
277
278
|
}
|
|
278
|
-
var z =
|
|
279
|
-
clientKey:
|
|
280
|
-
payload:
|
|
281
|
-
}), B =
|
|
282
|
-
expectedRevision:
|
|
283
|
-
payload:
|
|
279
|
+
var z = F.object({
|
|
280
|
+
clientKey: T,
|
|
281
|
+
payload: F.unknown()
|
|
282
|
+
}), B = F.object({
|
|
283
|
+
expectedRevision: F.number().int().min(1).max(2 ** 53 - 1),
|
|
284
|
+
payload: F.unknown()
|
|
284
285
|
});
|
|
285
|
-
async function resolveRetryRecord(
|
|
286
|
-
if (
|
|
287
|
-
if (
|
|
288
|
-
if (
|
|
289
|
-
let
|
|
290
|
-
return
|
|
286
|
+
async function resolveRetryRecord(t, r, i, s) {
|
|
287
|
+
if (i === null || i.expires_at <= Date.now()) return null;
|
|
288
|
+
if (i.payload_hash !== s) throw n("That key was used for a different payload.");
|
|
289
|
+
if (i.publication_id === null) throw new e(410, "gone", "That publication was removed.");
|
|
290
|
+
let c = await entryById(t, r, i.publication_id);
|
|
291
|
+
return c === null ? "vanished" : a(200, c, o);
|
|
291
292
|
}
|
|
292
|
-
async function publishEntry(t,
|
|
293
|
-
if (!
|
|
294
|
-
let
|
|
295
|
-
if (!
|
|
296
|
-
let { clientKey:
|
|
297
|
-
if (
|
|
298
|
-
let
|
|
299
|
-
if (
|
|
300
|
-
let
|
|
301
|
-
if (
|
|
302
|
-
if (
|
|
303
|
-
if (
|
|
304
|
-
return
|
|
293
|
+
async function publishEntry(t, s, c, u, f) {
|
|
294
|
+
if (!u.publishingEnabled) throw new e(503, "publishing-disabled", "Publishing is currently disabled.");
|
|
295
|
+
let p = requirePublisherRow(f), m = await l(s), h = z.safeParse(m);
|
|
296
|
+
if (!h.success) throw i();
|
|
297
|
+
let { clientKey: g } = h.data, _ = await t.derive(h.data.payload);
|
|
298
|
+
if (_ === null) throw i();
|
|
299
|
+
let v = _.canonicalJson, y = _.contentHash, b = p.id, x = t.name, S = await resolveRetryRecord(t, c, await first(c, k.request, b, x, g), y);
|
|
300
|
+
if (S !== null && S !== "vanished") return S;
|
|
301
|
+
let C = await first(c, k.publicationByKey, b, x, g);
|
|
302
|
+
if (C !== null) {
|
|
303
|
+
if (C.hidden_at !== null) throw r("This entry is under moderation review.");
|
|
304
|
+
if (C.content_hash !== y) throw n("That key was used for a different payload.");
|
|
305
|
+
return a(200, rowToEntry(t, C), o);
|
|
305
306
|
}
|
|
306
|
-
let
|
|
307
|
-
if (((await databaseCall(() =>
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
307
|
+
let w = now(), T = Date.now() + d.idempotencyRetentionMs, E = JSON.stringify(_.tags);
|
|
308
|
+
if (((await databaseCall(() => c.DB.batch([
|
|
309
|
+
c.DB.prepare(k.expireRequests).bind(Date.now()),
|
|
310
|
+
c.DB.prepare(k.insertPublication).bind(uuid(), b, x, g, 1, _.contentVersion, v, y, _.filter, _.title, E, w, w, b, x, d.maxPublications, b, x, g, Date.now()),
|
|
311
|
+
c.DB.prepare(k.insertRequest).bind(b, x, g, y, w, T, b, x, g, y)
|
|
311
312
|
])))[1]?.meta?.changes ?? 0) === 0) {
|
|
312
|
-
let
|
|
313
|
-
if (
|
|
314
|
-
if (
|
|
315
|
-
let
|
|
316
|
-
if (
|
|
317
|
-
if (
|
|
318
|
-
throw
|
|
313
|
+
let i = await resolveRetryRecord(t, c, await first(c, k.request, b, x, g), y);
|
|
314
|
+
if (i === "vanished") throw new e(410, "gone", "That publication was removed.");
|
|
315
|
+
if (i !== null) return i;
|
|
316
|
+
let s = await first(c, k.publicationByKey, b, x, g);
|
|
317
|
+
if (s !== null && s.hidden_at !== null) throw r("This entry is under moderation review.");
|
|
318
|
+
if (s !== null && s.content_hash === y) return a(200, rowToEntry(t, s), o);
|
|
319
|
+
throw s === null ? new e(429, "quota", "Publication quota reached.") : n("That key was used for a different payload.");
|
|
319
320
|
}
|
|
320
|
-
let
|
|
321
|
-
if (
|
|
322
|
-
return
|
|
321
|
+
let D = await first(c, k.publicationByKey, b, x, g);
|
|
322
|
+
if (D === null) throw n();
|
|
323
|
+
return a(201, rowToEntry(t, D), o);
|
|
323
324
|
}
|
|
324
|
-
async function updateEntry(
|
|
325
|
-
if (!
|
|
326
|
-
let
|
|
327
|
-
if (
|
|
328
|
-
if (
|
|
329
|
-
let
|
|
330
|
-
if (!
|
|
331
|
-
let { expectedRevision:
|
|
332
|
-
if (
|
|
333
|
-
let
|
|
334
|
-
if (
|
|
335
|
-
let
|
|
336
|
-
if (
|
|
337
|
-
let
|
|
338
|
-
if (
|
|
339
|
-
if (
|
|
340
|
-
if (
|
|
341
|
-
return
|
|
325
|
+
async function updateEntry(t, c, u, d, f, p) {
|
|
326
|
+
if (!d.publishingEnabled) throw new e(503, "publishing-disabled", "Publishing is currently disabled.");
|
|
327
|
+
let m = requirePublisherRow(f), h = await first(u, k.publicationAny, p, t.name);
|
|
328
|
+
if (h === null || h.owner_id !== m.id) throw s();
|
|
329
|
+
if (h.hidden_at !== null) throw r("This entry is under moderation review.");
|
|
330
|
+
let g = await l(c), _ = B.safeParse(g);
|
|
331
|
+
if (!_.success) throw i();
|
|
332
|
+
let { expectedRevision: v } = _.data;
|
|
333
|
+
if (v !== h.revision) throw n();
|
|
334
|
+
let y = await t.derive(_.data.payload);
|
|
335
|
+
if (y === null) throw i();
|
|
336
|
+
let b = y.canonicalJson, x = y.contentHash;
|
|
337
|
+
if (x === h.content_hash) {
|
|
338
|
+
let e = await first(u, k.publicationAny, p, t.name);
|
|
339
|
+
if (e === null || e.owner_id !== m.id) throw s();
|
|
340
|
+
if (e.hidden_at !== null) throw r("This entry is under moderation review.");
|
|
341
|
+
if (e.revision !== v) throw n();
|
|
342
|
+
return a(200, rowToEntry(t, e), o);
|
|
342
343
|
}
|
|
343
|
-
let
|
|
344
|
-
if (((await run(
|
|
345
|
-
let
|
|
346
|
-
throw
|
|
344
|
+
let S = JSON.stringify(y.tags);
|
|
345
|
+
if (((await run(u, k.updatePublication, 1, y.contentVersion, b, x, y.filter, y.title, S, now(), p, m.id, v, x)).meta?.changes ?? 0) === 0) {
|
|
346
|
+
let e = await first(u, k.publicationAny, p, t.name);
|
|
347
|
+
throw e === null ? s() : e.hidden_at === null ? n() : r("This entry is under moderation review.");
|
|
347
348
|
}
|
|
348
|
-
let
|
|
349
|
-
if (
|
|
350
|
-
return
|
|
349
|
+
let C = await entryById(t, u, p);
|
|
350
|
+
if (C === null) throw s();
|
|
351
|
+
return a(200, C, o);
|
|
351
352
|
}
|
|
352
353
|
async function deleteEntry(e, t, n, r) {
|
|
353
354
|
if (n.operator) {
|
|
354
355
|
let i = requireOperatorRow(n);
|
|
355
|
-
await databaseCall(() => t.DB.batch([t.DB.prepare(
|
|
356
|
+
await databaseCall(() => t.DB.batch([t.DB.prepare(k.auditRemoval).bind(uuid(), i.id, now(), r, e.name), t.DB.prepare(k.removePublicationAny).bind(r, e.name)]));
|
|
356
357
|
} else {
|
|
357
|
-
let i = requirePublisherRow(n), a = await first(t,
|
|
358
|
+
let i = requirePublisherRow(n), a = await first(t, k.publicationAny, r, e.name);
|
|
358
359
|
if (a === null) return new Response(null, {
|
|
359
|
-
headers:
|
|
360
|
+
headers: o,
|
|
360
361
|
status: 204
|
|
361
362
|
});
|
|
362
|
-
if (a.owner_id !== i.id) throw
|
|
363
|
-
await run(t,
|
|
363
|
+
if (a.owner_id !== i.id) throw s();
|
|
364
|
+
await run(t, k.removePublication, r, i.id);
|
|
364
365
|
}
|
|
365
366
|
return new Response(null, {
|
|
366
|
-
headers:
|
|
367
|
+
headers: o,
|
|
367
368
|
status: 204
|
|
368
369
|
});
|
|
369
370
|
}
|
|
370
|
-
async function setLike(
|
|
371
|
-
let
|
|
372
|
-
if (
|
|
373
|
-
if (await first(
|
|
374
|
-
|
|
375
|
-
let
|
|
376
|
-
if (
|
|
377
|
-
let
|
|
378
|
-
id:
|
|
379
|
-
likeCount:
|
|
380
|
-
liked:
|
|
381
|
-
},
|
|
382
|
-
if (!
|
|
383
|
-
return
|
|
371
|
+
async function setLike(t, n, r, i, c, l) {
|
|
372
|
+
let u = requirePublisherRow(i);
|
|
373
|
+
if (l && !r.likesEnabled) throw new e(503, "likes-disabled", "Likes are currently disabled.");
|
|
374
|
+
if (await first(n, k.publication, c, t.name) === null) throw s();
|
|
375
|
+
l ? await run(n, k.insertLike, u.id, now(), c) : await run(n, k.removeLike, c, u.id);
|
|
376
|
+
let d = await first(n, k.likeState, u.id, c);
|
|
377
|
+
if (d === null) throw s();
|
|
378
|
+
let f = {
|
|
379
|
+
id: d.id,
|
|
380
|
+
likeCount: d.like_count,
|
|
381
|
+
liked: d.liked === 1
|
|
382
|
+
}, p = _.safeParse(f);
|
|
383
|
+
if (!p.success) throw recordUnavailable();
|
|
384
|
+
return a(200, p.data, o);
|
|
384
385
|
}
|
|
385
|
-
var V =
|
|
386
|
-
async function reportEntry(e, t, n, r,
|
|
387
|
-
let
|
|
388
|
-
if (!
|
|
389
|
-
if (await first(n,
|
|
390
|
-
return await run(n,
|
|
391
|
-
headers:
|
|
386
|
+
var V = F.object({ reason: F.string().check(w).trim().min(1).max(d.reportReasonChars) });
|
|
387
|
+
async function reportEntry(e, t, n, r, a) {
|
|
388
|
+
let c = requirePublisherRow(r), u = await l(t), d = V.safeParse(u);
|
|
389
|
+
if (!d.success) throw i();
|
|
390
|
+
if (await first(n, k.publication, a, e.name) === null) throw s();
|
|
391
|
+
return await run(n, k.insertReport, uuid(), c.id, d.data.reason, now(), a, e.name), new Response(null, {
|
|
392
|
+
headers: o,
|
|
392
393
|
status: 204
|
|
393
394
|
});
|
|
394
395
|
}
|
|
395
396
|
async function setHidden(e, t, n, r, i) {
|
|
396
397
|
let a = requireOperatorRow(n);
|
|
397
|
-
if (await first(t,
|
|
398
|
-
let
|
|
399
|
-
return await databaseCall(() => t.DB.batch([t.DB.prepare(
|
|
400
|
-
headers:
|
|
398
|
+
if (await first(t, k.publicationAny, r, e.name) === null) throw s();
|
|
399
|
+
let c = i ? t.DB.prepare(k.hidePublication).bind(now(), r, e.name) : t.DB.prepare(k.unhidePublication).bind(r, e.name);
|
|
400
|
+
return await databaseCall(() => t.DB.batch([t.DB.prepare(k.auditHide).bind(uuid(), a.id, i ? "hide" : "unhide", now(), r, e.name, Number(i)), c])), new Response(null, {
|
|
401
|
+
headers: o,
|
|
401
402
|
status: 204
|
|
402
403
|
});
|
|
403
404
|
}
|
|
404
|
-
async function listReports(e,
|
|
405
|
-
requireOperatorRow(
|
|
406
|
-
let
|
|
407
|
-
if (
|
|
408
|
-
let
|
|
405
|
+
async function listReports(e, n, r, i) {
|
|
406
|
+
requireOperatorRow(i);
|
|
407
|
+
let s = new URL(n.url).searchParams.get("status") ?? "unresolved";
|
|
408
|
+
if (s !== "unresolved" && s !== "resolved") throw t();
|
|
409
|
+
let c = `reports:${e.name}:${s}`, { cursor: l, limit: u } = E(n, c, ["status"]), d = P({
|
|
409
410
|
collection: e.name,
|
|
410
|
-
resolved:
|
|
411
|
+
resolved: s === "resolved"
|
|
411
412
|
}, {
|
|
412
|
-
cursor:
|
|
413
|
-
limit:
|
|
414
|
-
}),
|
|
415
|
-
cursor:
|
|
416
|
-
createdAt:
|
|
417
|
-
id:
|
|
418
|
-
},
|
|
419
|
-
reports:
|
|
413
|
+
cursor: l,
|
|
414
|
+
limit: u + 1
|
|
415
|
+
}), f = await all(r, d.sql, ...d.parameters), p = f.slice(0, u), m = p.at(-1), h = S.safeParse({
|
|
416
|
+
cursor: f.length > u && m !== void 0 ? O({
|
|
417
|
+
createdAt: m.created_at,
|
|
418
|
+
id: m.id
|
|
419
|
+
}, c) : null,
|
|
420
|
+
reports: p.map((e) => ({
|
|
420
421
|
createdAt: e.created_at,
|
|
421
422
|
entryId: e.publication_id,
|
|
422
423
|
entryTitle: e.entry_title,
|
|
@@ -429,31 +430,31 @@ async function listReports(e, t, n, r) {
|
|
|
429
430
|
resolvedAt: e.resolved_at
|
|
430
431
|
}))
|
|
431
432
|
});
|
|
432
|
-
if (!
|
|
433
|
-
return
|
|
433
|
+
if (!h.success) throw recordUnavailable();
|
|
434
|
+
return a(200, h.data, o);
|
|
434
435
|
}
|
|
435
436
|
async function resolveReport(e, t, n, r) {
|
|
436
|
-
if (requireOperatorRow(n), await first(t,
|
|
437
|
-
return await run(t,
|
|
438
|
-
headers:
|
|
437
|
+
if (requireOperatorRow(n), await first(t, k.reportById, r, e.name) === null) throw s();
|
|
438
|
+
return await run(t, k.resolveReport, now(), r, e.name), new Response(null, {
|
|
439
|
+
headers: o,
|
|
439
440
|
status: 204
|
|
440
441
|
});
|
|
441
442
|
}
|
|
442
443
|
async function myReports(e, t, n) {
|
|
443
|
-
let r = `own-reports:${n.publisher?.id ?? "unregistered"}`, { cursor: i, limit:
|
|
444
|
-
if (n.publisher === null) return
|
|
444
|
+
let r = `own-reports:${n.publisher?.id ?? "unregistered"}`, { cursor: i, limit: s } = E(e, r);
|
|
445
|
+
if (n.publisher === null) return a(200, {
|
|
445
446
|
cursor: null,
|
|
446
447
|
reports: []
|
|
447
|
-
},
|
|
448
|
-
let
|
|
448
|
+
}, o);
|
|
449
|
+
let c = P({ publisherId: n.publisher.id }, {
|
|
449
450
|
cursor: i,
|
|
450
|
-
limit:
|
|
451
|
-
}),
|
|
452
|
-
cursor:
|
|
453
|
-
createdAt:
|
|
454
|
-
id:
|
|
451
|
+
limit: s + 1
|
|
452
|
+
}), l = await all(t, c.sql, ...c.parameters), u = l.slice(0, s), d = u.at(-1), f = x.safeParse({
|
|
453
|
+
cursor: l.length > s && d !== void 0 ? O({
|
|
454
|
+
createdAt: d.created_at,
|
|
455
|
+
id: d.id
|
|
455
456
|
}, r) : null,
|
|
456
|
-
reports:
|
|
457
|
+
reports: u.map((e) => ({
|
|
457
458
|
collection: e.collection,
|
|
458
459
|
createdAt: e.created_at,
|
|
459
460
|
entryId: e.publication_id,
|
|
@@ -462,8 +463,8 @@ async function myReports(e, t, n) {
|
|
|
462
463
|
resolvedAt: e.resolved_at
|
|
463
464
|
}))
|
|
464
465
|
});
|
|
465
|
-
if (!
|
|
466
|
-
return
|
|
466
|
+
if (!f.success) throw recordUnavailable();
|
|
467
|
+
return a(200, f.data, o);
|
|
467
468
|
}
|
|
468
469
|
export { collectionMeta, deleteAccountProfile, deleteEntry, deleteProfile, erasePublisher, getEntry, getMe, healthJson, listEntries, listModerationLog, listReports, myLikes, myPublications, myReports, publishEntry, registerMe, renameMe, reportEntry, requireOperatorRow, requirePublisherRow, resolveReport, setHidden, setLike, updateEntry };
|
|
469
470
|
|