tangled
alpha
login
or
join now
stevedylan.dev
/
sequoia
45
fork
atom
A CLI for publishing standard.site documents to ATProto
sequoia.pub
standard
site
lexicon
cli
publishing
45
fork
atom
overview
issues
7
pulls
1
pipelines
chore: applied formatting
stevedylan.dev
2 weeks ago
bcb1730f
efa2989a
+80
-28
3 changed files
expand all
collapse all
unified
split
docs
src
index.ts
lib
path-redirect.ts
routes
subscribe.ts
+15
-8
docs/src/index.ts
···
12
12
13
13
const app = new Hono<{ Bindings: Bindings }>();
14
14
15
15
+
app.use(
16
16
+
"/subscribe",
17
17
+
cors({
18
18
+
origin: (origin) => origin,
19
19
+
credentials: true,
20
20
+
}),
21
21
+
);
22
22
+
app.use(
23
23
+
"/subscribe/*",
24
24
+
cors({
25
25
+
origin: (origin) => origin,
26
26
+
credentials: true,
27
27
+
}),
28
28
+
);
29
29
+
15
30
app.route("/oauth", auth);
16
31
app.route("/subscribe", subscribe);
17
17
-
app.use("/subscribe", cors({
18
18
-
origin: (origin) => origin,
19
19
-
credentials: true,
20
20
-
}));
21
21
-
app.use("/subscribe/*", cors({
22
22
-
origin: (origin) => origin,
23
23
-
credentials: true,
24
24
-
}));
25
32
26
33
app.get("/api/health", (c) => {
27
34
return c.json({ status: "ok" });
+1
-4
docs/src/lib/path-redirect.ts
···
19
19
const OriginalRequest = globalThis.Request;
20
20
21
21
globalThis.Request = class extends OriginalRequest {
22
22
-
constructor(
23
23
-
input: RequestInfo | URL,
24
24
-
init?: RequestInit,
25
25
-
) {
22
22
+
constructor(input: RequestInfo | URL, init?: RequestInit) {
26
23
super(input, sanitizeInit(init));
27
24
if (init?.redirect === "error") {
28
25
errorRedirectRequests.add(this);
+64
-16
docs/src/routes/subscribe.ts
···
12
12
// Cache the vocs-generated stylesheet href across requests (changes on rebuild).
13
13
let _vocsStyleHref: string | null = null;
14
14
15
15
-
async function getVocsStyleHref(assets: Fetcher, baseUrl: string): Promise<string> {
15
15
+
async function getVocsStyleHref(
16
16
+
assets: Fetcher,
17
17
+
baseUrl: string,
18
18
+
): Promise<string> {
16
19
if (_vocsStyleHref) return _vocsStyleHref;
17
20
try {
18
21
const indexUrl = new URL("/", baseUrl).toString();
···
105
108
const session = await client.restore(did);
106
109
const agent = new Agent(session);
107
110
108
108
-
const existingUri = await findExistingSubscription(agent, did, publicationUri);
111
111
+
const existingUri = await findExistingSubscription(
112
112
+
agent,
113
113
+
did,
114
114
+
publicationUri,
115
115
+
);
109
116
if (existingUri) {
110
110
-
return c.json({ subscribed: true, existing: true, recordUri: existingUri });
117
117
+
return c.json({
118
118
+
subscribed: true,
119
119
+
existing: true,
120
120
+
recordUri: existingUri,
121
121
+
});
111
122
}
112
123
113
124
const result = await agent.com.atproto.repo.createRecord({
···
119
130
},
120
131
});
121
132
122
122
-
return c.json({ subscribed: true, existing: false, recordUri: result.data.uri });
133
133
+
return c.json({
134
134
+
subscribed: true,
135
135
+
existing: false,
136
136
+
recordUri: result.data.uri,
137
137
+
});
123
138
} catch (error) {
124
139
console.error("Subscribe POST error:", error);
125
140
// Treat expired/missing session as unauthenticated
···
141
156
const styleHref = await getVocsStyleHref(c.env.ASSETS, c.req.url);
142
157
143
158
if (!publicationUri || !publicationUri.startsWith("at://")) {
144
144
-
return c.html(renderError("Missing or invalid publication URI.", styleHref), 400);
159
159
+
return c.html(
160
160
+
renderError("Missing or invalid publication URI.", styleHref),
161
161
+
400,
162
162
+
);
145
163
}
146
164
147
165
const did = getSessionDid(c);
···
154
172
const session = await client.restore(did);
155
173
const agent = new Agent(session);
156
174
157
157
-
const existingUri = await findExistingSubscription(agent, did, publicationUri);
175
175
+
const existingUri = await findExistingSubscription(
176
176
+
agent,
177
177
+
did,
178
178
+
publicationUri,
179
179
+
);
158
180
if (existingUri) {
159
159
-
return c.html(renderSuccess(publicationUri, existingUri, true, styleHref));
181
181
+
return c.html(
182
182
+
renderSuccess(publicationUri, existingUri, true, styleHref),
183
183
+
);
160
184
}
161
185
162
186
const result = await agent.com.atproto.repo.createRecord({
···
168
192
},
169
193
});
170
194
171
171
-
return c.html(renderSuccess(publicationUri, result.data.uri, false, styleHref));
195
195
+
return c.html(
196
196
+
renderSuccess(publicationUri, result.data.uri, false, styleHref),
197
197
+
);
172
198
} catch (error) {
173
199
console.error("Subscribe GET error:", error);
174
200
// Session expired - ask the user to sign in again
175
175
-
return c.html(renderHandleForm(publicationUri, styleHref, "Session expired. Please sign in again."));
201
201
+
return c.html(
202
202
+
renderHandleForm(
203
203
+
publicationUri,
204
204
+
styleHref,
205
205
+
"Session expired. Please sign in again.",
206
206
+
),
207
207
+
);
176
208
}
177
209
});
178
210
···
190
222
191
223
if (!handle || !publicationUri) {
192
224
const styleHref = await getVocsStyleHref(c.env.ASSETS, c.req.url);
193
193
-
return c.html(renderError("Missing handle or publication URI.", styleHref), 400);
225
225
+
return c.html(
226
226
+
renderError("Missing handle or publication URI.", styleHref),
227
227
+
400,
228
228
+
);
194
229
}
195
230
196
231
const returnTo = `${c.env.CLIENT_URL}/subscribe?publicationUri=${encodeURIComponent(publicationUri)}`;
···
205
240
// HTML rendering
206
241
// ============================================================================
207
242
208
208
-
function renderHandleForm(publicationUri: string, styleHref: string, error?: string): string {
243
243
+
function renderHandleForm(
244
244
+
publicationUri: string,
245
245
+
styleHref: string,
246
246
+
error?: string,
247
247
+
): string {
209
248
const errorHtml = error
210
249
? `<p class="vocs_Paragraph error">${escapeHtml(error)}</p>`
211
250
: "";
212
251
213
213
-
return page(`
252
252
+
return page(
253
253
+
`
214
254
<h1 class="vocs_H1 vocs_Heading">Subscribe on Bluesky</h1>
215
255
<p class="vocs_Paragraph">Enter your Bluesky handle to subscribe to this publication.</p>
216
256
${errorHtml}
···
226
266
/>
227
267
<button type="submit" class="vocs_Button_button vocs_Button_button_accent">Continue on Bluesky</button>
228
268
</form>
229
229
-
`, styleHref);
269
269
+
`,
270
270
+
styleHref,
271
271
+
);
230
272
}
231
273
232
274
function renderSuccess(
···
240
282
: "You've successfully subscribed!";
241
283
const escapedPublicationUri = escapeHtml(publicationUri);
242
284
const escapedRecordUri = escapeHtml(recordUri);
243
243
-
return page(`
285
285
+
return page(
286
286
+
`
244
287
<h1 class="vocs_H1 vocs_Heading">Subscribed ✓</h1>
245
288
<p class="vocs_Paragraph">${msg}</p>
246
289
<p class="vocs_Paragraph"><small>Publication: <code class="vocs_Code"><a href="https://pds.ls/${escapedPublicationUri}">${escapedPublicationUri}</a></code></small></p>
247
290
<p class="vocs_Paragraph"><small>Record: <code class="vocs_Code"><a href="https://pds.ls/${escapedRecordUri}">${escapedRecordUri}</a></code></small></p>
248
248
-
`, styleHref);
291
291
+
`,
292
292
+
styleHref,
293
293
+
);
249
294
}
250
295
251
296
function renderError(message: string, styleHref: string): string {
252
252
-
return page(`<h1 class="vocs_H1 vocs_Heading">Error</h1><p class="vocs_Paragraph error">${escapeHtml(message)}</p>`, styleHref);
297
297
+
return page(
298
298
+
`<h1 class="vocs_H1 vocs_Heading">Error</h1><p class="vocs_Paragraph error">${escapeHtml(message)}</p>`,
299
299
+
styleHref,
300
300
+
);
253
301
}
254
302
255
303
function page(body: string, styleHref: string): string {