diff --git a/README.md b/README.md index 39244f2..77c204a 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ Free IDaas And Single Sign-On Service - `/api/users` GET - `/api/users/:id` GET/PUT/POST - `/api/users/:id/forbidden` PUT/POST +- `/api/users/:id/:provider` DELETE ## 赞助 Sponsor diff --git a/apps/sso/src/routes/api/users.ts b/apps/sso/src/routes/api/users.ts index f013468..d0aff55 100644 --- a/apps/sso/src/routes/api/users.ts +++ b/apps/sso/src/routes/api/users.ts @@ -3,6 +3,7 @@ import { zBodyValidator } from '@hono-dev/zod-body-validator'; import { Hono } from 'hono'; // import { cache } from 'hono/cache'; import { z } from 'zod'; +import { AvailableProviders } from '../../config'; import { guard } from '../../middleware/guard'; import { PaginationQuerySchema } from '../../types'; @@ -55,4 +56,50 @@ router.on( } ); +router.on( + ['POST', 'PUT'], + '/users/:id', + guard(), + zBodyValidator( + z.object({ + username: z.string(), + display_name: z.string(), + avatar: z.string() + }) + ), + async (c) => { + const s = c.get('services'); + const viewer = c.get('viewer'); + const id = c.req.param('id'); + if (viewer.type !== 'admin' && viewer.id !== id) { + return c.json({ error: 'forbidden' }, 403); + } + const body = c.req.valid('form'); + const result = await s.user.updateUser(id, body); + return c.json({ result }); + } +); + +router.delete( + '/users/:id/:provider', + guard(), + zValidator( + 'param', + z.object({ + id: z.string(), + provider: z.enum(AvailableProviders) + }) + ), + async (c) => { + const s = c.get('services'); + const { id, provider } = c.req.valid('param'); + const viewer = c.get('viewer'); + if (viewer.type !== 'admin' && viewer.id !== id) { + return c.json({ error: 'forbidden' }, 403); + } + const result = await s.user.unbindThirdUser(id, provider); + return c.json({ result }); + } +); + export { router }; diff --git a/apps/sso/src/routes/auth/afdian.ts b/apps/sso/src/routes/auth/afdian.ts index ddbc64a..ba563cd 100644 --- a/apps/sso/src/routes/auth/afdian.ts +++ b/apps/sso/src/routes/auth/afdian.ts @@ -26,7 +26,7 @@ router.get('/afdian/*', async (c) => { // Bind thid login method to the user if (!bindedUid) { await s.user.bindThirdUser(viewer.id, 'afdian', formattedUser); - return c.redirect('/dashboard'); + return callbackOrBindRedirect(c, '/dashboard'); } } // Register the user diff --git a/apps/sso/src/routes/auth/alipay.ts b/apps/sso/src/routes/auth/alipay.ts index 8ca39b1..d199594 100644 --- a/apps/sso/src/routes/auth/alipay.ts +++ b/apps/sso/src/routes/auth/alipay.ts @@ -27,7 +27,7 @@ router.get('/alipay/*', async (c) => { // Bind thid login method to the user if (!bindedUid) { await s.user.bindThirdUser(viewer.id, 'alipay', formattedUser); - return c.redirect('/dashboard'); + return callbackOrBindRedirect(c, '/dashboard'); } } // Register the user diff --git a/apps/sso/src/routes/auth/github.ts b/apps/sso/src/routes/auth/github.ts index a3f7b6d..5c0f9a9 100644 --- a/apps/sso/src/routes/auth/github.ts +++ b/apps/sso/src/routes/auth/github.ts @@ -27,7 +27,7 @@ router.get('/github/*', async (c) => { // Bind thid login method to the user if (!bindedUid) { await s.user.bindThirdUser(viewer.id, 'github', formattedUser); - return c.redirect('/dashboard'); + return callbackOrBindRedirect(c, '/dashboard'); } } // Register the user diff --git a/apps/website/src/app.d.ts b/apps/website/src/app.d.ts index 1f0e2b2..95c6f82 100644 --- a/apps/website/src/app.d.ts +++ b/apps/website/src/app.d.ts @@ -8,7 +8,9 @@ import type { App as Apps, User, ThirdUser } from '$lib/types'; declare global { namespace App { // interface Error {} - // interface Locals {} + interface Locals { + user: User & { thirdparty: ThirdUser[] }; + } interface PageData { user: User & { thirdparty: ThirdUser[] }; apps: Apps[]; diff --git a/apps/website/src/routes/[[lang=locale]]/dashboard/me/+page.server.ts b/apps/website/src/routes/[[lang=locale]]/dashboard/me/+page.server.ts new file mode 100644 index 0000000..ce7a885 --- /dev/null +++ b/apps/website/src/routes/[[lang=locale]]/dashboard/me/+page.server.ts @@ -0,0 +1,31 @@ +import type { Actions } from './$types'; + +export const actions: Actions = { + save: async ({ request, fetch }) => { + const form = await request.formData(); + const body = Object.fromEntries(form.entries()); + const { id, ...rest } = body; + const res = await fetch(`/api/users/${id}`, { + method: 'PUT', + body: JSON.stringify(rest), + headers: { + 'content-type': 'application/json' + } + }); + const result = await res.json(); + return result; + }, + unbind: async ({ request, fetch }) => { + const form = await request.formData(); + const body = Object.fromEntries(form.entries()); + const res = await fetch(`/api/users/${body.id}/${body.provider}`, { + method: 'DELETE', + body: JSON.stringify({}), + headers: { + 'content-type': 'application/json' + } + }); + const result = await res.json(); + return result; + } +}; diff --git a/apps/website/src/routes/[[lang=locale]]/dashboard/me/+page.svelte b/apps/website/src/routes/[[lang=locale]]/dashboard/me/+page.svelte index e6fe6c2..d4470f0 100644 --- a/apps/website/src/routes/[[lang=locale]]/dashboard/me/+page.svelte +++ b/apps/website/src/routes/[[lang=locale]]/dashboard/me/+page.svelte @@ -30,9 +30,17 @@ loading = false; }; } + + function confirmOperation(e: Event) { + if (!confirm($t('common.confirm'))) { + e.preventDefault(); + return false; + } + }
+