+44
-16
apps/web/index.ts
+44
-16
apps/web/index.ts
···
1
import { serve, type HttpBindings } from "@hono/node-server";
2
import { logger } from "@tealfmbot/common/logger.js";
3
import { Hono } from "hono";
4
import pinoHttpLogger from "pino-http";
5
6
import { client } from "./client";
7
import { createSession, MAX_AGE } from "./utils";
8
-
import { getSignedCookie } from "hono/cookie";
9
-
import { COOKIE_SECRET } from "@tealfmbot/common/constants.js";
10
11
type Variables = {
12
logger: typeof logger;
···
27
});
28
29
app.get("/", (c) => {
30
-
c.var.logger.info("test log");
31
return c.text("yo!!");
32
});
33
34
app.get("/oauth-client-metadata.json", (c) => {
35
c.header("Cache-Control", `max-age=${MAX_AGE}, public`);
36
return c.json(client.clientMetadata);
···
42
});
43
44
app.get("/oauth/callback", async (c) => {
45
-
c.header("Cache-Control", "no-store")
46
-
const params = new URLSearchParams(c.req.url.split("?")[1])
47
48
try {
49
-
const session = await getSignedCookie(c, COOKIE_SECRET, "__teal_fm_bot_session")
50
if (session) {
51
try {
52
-
const oauthSession = await client.restore(session)
53
-
if (oauthSession) oauthSession.signOut()
54
-
} catch {
55
-
logger.warn("oauth restore failed")
56
}
57
}
58
-
const oauth = await client.callback(params)
59
await createSession(oauth.session.did);
60
-
61
-
} catch {
62
-
logger.error("oauth callback failed")
63
}
64
65
-
return c.redirect("/")
66
-
})
67
68
const server = serve({
69
fetch: app.fetch,
···
1
+
import { isValidHandle } from "@atproto/syntax";
2
import { serve, type HttpBindings } from "@hono/node-server";
3
+
import { COOKIE_SECRET } from "@tealfmbot/common/constants.js";
4
import { logger } from "@tealfmbot/common/logger.js";
5
import { Hono } from "hono";
6
+
import { getSignedCookie } from "hono/cookie";
7
+
import { validator } from "hono/validator";
8
import pinoHttpLogger from "pino-http";
9
10
import { client } from "./client";
11
import { createSession, MAX_AGE } from "./utils";
12
13
type Variables = {
14
logger: typeof logger;
···
29
});
30
31
app.get("/", (c) => {
32
return c.text("yo!!");
33
});
34
35
+
app.get("/health", (c) => {
36
+
return c.json({ message: "OK" }, 200);
37
+
});
38
+
39
app.get("/oauth-client-metadata.json", (c) => {
40
c.header("Cache-Control", `max-age=${MAX_AGE}, public`);
41
return c.json(client.clientMetadata);
···
47
});
48
49
app.get("/oauth/callback", async (c) => {
50
+
c.header("Cache-Control", "no-store");
51
+
const params = new URLSearchParams(c.req.url.split("?")[1]);
52
53
try {
54
+
const session = await getSignedCookie(c, COOKIE_SECRET, "__teal_fm_bot_session");
55
if (session) {
56
try {
57
+
const oauthSession = await client.restore(session);
58
+
if (oauthSession) oauthSession.signOut();
59
+
} catch (error) {
60
+
logger.warn({ error }, "oauth restore failed");
61
}
62
}
63
+
const oauth = await client.callback(params);
64
await createSession(oauth.session.did);
65
+
} catch (error) {
66
+
logger.error({ error }, "oauth callback failed");
67
}
68
69
+
return c.redirect("/");
70
+
});
71
+
72
+
app.post(
73
+
"/login",
74
+
validator("form", (value, c) => {
75
+
const identifier = value["identifier"];
76
+
if (!identifier || typeof identifier !== "string" || !isValidHandle(identifier)) {
77
+
return c.json({ message: "Invalid handle, did or PDS URL" }, 400);
78
+
}
79
+
80
+
return {
81
+
identifier,
82
+
};
83
+
}),
84
+
(c) => {
85
+
c.header("Cache-Control", "no-store");
86
+
const { identifier } = c.req.valid("form");
87
+
try {
88
+
return c.text(identifier);
89
+
} catch (error) {
90
+
logger.error({ error }, "oauth authorize failed");
91
+
return c.json({ message: "oauth authorize failed" }, 500);
92
+
}
93
+
},
94
+
);
95
96
const server = serve({
97
fetch: app.fetch,
+1
apps/web/package.json
+1
apps/web/package.json
+12
-10
apps/web/utils.ts
+12
-10
apps/web/utils.ts
···
1
-
import { COOKIE_SECRET } from "@tealfmbot/common/constants.ts";
2
import type { Context } from "hono";
3
-
import { deleteCookie, generateSignedCookie, getSignedCookie } from "hono/cookie";
4
-
import { client } from "./client";
5
import { Agent } from "@atproto/api";
6
import { logger } from "@tealfmbot/common/logger.js";
7
8
export const MAX_AGE = process.env.NODE_ENV === "production" ? 60 : 0;
9
···
19
}
20
21
export async function getSessionAgent(c: Context) {
22
-
c.header("Vary", "Cookie")
23
-
const session = await getSignedCookie(c, COOKIE_SECRET, "__teal_fm_bot_session")
24
if (!session) return null;
25
-
c.header("Cache-Control", `max-age=${MAX_AGE}, private`)
26
27
try {
28
-
const oauthSession = await client.restore(session)
29
-
return oauthSession ? new Agent(oauthSession) : null
30
} catch {
31
-
logger.warn("oauth restore failed")
32
-
deleteCookie(c, "__teal_fm_bot_session")
33
return null;
34
}
35
}
···
1
import type { Context } from "hono";
2
+
3
import { Agent } from "@atproto/api";
4
+
import { COOKIE_SECRET } from "@tealfmbot/common/constants.ts";
5
import { logger } from "@tealfmbot/common/logger.js";
6
+
import { deleteCookie, generateSignedCookie, getSignedCookie } from "hono/cookie";
7
+
8
+
import { client } from "./client";
9
10
export const MAX_AGE = process.env.NODE_ENV === "production" ? 60 : 0;
11
···
21
}
22
23
export async function getSessionAgent(c: Context) {
24
+
c.header("Vary", "Cookie");
25
+
const session = await getSignedCookie(c, COOKIE_SECRET, "__teal_fm_bot_session");
26
if (!session) return null;
27
+
c.header("Cache-Control", `max-age=${MAX_AGE}, private`);
28
29
try {
30
+
const oauthSession = await client.restore(session);
31
+
return oauthSession ? new Agent(oauthSession) : null;
32
} catch {
33
+
logger.warn("oauth restore failed");
34
+
deleteCookie(c, "__teal_fm_bot_session");
35
return null;
36
}
37
}
+3
pnpm-lock.yaml
+3
pnpm-lock.yaml