@podosoft/podokit 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/add.d.ts +2 -0
- package/dist/add.js +5 -1
- package/dist/assemble.d.ts +14 -0
- package/dist/assemble.js +60 -0
- package/dist/create.d.ts +2 -0
- package/dist/create.js +7 -0
- package/dist/eject.d.ts +11 -0
- package/dist/eject.js +27 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +122 -1
- package/dist/inspect.d.ts +43 -0
- package/dist/inspect.js +82 -0
- package/dist/lockfile.d.ts +80 -0
- package/dist/lockfile.js +193 -0
- package/dist/templates/base/dot-env.example +11 -6
- package/dist/templates/fullstack-nest-svelte/README.md +2 -2
- package/dist/templates/fullstack-nest-svelte/apps/api/package.json +1 -0
- package/dist/templates/fullstack-nest-svelte/apps/api/src/app.module.ts +6 -3
- package/dist/templates/fullstack-nest-svelte/apps/api/src/common/app-exception.ts +4 -12
- package/dist/templates/fullstack-nest-svelte/apps/api/src/config/env.validation.ts +1 -1
- package/dist/templates/fullstack-nest-svelte/apps/api/src/main.ts +1 -1
- package/dist/templates/fullstack-nest-svelte/apps/web/package.json +2 -2
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-content.svelte +1 -1
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/select/select-item.svelte +1 -1
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/switch/index.ts +7 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/switch/switch.svelte +29 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/server/backend-proxy.ts +1 -1
- package/dist/templates/fullstack-nest-svelte/dot-env.example +10 -5
- package/dist/templates/fullstack-nest-svelte/infra/docker/docker-compose.yml +37 -0
- package/dist/templates/fullstack-nest-svelte/infra/docker/sms-sink.mjs +40 -0
- package/dist/templates/fullstack-nest-svelte/tests/README.md +1 -1
- package/dist/templates/fullstack-nest-svelte/tests/package.json +2 -1
- package/dist/templates/fullstack-nest-svelte/tests/playwright.config.ts +1 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/app.d.ts +3 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/app-sidebar.svelte +7 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/data-table.svelte +149 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/table-pagination.svelte +21 -22
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/table-toolbar.svelte +93 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/messages.ts +224 -8
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/server/guards.ts +22 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/+layout.server.ts +19 -3
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/accept-invitation/[id]/+page.svelte +36 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/+page.svelte +21 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/account/+page.svelte +360 -49
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/audit/+page.server.ts +2 -4
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/audit/+page.svelte +82 -37
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/organizations/+page.server.ts +6 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/organizations/+page.svelte +264 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/sessions/+page.server.ts +3 -4
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/sessions/+page.svelte +79 -45
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/settings/+page.server.ts +26 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/settings/+page.svelte +322 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/users/+page.server.ts +3 -5
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/users/+page.svelte +254 -108
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/forgot-password/+page.svelte +8 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/login/+page.server.ts +14 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/login/+page.svelte +127 -5
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/signup/+page.svelte +18 -2
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/verify-email/+page.svelte +63 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/oauth2/consent/+page.svelte +54 -0
- package/dist/templates/modules/admin-dashboard/files/tests/api/audit.api.spec.ts +51 -2
- package/dist/templates/modules/admin-dashboard/files/tests/api/auth-config.api.spec.ts +80 -0
- package/dist/templates/modules/admin-dashboard/files/tests/api/auth.api.spec.ts +264 -1
- package/dist/templates/modules/admin-dashboard/files/tests/api/email.api.spec.ts +120 -0
- package/dist/templates/modules/admin-dashboard/files/tests/api/users-admin.api.spec.ts +10 -10
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/accounts.ts +2 -2
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/mailpit.ts +49 -0
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/sms.ts +35 -0
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/totp.ts +9 -0
- package/dist/templates/modules/admin-dashboard/files/tests/playwright.config.ts +1 -1
- package/dist/templates/modules/admin-dashboard/files/tests/seed.setup.ts +12 -1
- package/dist/templates/modules/admin-dashboard/files/tests/ui/account.ui.spec.ts +77 -6
- package/dist/templates/modules/admin-dashboard/files/tests/ui/dashboard.ui.spec.ts +1 -1
- package/dist/templates/modules/admin-dashboard/files/tests/ui/data-table.ui.spec.ts +32 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/email-admin.ui.spec.ts +60 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/email.ui.spec.ts +42 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/guards.ui.spec.ts +1 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/oidc.ui.spec.ts +57 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/organizations.ui.spec.ts +52 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/passkey.ui.spec.ts +36 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/sessions.ui.spec.ts +9 -2
- package/dist/templates/modules/admin-dashboard/files/tests/ui/settings.ui.spec.ts +34 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/table-toolbar.ui.spec.ts +32 -0
- package/dist/templates/modules/admin-dashboard/files/tests/ui/users-crud.ui.spec.ts +28 -12
- package/dist/templates/modules/admin-dashboard/files/tests/ui/users.ui.spec.ts +35 -3
- package/dist/templates/modules/admin-dashboard/module.manifest.json +13 -7
- package/dist/templates/modules/api-key-auth/module.manifest.json +3 -3
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit-enabled.ts +16 -0
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit-hook.ts +4 -0
- package/dist/templates/modules/audit-log/files/apps/api/src/audit/audit.interceptor.ts +2 -0
- package/dist/templates/modules/audit-log/module.manifest.json +15 -9
- package/dist/templates/modules/auth/files/apps/api/src/account/account.controller.ts +78 -13
- package/dist/templates/modules/auth/files/apps/api/src/account/account.module.ts +2 -1
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth-provider.ts +73 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/auth.ts +197 -52
- package/dist/templates/modules/auth/files/apps/api/src/auth/db.ts +12 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/feature-gate.ts +56 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/org-permissions.ts +22 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth/permissions.ts +23 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth-config/auth-config.controller.ts +33 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth-config/auth-config.entity.ts +26 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth-config/auth-config.module.ts +12 -0
- package/dist/templates/modules/auth/files/apps/api/src/auth-config/auth-config.service.ts +118 -0
- package/dist/templates/modules/auth/files/apps/api/src/mail/mailer.ts +65 -0
- package/dist/templates/modules/auth/files/apps/api/src/migrations/1720400000000-InitAppSettings.ts +38 -0
- package/dist/templates/modules/auth/files/apps/api/src/migrations/1720500000000-InitAuthConfig.ts +27 -0
- package/dist/templates/modules/auth/files/apps/api/src/settings/app-setting.entity.ts +16 -0
- package/dist/templates/modules/auth/files/apps/api/src/settings/flag-defaults.ts +25 -0
- package/dist/templates/modules/auth/files/apps/api/src/settings/settings.module.ts +11 -0
- package/dist/templates/modules/auth/files/apps/api/src/settings/settings.service.ts +57 -0
- package/dist/templates/modules/auth/files/apps/api/src/sms/sms.ts +23 -0
- package/dist/templates/modules/auth/module.manifest.json +54 -20
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/worker.module.ts +6 -3
- package/dist/templates/modules/bullmq/module.manifest.json +4 -4
- package/dist/templates/modules/file-upload/module.manifest.json +3 -3
- package/dist/templates/modules/job-progress/module.manifest.json +9 -9
- package/dist/templates/modules/logging/module.manifest.json +3 -3
- package/dist/templates/modules/object-storage-s3/files/infra/docker/minio.compose.yml +5 -3
- package/dist/templates/modules/object-storage-s3/module.manifest.json +5 -5
- package/dist/templates/modules/rate-limit/module.manifest.json +2 -2
- package/dist/templates/modules/redis/module.manifest.json +4 -4
- package/dist/templates/modules/sse/module.manifest.json +4 -4
- package/dist/templates/todo/README.md +2 -2
- package/dist/templates/todo/apps/api/package.json +1 -0
- package/dist/templates/todo/apps/api/src/app.module.ts +6 -3
- package/dist/templates/todo/apps/api/src/common/app-exception.ts +4 -12
- package/dist/templates/todo/apps/api/src/config/env.validation.ts +1 -1
- package/dist/templates/todo/apps/api/src/main.ts +1 -1
- package/dist/templates/todo/apps/web/package.json +2 -2
- package/dist/templates/todo/apps/web/src/lib/components/ui/switch/index.ts +7 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/switch/switch.svelte +29 -0
- package/dist/templates/todo/apps/web/src/lib/server/backend-proxy.ts +1 -1
- package/dist/templates/todo/dot-env.example +10 -5
- package/dist/templates/todo/infra/docker/docker-compose.yml +4 -0
- package/dist/templates/todo/tests/README.md +1 -1
- package/dist/templates/todo/tests/package.json +2 -1
- package/dist/templates/todo/tests/playwright.config.ts +1 -1
- package/dist/update.d.ts +49 -0
- package/dist/update.js +158 -0
- package/package.json +2 -2
|
@@ -12,15 +12,22 @@ const en = {
|
|
|
12
12
|
common: { appName: "Admin" },
|
|
13
13
|
landing: { title: "Welcome", subtitle: "This is your app's home page. Replace it with your own content — the admin area lives under /admin.", openAdmin: "Open admin" },
|
|
14
14
|
language: { label: "Language" },
|
|
15
|
+
toolbar: { filter: "Filter", search: "Search", searchButton: "Search", all: "All" },
|
|
15
16
|
theme: { toggle: "Toggle theme", light: "Light", dark: "Dark", system: "System" },
|
|
16
17
|
auth: {
|
|
17
18
|
signInTitle: "Sign in",
|
|
18
19
|
signInDesc: "Enter your credentials to continue.",
|
|
19
20
|
email: "Email",
|
|
21
|
+
emailOrUsername: "Email or username",
|
|
20
22
|
password: "Password",
|
|
21
23
|
forgot: "Forgot?",
|
|
22
24
|
signIn: "Sign in",
|
|
23
25
|
signingIn: "Signing in…",
|
|
26
|
+
magicLinkButton: "Email me a sign-in link",
|
|
27
|
+
passkeyButton: "Use a passkey",
|
|
28
|
+
magicLinkSent: "Check your email for a sign-in link.",
|
|
29
|
+
magicLinkNeedsEmail: "Enter your email first.",
|
|
30
|
+
otpButton: "Email me a code", otpCode: "One-time code", otpVerify: "Verify code",
|
|
24
31
|
noAccount: "No account?",
|
|
25
32
|
signUp: "Sign up",
|
|
26
33
|
signInFailed: "Sign in failed",
|
|
@@ -44,10 +51,86 @@ const en = {
|
|
|
44
51
|
updating: "Updating…",
|
|
45
52
|
missingToken: "Missing or invalid reset token.",
|
|
46
53
|
resetFailed: "Reset failed",
|
|
54
|
+
verifyTitle: "Verify your email",
|
|
55
|
+
verifyDesc: "We sent a verification link to {email}. Open it to activate your account.",
|
|
56
|
+
verifyDescNoEmail: "Enter your email to receive a new verification link.",
|
|
57
|
+
verifySent: "Verification email sent — check your inbox.",
|
|
58
|
+
verifyLinkError: "That verification link is invalid or has expired. Request a new one below.",
|
|
59
|
+
resendVerification: "Resend verification email",
|
|
60
|
+
emailNotVerified: "Verify your email before signing in.",
|
|
47
61
|
},
|
|
48
|
-
nav: { overview: "Overview", users: "Users", sessions: "Sessions", audit: "Audit log", account: "Account", signOut: "Sign out" },
|
|
49
|
-
|
|
50
|
-
|
|
62
|
+
nav: { overview: "Overview", users: "Users", sessions: "Sessions", organizations: "Organizations", audit: "Audit log", account: "Account", settings: "Settings", signOut: "Sign out" },
|
|
63
|
+
passkeys: {
|
|
64
|
+
title: "Passkeys", subtitle: "Passwordless sign-in with your device (Face ID, fingerprint, security key).",
|
|
65
|
+
name: "Name", namePlaceholder: "e.g. MacBook", add: "Add passkey", remove: "Remove", untitled: "Passkey",
|
|
66
|
+
added: "Passkey added", addFailed: "Failed to add passkey", removed: "Passkey removed", removeFailed: "Failed to remove passkey",
|
|
67
|
+
empty: "No passkeys yet.", created: "Created", total: "{count} passkeys",
|
|
68
|
+
},
|
|
69
|
+
acceptInvitation: {
|
|
70
|
+
title: "Organization invitation", pending: "Accepting your invitation…",
|
|
71
|
+
ok: "You have joined the organization.", error: "This invitation is invalid or has expired.",
|
|
72
|
+
goToOrgs: "Go to organizations",
|
|
73
|
+
},
|
|
74
|
+
organizations: {
|
|
75
|
+
title: "Organizations", subtitle: "Multi-tenant teams. Create organizations and invite members.",
|
|
76
|
+
create: "New organization", createTitle: "Create organization", name: "Name", slug: "Slug",
|
|
77
|
+
created: "Created", delete: "Delete", cancel: "Cancel", empty: "No organizations yet.", total: "{count} organizations",
|
|
78
|
+
submit: "Create", createdMsg: "Organization created", deletedMsg: "Organization deleted", actionFailed: "Action failed",
|
|
79
|
+
manage: "Manage", membersDesc: "Members and pending invitations.", members: "Members", pending: "Pending invitations",
|
|
80
|
+
parent: "Parent organization", noParent: "None (top level)", managers: "Managers", managersHint: "Existing users to add as managers (any number).",
|
|
81
|
+
inviteEmail: "Invite by email", invite: "Invite", invited: "Invitation sent", remove: "Remove", cancelInvite: "Cancel",
|
|
82
|
+
},
|
|
83
|
+
consent: {
|
|
84
|
+
title: "Authorize access", subtitle: "An application is requesting access to your account.",
|
|
85
|
+
requests: "It will be able to access:", allow: "Allow", deny: "Deny",
|
|
86
|
+
},
|
|
87
|
+
apiKeys: {
|
|
88
|
+
title: "API keys", subtitle: "Personal keys for programmatic access. Send as an x-api-key header.",
|
|
89
|
+
name: "Name", namePlaceholder: "e.g. CI pipeline", create: "Create key",
|
|
90
|
+
keyColumn: "Key", created: "Created", untitled: "Untitled", revoke: "Revoke",
|
|
91
|
+
revoked: "Key revoked", revokeFailed: "Failed to revoke key", empty: "No API keys yet.", total: "{count} API keys",
|
|
92
|
+
createdTitle: "API key created", createdDesc: "Copy it now — you will not be able to see it again.",
|
|
93
|
+
copy: "Copy", copied: "Copied to clipboard", done: "Done",
|
|
94
|
+
},
|
|
95
|
+
settings: {
|
|
96
|
+
title: "Settings", subtitle: "Which authentication features are enabled, and how they are configured.",
|
|
97
|
+
authFeaturesTitle: "Authentication features", authFeaturesSubtitle: "Toggle sign-in methods and security features. Changes apply immediately.",
|
|
98
|
+
serverConfigTitle: "Server configuration", serverConfigSubtitle: "Set through environment variables and applied at startup (read-only).",
|
|
99
|
+
enabled: "Enabled", disabled: "Disabled", configuredVia: "Configured via {env}", manage: "Manage",
|
|
100
|
+
saved: "Setting updated", saveFailed: "Failed to update setting",
|
|
101
|
+
emailPassword: "Email & password", emailPasswordDesc: "Sign in with an email address and password.",
|
|
102
|
+
emailVerification: "Email verification", emailVerificationDesc: "Require new sign-ups to confirm their address.",
|
|
103
|
+
magicLink: "Magic link", magicLinkDesc: "Passwordless sign-in via an emailed link.",
|
|
104
|
+
emailOtp: "Email one-time code", emailOtpDesc: "Passwordless sign-in with an emailed code.",
|
|
105
|
+
username: "Username sign-in", usernameDesc: "Let users sign in with a username instead of an email.",
|
|
106
|
+
multiSession: "Multi-account sessions", multiSessionDesc: "Let one browser stay signed into several accounts and switch between them.",
|
|
107
|
+
phoneNumber: "Phone number", phoneNumberDesc: "Register and verify a phone number via SMS (configure a provider for real delivery).",
|
|
108
|
+
apiKey: "API keys", apiKeyDesc: "Let users create personal API keys for programmatic access.",
|
|
109
|
+
passkey: "Passkeys", passkeyDesc: "Let users register passkeys for passwordless sign-in.",
|
|
110
|
+
organization: "Organizations", organizationDesc: "Multi-tenant organizations with members and email invitations.",
|
|
111
|
+
oidcProvider: "OIDC provider", oidcProviderDesc: "Act as an identity provider so other apps can sign in with this one.",
|
|
112
|
+
twoFactor: "Two-factor authentication", twoFactorDesc: "TOTP second factor with backup codes.",
|
|
113
|
+
socialProviders: "Social providers", socialProvidersDesc: "OAuth sign-in (Google, GitHub).",
|
|
114
|
+
breachCheck: "Breached-password check", breachCheckDesc: "Reject passwords found in known breaches (Have I Been Pwned).",
|
|
115
|
+
accountDeletion: "Account deletion", accountDeletionDesc: "Let users delete their own account.",
|
|
116
|
+
auditLog: "Audit log", auditLogDesc: "Record security-relevant actions.",
|
|
117
|
+
signInTitle: "Sign-in methods", signInSubtitle: "How users authenticate. Toggle methods on/off; social providers open a popup for credentials.",
|
|
118
|
+
emailTitle: "Email", emailSubtitle: "Outgoing mail (SMTP) and the email-verification policy.",
|
|
119
|
+
securityTitle: "Security & policy", securitySubtitle: "Second factor, breached-password rejection, account deletion, and audit logging.",
|
|
120
|
+
advancedTitle: "Advanced", advancedSubtitle: "API keys, organizations, and acting as an OIDC identity provider.",
|
|
121
|
+
socialTitle: "Social login", socialSubtitle: "Paste OAuth credentials to enable sign-in with a provider. Applied immediately — no restart. Secrets are stored encrypted.",
|
|
122
|
+
smtpTitle: "SMTP (email delivery)", smtpSubtitle: "Outgoing mail for sign-in links, verification, and invitations. Applied immediately.",
|
|
123
|
+
google: "Google", github: "GitHub",
|
|
124
|
+
clientId: "Client ID", clientSecret: "Client secret", redirectUri: "Redirect URI", secretSet: "•••••••• (saved — leave blank to keep)",
|
|
125
|
+
save: "Save", cancel: "Cancel", configure: "Configure", notConfigured: "Not configured", configuredOff: "Configured (off)",
|
|
126
|
+
socialDialogDesc: "Paste the OAuth client ID and secret from the provider's console, then enable it. Applied immediately.",
|
|
127
|
+
smtpEnabled: "Enabled", smtpHost: "Host", smtpPort: "Port", smtpUser: "Username", smtpPass: "Password", smtpFrom: "From address", smtpSecure: "TLS (secure)",
|
|
128
|
+
addProvider: "Add provider", provider: "Provider", remove: "Remove", smtpConfigure: "SMTP settings",
|
|
129
|
+
edit: "Edit", close: "Close", back: "Back", noProviders: "No providers added yet.",
|
|
130
|
+
socialCardDesc: "Sign in with Google, GitHub, Apple and other OAuth providers.",
|
|
131
|
+
},
|
|
132
|
+
dashboard: { title: "Dashboard", welcome: "Welcome back, {name}.", yourRole: "Your role", signedInAs: "Signed in as", unverifiedUsers: "Unverified users" },
|
|
133
|
+
audit: { title: "Audit log", when: "When", actor: "Actor", action: "Action", target: "Target", ip: "IP", empty: "No audit entries.", loadFailed: "Failed to load audit log", total: "{count} entries", searchField: "Actor or target" },
|
|
51
134
|
users: {
|
|
52
135
|
title: "Users",
|
|
53
136
|
search: "Search by email…",
|
|
@@ -75,6 +158,10 @@ const en = {
|
|
|
75
158
|
password: "Password",
|
|
76
159
|
create: "Create",
|
|
77
160
|
cancel: "Cancel",
|
|
161
|
+
close: "Close",
|
|
162
|
+
roleAdmin: "Admin",
|
|
163
|
+
roleUser: "User",
|
|
164
|
+
roleModerator: "Moderator",
|
|
78
165
|
userCreated: "User created",
|
|
79
166
|
setPassword: "Set password",
|
|
80
167
|
newPassword: "New password",
|
|
@@ -94,10 +181,22 @@ const en = {
|
|
|
94
181
|
banTitle: "Ban user",
|
|
95
182
|
banReason: "Reason (optional)",
|
|
96
183
|
banExpiryDays: "Expires in (days, optional)",
|
|
184
|
+
banExpiry: "Ban expires",
|
|
185
|
+
banPermanent: "Permanent ban",
|
|
186
|
+
joined: "Joined",
|
|
97
187
|
stopImpersonating: "Stop impersonating",
|
|
98
188
|
impersonatingAs: "Impersonating {email}",
|
|
99
189
|
manage: "Manage",
|
|
100
190
|
revokeSession: "Revoke",
|
|
191
|
+
verified: "Verified",
|
|
192
|
+
unverified: "Unverified",
|
|
193
|
+
sendVerification: "Send verification email",
|
|
194
|
+
verificationSent: "Verification email sent",
|
|
195
|
+
markVerified: "Mark verified",
|
|
196
|
+
markedVerified: "Marked as verified",
|
|
197
|
+
sendResetEmail: "Send password reset email",
|
|
198
|
+
resetEmailSent: "Password reset email sent",
|
|
199
|
+
sendVerificationOnCreate: "Send verification email",
|
|
101
200
|
},
|
|
102
201
|
sessions: {
|
|
103
202
|
title: "Active sessions",
|
|
@@ -113,6 +212,7 @@ const en = {
|
|
|
113
212
|
status: "Status",
|
|
114
213
|
current: "Current",
|
|
115
214
|
signOutOthers: "Sign out other sessions",
|
|
215
|
+
accountsTitle: "Signed-in accounts", switchAccount: "Switch", switchFailed: "Failed to switch account",
|
|
116
216
|
othersRevoked: "Signed out other sessions",
|
|
117
217
|
total: "{count} sessions",
|
|
118
218
|
},
|
|
@@ -122,6 +222,8 @@ const en = {
|
|
|
122
222
|
device: "Device",
|
|
123
223
|
ip: "IP",
|
|
124
224
|
since: "Since",
|
|
225
|
+
expires: "Expires",
|
|
226
|
+
impersonated: "Impersonated",
|
|
125
227
|
revoke: "Revoke session",
|
|
126
228
|
revokeAll: "Revoke all for user",
|
|
127
229
|
unknown: "Unknown",
|
|
@@ -135,6 +237,8 @@ const en = {
|
|
|
135
237
|
title: "Account",
|
|
136
238
|
profile: "Profile",
|
|
137
239
|
name: "Name",
|
|
240
|
+
username: "Username",
|
|
241
|
+
phone: "Phone number", phoneCode: "Verification code", phoneSendCode: "Send code", phoneVerify: "Verify", phoneCodeSent: "Code sent", phoneVerified: "Phone verified",
|
|
138
242
|
email: "Email",
|
|
139
243
|
role: "Role",
|
|
140
244
|
changePassword: "Change password",
|
|
@@ -144,7 +248,7 @@ const en = {
|
|
|
144
248
|
updating: "Updating…",
|
|
145
249
|
changed: "Password changed",
|
|
146
250
|
changeFailed: "Failed to change password",
|
|
147
|
-
nav: { profile: "Profile", security: "Security", connected: "Connected", sessions: "Sessions", danger: "Danger zone" },
|
|
251
|
+
nav: { profile: "Profile", security: "Security", connected: "Connected", sessions: "Sessions", apiKeys: "API keys", danger: "Danger zone" },
|
|
148
252
|
twoFactorTitle: "Two-factor authentication",
|
|
149
253
|
twoFactorDesc: "Add a one-time code from your authenticator app at sign-in.",
|
|
150
254
|
twoFactorOn: "Enabled",
|
|
@@ -174,6 +278,10 @@ const en = {
|
|
|
174
278
|
save: "Save changes",
|
|
175
279
|
saved: "Profile updated",
|
|
176
280
|
saveFailed: "Failed to save profile",
|
|
281
|
+
saving: "Saving…",
|
|
282
|
+
changeEmail: "Change email",
|
|
283
|
+
emailChanged: "Email address updated",
|
|
284
|
+
emailChangePending: "Check your current inbox to approve the change",
|
|
177
285
|
verified: "Verified",
|
|
178
286
|
unverified: "Unverified",
|
|
179
287
|
},
|
|
@@ -185,15 +293,22 @@ const ko: Messages = {
|
|
|
185
293
|
common: { appName: "관리자" },
|
|
186
294
|
landing: { title: "환영합니다", subtitle: "앱의 홈 페이지입니다. 원하는 내용으로 교체하세요 — 관리자 기능은 /admin 에 있습니다.", openAdmin: "관리자 열기" },
|
|
187
295
|
language: { label: "언어" },
|
|
296
|
+
toolbar: { filter: "필터", search: "검색", searchButton: "검색", all: "전체" },
|
|
188
297
|
theme: { toggle: "테마 전환", light: "라이트", dark: "다크", system: "시스템" },
|
|
189
298
|
auth: {
|
|
190
299
|
signInTitle: "로그인",
|
|
191
300
|
signInDesc: "계속하려면 로그인하세요.",
|
|
192
301
|
email: "이메일",
|
|
302
|
+
emailOrUsername: "이메일 또는 사용자명",
|
|
193
303
|
password: "비밀번호",
|
|
194
304
|
forgot: "비밀번호 찾기",
|
|
195
305
|
signIn: "로그인",
|
|
196
306
|
signingIn: "로그인 중…",
|
|
307
|
+
magicLinkButton: "로그인 링크 이메일로 받기",
|
|
308
|
+
passkeyButton: "패스키 사용",
|
|
309
|
+
magicLinkSent: "이메일의 로그인 링크를 확인하세요.",
|
|
310
|
+
magicLinkNeedsEmail: "먼저 이메일을 입력하세요.",
|
|
311
|
+
otpButton: "코드 이메일로 받기", otpCode: "일회용 코드", otpVerify: "코드 확인",
|
|
197
312
|
noAccount: "계정이 없으신가요?",
|
|
198
313
|
signUp: "회원가입",
|
|
199
314
|
signInFailed: "로그인에 실패했습니다",
|
|
@@ -216,11 +331,87 @@ const ko: Messages = {
|
|
|
216
331
|
updatePassword: "비밀번호 변경",
|
|
217
332
|
updating: "변경 중…",
|
|
218
333
|
missingToken: "재설정 토큰이 없거나 유효하지 않습니다.",
|
|
334
|
+
verifyTitle: "이메일 인증",
|
|
335
|
+
verifyDesc: "{email} 주소로 인증 링크를 보냈습니다. 링크를 열어 계정을 활성화하세요.",
|
|
336
|
+
verifyDescNoEmail: "새 인증 링크를 받을 이메일을 입력하세요.",
|
|
337
|
+
verifySent: "인증 메일을 보냈습니다 — 받은편지함을 확인하세요.",
|
|
338
|
+
verifyLinkError: "인증 링크가 유효하지 않거나 만료되었습니다. 아래에서 다시 요청하세요.",
|
|
339
|
+
resendVerification: "인증 메일 다시 보내기",
|
|
340
|
+
emailNotVerified: "로그인 전에 이메일을 인증해 주세요.",
|
|
219
341
|
resetFailed: "재설정에 실패했습니다",
|
|
220
342
|
},
|
|
221
|
-
nav: { overview: "개요", users: "회원", sessions: "세션", audit: "감사 로그", account: "계정", signOut: "로그아웃" },
|
|
222
|
-
|
|
223
|
-
|
|
343
|
+
nav: { overview: "개요", users: "회원", sessions: "세션", organizations: "조직", audit: "감사 로그", account: "계정", settings: "설정", signOut: "로그아웃" },
|
|
344
|
+
passkeys: {
|
|
345
|
+
title: "패스키", subtitle: "기기로 비밀번호 없이 로그인(Face ID, 지문, 보안 키).",
|
|
346
|
+
name: "이름", namePlaceholder: "예: MacBook", add: "패스키 추가", remove: "삭제", untitled: "패스키",
|
|
347
|
+
added: "패스키를 추가했습니다", addFailed: "패스키 추가에 실패했습니다", removed: "패스키를 삭제했습니다", removeFailed: "패스키 삭제에 실패했습니다",
|
|
348
|
+
empty: "아직 패스키가 없습니다.", created: "생성일", total: "패스키 {count}개",
|
|
349
|
+
},
|
|
350
|
+
acceptInvitation: {
|
|
351
|
+
title: "조직 초대", pending: "초대를 수락하는 중…",
|
|
352
|
+
ok: "조직에 합류했습니다.", error: "유효하지 않거나 만료된 초대입니다.",
|
|
353
|
+
goToOrgs: "조직으로 이동",
|
|
354
|
+
},
|
|
355
|
+
organizations: {
|
|
356
|
+
title: "조직", subtitle: "멀티테넌트 팀. 조직을 만들고 멤버를 초대합니다.",
|
|
357
|
+
create: "새 조직", createTitle: "조직 만들기", name: "이름", slug: "슬러그",
|
|
358
|
+
created: "생성일", delete: "삭제", cancel: "취소", empty: "아직 조직이 없습니다.", total: "조직 {count}개",
|
|
359
|
+
submit: "만들기", createdMsg: "조직을 만들었습니다", deletedMsg: "조직을 삭제했습니다", actionFailed: "작업에 실패했습니다",
|
|
360
|
+
manage: "관리", membersDesc: "멤버와 대기 중인 초대.", members: "멤버", pending: "대기 중 초대",
|
|
361
|
+
parent: "상위 조직", noParent: "없음(최상위)", managers: "매니저", managersHint: "매니저로 추가할 기존 사용자(여러 명 가능).",
|
|
362
|
+
inviteEmail: "이메일로 초대", invite: "초대", invited: "초대를 보냈습니다", remove: "제거", cancelInvite: "취소",
|
|
363
|
+
},
|
|
364
|
+
consent: {
|
|
365
|
+
title: "접근 승인", subtitle: "한 애플리케이션이 계정 접근을 요청합니다.",
|
|
366
|
+
requests: "접근 가능 범위:", allow: "허용", deny: "거부",
|
|
367
|
+
},
|
|
368
|
+
apiKeys: {
|
|
369
|
+
title: "API 키", subtitle: "프로그래밍 방식 접근용 개인 키. x-api-key 헤더로 전송합니다.",
|
|
370
|
+
name: "이름", namePlaceholder: "예: CI 파이프라인", create: "키 생성",
|
|
371
|
+
keyColumn: "키", created: "생성일", untitled: "제목 없음", revoke: "폐기",
|
|
372
|
+
revoked: "키를 폐기했습니다", revokeFailed: "키 폐기에 실패했습니다", empty: "아직 API 키가 없습니다.", total: "API 키 {count}개",
|
|
373
|
+
createdTitle: "API 키 생성됨", createdDesc: "지금 복사하세요 — 다시 볼 수 없습니다.",
|
|
374
|
+
copy: "복사", copied: "클립보드에 복사됨", done: "완료",
|
|
375
|
+
},
|
|
376
|
+
settings: {
|
|
377
|
+
title: "설정", subtitle: "활성화된 인증 기능과 구성 방법입니다.",
|
|
378
|
+
authFeaturesTitle: "인증 기능", authFeaturesSubtitle: "로그인 수단과 보안 기능을 켜고 끕니다. 변경은 즉시 적용됩니다.",
|
|
379
|
+
serverConfigTitle: "서버 구성", serverConfigSubtitle: "환경변수로 설정되어 시작 시 적용됩니다(읽기 전용).",
|
|
380
|
+
enabled: "활성", disabled: "비활성", configuredVia: "{env} 로 구성", manage: "관리",
|
|
381
|
+
saved: "설정을 변경했습니다", saveFailed: "설정 변경에 실패했습니다",
|
|
382
|
+
emailPassword: "이메일·비밀번호", emailPasswordDesc: "이메일과 비밀번호로 로그인.",
|
|
383
|
+
emailVerification: "이메일 인증", emailVerificationDesc: "신규 가입 시 이메일 확인 요구.",
|
|
384
|
+
magicLink: "매직 링크", magicLinkDesc: "이메일 링크로 비밀번호 없이 로그인.",
|
|
385
|
+
emailOtp: "이메일 일회용 코드", emailOtpDesc: "이메일로 받은 코드로 비밀번호 없이 로그인.",
|
|
386
|
+
username: "사용자명 로그인", usernameDesc: "이메일 대신 사용자명으로 로그인.",
|
|
387
|
+
multiSession: "다중 계정 세션", multiSessionDesc: "한 브라우저에서 여러 계정에 로그인하고 전환.",
|
|
388
|
+
phoneNumber: "전화번호", phoneNumberDesc: "SMS로 전화번호 등록·인증(실발송은 프로바이더 연동 필요).",
|
|
389
|
+
apiKey: "API 키", apiKeyDesc: "프로그래밍 방식 접근을 위한 개인 API 키 발급을 허용.",
|
|
390
|
+
passkey: "패스키", passkeyDesc: "비밀번호 없는 로그인을 위한 패스키 등록을 허용.",
|
|
391
|
+
organization: "조직", organizationDesc: "멤버와 이메일 초대를 갖춘 멀티테넌트 조직.",
|
|
392
|
+
oidcProvider: "OIDC 제공자", oidcProviderDesc: "다른 앱이 이 앱으로 로그인하도록 신원 제공자 역할.",
|
|
393
|
+
twoFactor: "2단계 인증", twoFactorDesc: "TOTP 2차 인증 + 백업 코드.",
|
|
394
|
+
socialProviders: "소셜 로그인", socialProvidersDesc: "OAuth 로그인 (Google, GitHub).",
|
|
395
|
+
breachCheck: "유출 비밀번호 검사", breachCheckDesc: "유출된 비밀번호 거부 (Have I Been Pwned).",
|
|
396
|
+
accountDeletion: "계정 삭제", accountDeletionDesc: "사용자가 자신의 계정을 삭제.",
|
|
397
|
+
auditLog: "감사 로그", auditLogDesc: "보안 관련 동작 기록.",
|
|
398
|
+
signInTitle: "로그인 수단", signInSubtitle: "사용자가 인증하는 방법. 각 수단을 켜고 끄며, 소셜 로그인은 팝업에서 자격증명을 입력합니다.",
|
|
399
|
+
emailTitle: "이메일", emailSubtitle: "메일 발송(SMTP) 설정과 이메일 인증 정책.",
|
|
400
|
+
securityTitle: "보안·정책", securitySubtitle: "2단계 인증, 유출 비밀번호 거부, 계정 삭제, 감사 로그.",
|
|
401
|
+
advancedTitle: "고급", advancedSubtitle: "API 키, 조직, OIDC 공급자(자체 IdP) 역할.",
|
|
402
|
+
socialTitle: "소셜 로그인", socialSubtitle: "OAuth 자격증명을 붙여넣으면 해당 provider 로그인이 켜집니다. 재시작 없이 즉시 적용되며, 비밀은 암호화 저장됩니다.",
|
|
403
|
+
smtpTitle: "SMTP(이메일 발송)", smtpSubtitle: "로그인 링크·인증·초대 발송용. 즉시 적용됩니다.",
|
|
404
|
+
google: "Google", github: "GitHub",
|
|
405
|
+
clientId: "Client ID", clientSecret: "Client secret", redirectUri: "Redirect URI", secretSet: "•••••••• (저장됨 — 비우면 유지)",
|
|
406
|
+
save: "저장", cancel: "취소", configure: "설정", notConfigured: "미설정", configuredOff: "설정됨(꺼짐)",
|
|
407
|
+
socialDialogDesc: "provider 콘솔의 OAuth client ID와 secret을 붙여넣고 사용을 켜세요. 즉시 적용됩니다.",
|
|
408
|
+
smtpEnabled: "사용", smtpHost: "호스트", smtpPort: "포트", smtpUser: "사용자", smtpPass: "비밀번호", smtpFrom: "발신 주소", smtpSecure: "TLS(보안)",
|
|
409
|
+
addProvider: "공급자 추가", provider: "공급자", remove: "삭제", smtpConfigure: "SMTP 설정",
|
|
410
|
+
edit: "편집", close: "닫기", back: "뒤로", noProviders: "아직 추가된 공급자가 없습니다.",
|
|
411
|
+
socialCardDesc: "Google, GitHub, Apple 등 OAuth 공급자로 로그인.",
|
|
412
|
+
},
|
|
413
|
+
dashboard: { title: "대시보드", welcome: "{name}님, 환영합니다.", yourRole: "내 권한", signedInAs: "로그인 계정", unverifiedUsers: "미인증 사용자" },
|
|
414
|
+
audit: { title: "감사 로그", when: "시각", actor: "수행자", action: "동작", target: "대상", ip: "IP", empty: "감사 기록이 없습니다.", loadFailed: "감사 로그를 불러오지 못했습니다", total: "항목 {count}개", searchField: "수행자·대상" },
|
|
224
415
|
users: {
|
|
225
416
|
title: "회원",
|
|
226
417
|
search: "이메일로 검색…",
|
|
@@ -248,6 +439,10 @@ const ko: Messages = {
|
|
|
248
439
|
password: "비밀번호",
|
|
249
440
|
create: "생성",
|
|
250
441
|
cancel: "취소",
|
|
442
|
+
close: "닫기",
|
|
443
|
+
roleAdmin: "관리자",
|
|
444
|
+
roleUser: "사용자",
|
|
445
|
+
roleModerator: "중재자",
|
|
251
446
|
userCreated: "사용자를 생성했습니다",
|
|
252
447
|
setPassword: "비밀번호 설정",
|
|
253
448
|
newPassword: "새 비밀번호",
|
|
@@ -267,10 +462,22 @@ const ko: Messages = {
|
|
|
267
462
|
banTitle: "사용자 차단",
|
|
268
463
|
banReason: "사유 (선택)",
|
|
269
464
|
banExpiryDays: "만료 (일, 선택)",
|
|
465
|
+
banExpiry: "차단 만료",
|
|
466
|
+
banPermanent: "영구 차단",
|
|
467
|
+
joined: "가입일",
|
|
270
468
|
stopImpersonating: "대리 로그인 종료",
|
|
271
469
|
impersonatingAs: "{email} 사용자로 대리 로그인 중",
|
|
272
470
|
manage: "관리",
|
|
273
471
|
revokeSession: "종료",
|
|
472
|
+
verified: "인증됨",
|
|
473
|
+
unverified: "미인증",
|
|
474
|
+
sendVerification: "인증 메일 보내기",
|
|
475
|
+
verificationSent: "인증 메일을 보냈습니다",
|
|
476
|
+
markVerified: "인증됨으로 표시",
|
|
477
|
+
markedVerified: "인증 처리했습니다",
|
|
478
|
+
sendResetEmail: "비밀번호 재설정 메일 보내기",
|
|
479
|
+
resetEmailSent: "재설정 메일을 보냈습니다",
|
|
480
|
+
sendVerificationOnCreate: "인증 메일 보내기",
|
|
274
481
|
},
|
|
275
482
|
sessions: {
|
|
276
483
|
title: "활성 세션",
|
|
@@ -286,6 +493,7 @@ const ko: Messages = {
|
|
|
286
493
|
status: "상태",
|
|
287
494
|
current: "현재",
|
|
288
495
|
signOutOthers: "다른 세션 모두 종료",
|
|
496
|
+
accountsTitle: "로그인된 계정", switchAccount: "전환", switchFailed: "계정 전환에 실패했습니다",
|
|
289
497
|
othersRevoked: "다른 세션을 모두 종료했습니다",
|
|
290
498
|
total: "세션 {count}개",
|
|
291
499
|
},
|
|
@@ -295,6 +503,8 @@ const ko: Messages = {
|
|
|
295
503
|
device: "기기",
|
|
296
504
|
ip: "IP",
|
|
297
505
|
since: "시작",
|
|
506
|
+
expires: "만료",
|
|
507
|
+
impersonated: "임퍼스네이트",
|
|
298
508
|
revoke: "세션 종료",
|
|
299
509
|
revokeAll: "사용자 세션 모두 종료",
|
|
300
510
|
unknown: "알 수 없음",
|
|
@@ -308,6 +518,8 @@ const ko: Messages = {
|
|
|
308
518
|
title: "계정",
|
|
309
519
|
profile: "프로필",
|
|
310
520
|
name: "이름",
|
|
521
|
+
username: "사용자명",
|
|
522
|
+
phone: "전화번호", phoneCode: "인증 코드", phoneSendCode: "코드 전송", phoneVerify: "인증", phoneCodeSent: "코드를 전송했습니다", phoneVerified: "전화번호 인증됨",
|
|
311
523
|
email: "이메일",
|
|
312
524
|
role: "권한",
|
|
313
525
|
changePassword: "비밀번호 변경",
|
|
@@ -317,7 +529,7 @@ const ko: Messages = {
|
|
|
317
529
|
updating: "변경 중…",
|
|
318
530
|
changed: "비밀번호를 변경했습니다",
|
|
319
531
|
changeFailed: "비밀번호 변경에 실패했습니다",
|
|
320
|
-
nav: { profile: "프로필", security: "보안", connected: "연결", sessions: "세션", danger: "위험 영역" },
|
|
532
|
+
nav: { profile: "프로필", security: "보안", connected: "연결", sessions: "세션", apiKeys: "API 키", danger: "위험 영역" },
|
|
321
533
|
twoFactorTitle: "2단계 인증",
|
|
322
534
|
twoFactorDesc: "로그인 시 인증 앱의 일회용 코드를 추가로 요구합니다.",
|
|
323
535
|
twoFactorOn: "사용 중",
|
|
@@ -347,6 +559,10 @@ const ko: Messages = {
|
|
|
347
559
|
save: "변경 저장",
|
|
348
560
|
saved: "프로필을 저장했습니다",
|
|
349
561
|
saveFailed: "프로필 저장에 실패했습니다",
|
|
562
|
+
saving: "저장 중…",
|
|
563
|
+
changeEmail: "이메일 변경",
|
|
564
|
+
emailChanged: "이메일 주소를 변경했습니다",
|
|
565
|
+
emailChangePending: "현재 메일함에서 변경을 승인해 주세요",
|
|
350
566
|
verified: "인증됨",
|
|
351
567
|
unverified: "미인증",
|
|
352
568
|
},
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { error } from "@sveltejs/kit";
|
|
2
|
+
|
|
3
|
+
/** Roles that may reach the admin console. Kept as a set so custom roles can be
|
|
4
|
+
* added here (and, later, replaced by a permission check) without touching every
|
|
5
|
+
* page loader. better-auth stores a user's role(s) as a comma-separated string. */
|
|
6
|
+
const ADMIN_ROLES = ["admin"];
|
|
7
|
+
|
|
8
|
+
type WithRole = { role?: string | null } | null | undefined;
|
|
9
|
+
|
|
10
|
+
/** True when the user holds at least one admin role. */
|
|
11
|
+
export function isAdmin(user: WithRole): boolean {
|
|
12
|
+
const roles = (user?.role ?? "")
|
|
13
|
+
.split(",")
|
|
14
|
+
.map((r) => r.trim())
|
|
15
|
+
.filter(Boolean);
|
|
16
|
+
return roles.some((r) => ADMIN_ROLES.includes(r));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Guard an admin-only loader: throws 403 unless the user holds an admin role. */
|
|
20
|
+
export function requireAdmin(user: WithRole): void {
|
|
21
|
+
if (!isAdmin(user)) error(403, "Admins only");
|
|
22
|
+
}
|
package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/+layout.server.ts
CHANGED
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
import type { LayoutServerLoad } from "./$types";
|
|
2
|
-
|
|
3
|
-
type Capabilities = { twoFactor: boolean; providers: string[]; deleteAccount: boolean; auditLog: boolean };
|
|
2
|
+
import type { Capabilities } from "@podosoft/podokit-api-client";
|
|
4
3
|
|
|
5
4
|
export const load: LayoutServerLoad = async ({ locals, fetch }) => {
|
|
6
|
-
let capabilities: Capabilities = {
|
|
5
|
+
let capabilities: Capabilities = {
|
|
6
|
+
twoFactor: false,
|
|
7
|
+
providers: [],
|
|
8
|
+
deleteAccount: false,
|
|
9
|
+
auditLog: false,
|
|
10
|
+
emailVerification: false,
|
|
11
|
+
passwordBreachCheck: false,
|
|
12
|
+
magicLink: false,
|
|
13
|
+
emailOtp: false,
|
|
14
|
+
username: false,
|
|
15
|
+
multiSession: false,
|
|
16
|
+
phoneNumber: false,
|
|
17
|
+
apiKey: false,
|
|
18
|
+
passkey: false,
|
|
19
|
+
organization: false,
|
|
20
|
+
oidcProvider: false,
|
|
21
|
+
roles: ["admin", "user"],
|
|
22
|
+
};
|
|
7
23
|
try {
|
|
8
24
|
const res = await fetch("/api/account/capabilities");
|
|
9
25
|
if (res.ok) capabilities = (await res.json()) as Capabilities;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { page } from "$app/state";
|
|
3
|
+
import { goto } from "$app/navigation";
|
|
4
|
+
import { Button } from "$lib/components/ui/button";
|
|
5
|
+
import * as Card from "$lib/components/ui/card";
|
|
6
|
+
import { api } from "$lib/api";
|
|
7
|
+
import { getI18n } from "$lib/i18n";
|
|
8
|
+
|
|
9
|
+
const i18n = getI18n();
|
|
10
|
+
let status = $state<"pending" | "ok" | "error">("pending");
|
|
11
|
+
|
|
12
|
+
async function accept(): Promise<void> {
|
|
13
|
+
const { error } = await api.auth.organization.acceptInvitation({ invitationId: page.params.id ?? "" });
|
|
14
|
+
status = error ? "error" : "ok";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
$effect(() => {
|
|
18
|
+
void accept();
|
|
19
|
+
});
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<div class="mx-auto flex min-h-[60vh] max-w-md items-center">
|
|
23
|
+
<Card.Root class="w-full">
|
|
24
|
+
<Card.Header>
|
|
25
|
+
<Card.Title>{i18n.t.acceptInvitation.title}</Card.Title>
|
|
26
|
+
<Card.Description>
|
|
27
|
+
{#if status === "pending"}{i18n.t.acceptInvitation.pending}
|
|
28
|
+
{:else if status === "ok"}{i18n.t.acceptInvitation.ok}
|
|
29
|
+
{:else}{i18n.t.acceptInvitation.error}{/if}
|
|
30
|
+
</Card.Description>
|
|
31
|
+
</Card.Header>
|
|
32
|
+
<Card.Content>
|
|
33
|
+
<Button onclick={() => goto("/admin/organizations")}>{i18n.t.acceptInvitation.goToOrgs}</Button>
|
|
34
|
+
</Card.Content>
|
|
35
|
+
</Card.Root>
|
|
36
|
+
</div>
|
package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(app)/admin/+page.svelte
CHANGED
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import * as Card from "$lib/components/ui/card";
|
|
3
|
+
import { api } from "$lib/api";
|
|
3
4
|
import { getI18n, fmt } from "$lib/i18n";
|
|
4
5
|
import type { SessionUser } from "../../../app.d.ts";
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
type Capabilities = { emailVerification: boolean };
|
|
8
|
+
let { data }: { data: { user: SessionUser; capabilities: Capabilities } } = $props();
|
|
7
9
|
const i18n = getI18n();
|
|
10
|
+
|
|
11
|
+
// Unverified-user count — only when email verification is enabled.
|
|
12
|
+
let unverifiedCount = $state<number | null>(null);
|
|
13
|
+
$effect(() => {
|
|
14
|
+
if (!data.capabilities.emailVerification) return;
|
|
15
|
+
void (async () => {
|
|
16
|
+
const { data: res } = await api.auth.admin.listUsers({
|
|
17
|
+
query: { filterField: "emailVerified", filterOperator: "eq", filterValue: false, limit: 1 },
|
|
18
|
+
});
|
|
19
|
+
unverifiedCount = res?.total ?? 0;
|
|
20
|
+
})();
|
|
21
|
+
});
|
|
8
22
|
</script>
|
|
9
23
|
|
|
10
24
|
<div class="flex flex-col gap-6">
|
|
@@ -21,5 +35,11 @@
|
|
|
21
35
|
<Card.Header><Card.Title>{i18n.t.dashboard.signedInAs}</Card.Title></Card.Header>
|
|
22
36
|
<Card.Content class="text-muted-foreground truncate text-sm">{data.user.email}</Card.Content>
|
|
23
37
|
</Card.Root>
|
|
38
|
+
{#if data.capabilities.emailVerification}
|
|
39
|
+
<Card.Root>
|
|
40
|
+
<Card.Header><Card.Title>{i18n.t.dashboard.unverifiedUsers}</Card.Title></Card.Header>
|
|
41
|
+
<Card.Content class="text-2xl font-semibold">{unverifiedCount ?? "—"}</Card.Content>
|
|
42
|
+
</Card.Root>
|
|
43
|
+
{/if}
|
|
24
44
|
</div>
|
|
25
45
|
</div>
|