From 355f2126a3cada0b5733c9e2826e78fbc43869cf Mon Sep 17 00:00:00 2001 From: Takumi Akimoto Date: Fri, 1 May 2026 21:41:02 +0900 Subject: [PATCH] Add settings form --- islands/SettingsForm.tsx | 55 +++++++++++++++++++++++++++++++++++++++ repository/user.ts | 20 ++++++++++++++ routes/api/_middleware.ts | 9 +++++++ routes/api/settings.ts | 28 ++++++++++++++++++++ routes/index.tsx | 37 +++++++++++++++++++++----- 5 files changed, 143 insertions(+), 6 deletions(-) create mode 100644 islands/SettingsForm.tsx create mode 100644 routes/api/_middleware.ts create mode 100644 routes/api/settings.ts diff --git a/islands/SettingsForm.tsx b/islands/SettingsForm.tsx new file mode 100644 index 0000000..194bd5e --- /dev/null +++ b/islands/SettingsForm.tsx @@ -0,0 +1,55 @@ +import { useSignal } from "preact/signals" + +interface Props { + did: string + targetChannelId: string +} + +export default function SettingsForm(props: Props) { + const did = useSignal(props.did) + const targetChannelId = useSignal(props.targetChannelId) + const message = useSignal("") + + const handleSubmit = async (e: Event) => { + e.preventDefault() + message.value = "" + + const res = await fetch("/api/settings", { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + did: did.value, + targetChannelId: targetChannelId.value, + }), + }) + + message.value = res.ok ? "Saved" : "Failed to save" + } + + return ( +
+
+ +
+
+ +
+ + {message.value &&

{message.value}

} +
+ ) +} diff --git a/repository/user.ts b/repository/user.ts index d9d1500..38c1050 100644 --- a/repository/user.ts +++ b/repository/user.ts @@ -24,6 +24,26 @@ export const getUserSettingByDid = async ( return result } +export const getUserSettingByUserId = async (userId: string) => { + return await db + .selectFrom("userSettings") + .select(["did", "targetChannelId"]) + .where("userId", "=", userId) + .executeTakeFirst() +} + +export const saveUserSettings = async ( + userId: string, + did: string, + targetChannelId: string, +) => { + await db + .insertInto("userSettings") + .values({ userId, did, targetChannelId }) + .onDuplicateKeyUpdate({ did, targetChannelId }) + .execute() +} + export const getUserAccessToken = async (userId: string) => { const result = await db .selectFrom("userTokens") diff --git a/routes/api/_middleware.ts b/routes/api/_middleware.ts new file mode 100644 index 0000000..fe593c1 --- /dev/null +++ b/routes/api/_middleware.ts @@ -0,0 +1,9 @@ +import { define } from "../../lib/define.ts" + +export const handler = define.middleware((ctx) => { + if (!ctx.state.userId) { + return new Response(null, { status: 401 }) + } + + return ctx.next() +}) diff --git a/routes/api/settings.ts b/routes/api/settings.ts new file mode 100644 index 0000000..e925770 --- /dev/null +++ b/routes/api/settings.ts @@ -0,0 +1,28 @@ +import { define } from "../../lib/define.ts" +import { + getUserSettingByUserId, + saveUserSettings, +} from "../../repository/user.ts" + +export const handler = define.handlers({ + GET: async (ctx) => { + const setting = await getUserSettingByUserId(ctx.state.userId!) + + return Response.json(setting ?? {}) + }, + + PUT: async (ctx) => { + const { did, targetChannelId } = await ctx.req.json() + + if (typeof did !== "string" || typeof targetChannelId !== "string") { + return Response.json( + { error: "did and targetChannelId are required" }, + { status: 400 }, + ) + } + + await saveUserSettings(ctx.state.userId!, did, targetChannelId) + + return Response.json({ ok: true }) + }, +}) diff --git a/routes/index.tsx b/routes/index.tsx index c6ffec2..71d6bea 100644 --- a/routes/index.tsx +++ b/routes/index.tsx @@ -1,21 +1,46 @@ import { page } from "fresh" +import SettingsForm from "../islands/SettingsForm.tsx" import { define } from "../lib/define.ts" import { oauth2Client } from "../lib/oauth2.ts" +import { getUserSettingByUserId } from "../repository/user.ts" export const handler = define.handlers({ GET: async (ctx) => { + if (ctx.state.userId) { + const setting = await getUserSettingByUserId(ctx.state.userId) + return page({ + loggedIn: true as const, + did: setting?.did ?? "", + targetChannelId: setting?.targetChannelId ?? "", + }) + } + const url = new URL(ctx.req.url) const redirectUri = `${url.origin}/callback` const authorizeUrl = await oauth2Client.authorizationCode.getAuthorizeUri({ redirectUri, }) - return page({ authorizeUrl }) + return page({ loggedIn: false as const, authorizeUrl }) }, }) -export default define.page((ctx) => ( -
- Login with traQ -
-)) +export default define.page((ctx) => { + if (!ctx.data.loggedIn) { + return ( +
+ Login with traQ +
+ ) + } + + return ( +
+

Settings

+ +
+ ) +}) -- 2.51.2