diff --git a/apps/backend/src/routes/auth.ts b/apps/backend/src/routes/auth.ts index 108e70d..1d75439 100644 --- a/apps/backend/src/routes/auth.ts +++ b/apps/backend/src/routes/auth.ts @@ -99,6 +99,27 @@ export default new Elysia({ prefix: "/auth" }) } const userData = await userResponse.json(); + + // Guild membership check + const guildsResponse = await fetch("https://discord.com/api/users/@me/guilds", { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }); + + if (!guildsResponse.ok) { + console.error("Failed to fetch user guilds", await guildsResponse.text()); + return status(500, "Failed to fetch user guilds"); + } + + const guilds = await guildsResponse.json(); + const targetGuildId = "1274213821459857528"; + const isMember = guilds.some((guild: any) => guild.id === targetGuildId); + + if (!isMember) { + return status(403, "You must be a member of the required guild to join."); + } + const isHardcodedOwner = hardcodedOwners.has(String(userData.id)); const updateSet: { username: string; avatar: string; role?: "admin" } = { username: userData.username, diff --git a/apps/backend/src/routes/post.ts b/apps/backend/src/routes/post.ts index 6a13617..6bbb086 100644 --- a/apps/backend/src/routes/post.ts +++ b/apps/backend/src/routes/post.ts @@ -30,6 +30,36 @@ type ExistsResponse = { documentId: string | null; }; +async function sendDiscordNotification(payload: { + title: string; + url: string; + author: string; + tags: string[]; + imageUrl?: string; +}) { + const webhookUrl = config.discord.webhook; + if (!webhookUrl) return; + + try { + await fetch(webhookUrl, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + embeds: [{ + title: payload.title, + url: payload.url, + author: { name: payload.author }, + fields: [{ name: "Tags", value: payload.tags.join(", ") || "None" }], + image: payload.imageUrl ? { url: payload.imageUrl } : undefined, + color: 0x00ff00, + }], + }), + }); + } catch (error) { + console.error("[Discord Webhook Error]", error); + } +} + function uploadOk(message: string, extra?: Omit): UploadResponse { return { success: true, @@ -230,7 +260,22 @@ export default new Elysia({ prefix: "/post" }) }), }) - .get("/detail/:id", async ({ params, status }) => { + .get("/detail/:id", async ({ params, status, jwt, cookie: { mizuki } }) => { + const rawToken = mizuki.value; + if (typeof rawToken !== "string" || rawToken.length === 0) { + return status(401, "Unauthorized"); + } + + const payload = await jwt.verify(rawToken); + if (!payload || typeof payload !== "object" || !("id" in payload) || typeof payload.id !== "string") { + return status(401, "Unauthorized"); + } + + const user = await User.findOne({ userId: payload.id }); + if (!user) { + return status(401, "Unauthorized"); + } + const post = await MediaUpload.findById(params.id); if (!post) { return status(404, "포스트를 찾을 수 없습니다.");