s3db.js 13.4.0 → 13.6.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 +25 -10
- package/dist/{s3db.cjs.js → s3db.cjs} +38801 -32446
- package/dist/s3db.cjs.map +1 -0
- package/dist/s3db.es.js +38653 -32291
- package/dist/s3db.es.js.map +1 -1
- package/package.json +218 -22
- package/src/concerns/id.js +90 -6
- package/src/concerns/index.js +2 -1
- package/src/concerns/password-hashing.js +150 -0
- package/src/database.class.js +6 -2
- package/src/plugins/api/auth/basic-auth.js +40 -10
- package/src/plugins/api/auth/index.js +49 -3
- package/src/plugins/api/auth/oauth2-auth.js +171 -0
- package/src/plugins/api/auth/oidc-auth.js +789 -0
- package/src/plugins/api/auth/oidc-client.js +462 -0
- package/src/plugins/api/auth/path-auth-matcher.js +284 -0
- package/src/plugins/api/concerns/event-emitter.js +134 -0
- package/src/plugins/api/concerns/failban-manager.js +651 -0
- package/src/plugins/api/concerns/guards-helpers.js +402 -0
- package/src/plugins/api/concerns/metrics-collector.js +346 -0
- package/src/plugins/api/index.js +510 -57
- package/src/plugins/api/middlewares/failban.js +305 -0
- package/src/plugins/api/middlewares/rate-limit.js +301 -0
- package/src/plugins/api/middlewares/request-id.js +74 -0
- package/src/plugins/api/middlewares/security-headers.js +120 -0
- package/src/plugins/api/middlewares/session-tracking.js +194 -0
- package/src/plugins/api/routes/auth-routes.js +119 -78
- package/src/plugins/api/routes/resource-routes.js +73 -30
- package/src/plugins/api/server.js +1139 -45
- package/src/plugins/api/utils/custom-routes.js +102 -0
- package/src/plugins/api/utils/guards.js +213 -0
- package/src/plugins/api/utils/mime-types.js +154 -0
- package/src/plugins/api/utils/openapi-generator.js +91 -12
- package/src/plugins/api/utils/path-matcher.js +173 -0
- package/src/plugins/api/utils/static-filesystem.js +262 -0
- package/src/plugins/api/utils/static-s3.js +231 -0
- package/src/plugins/api/utils/template-engine.js +188 -0
- package/src/plugins/cloud-inventory/drivers/alibaba-driver.js +853 -0
- package/src/plugins/cloud-inventory/drivers/aws-driver.js +2554 -0
- package/src/plugins/cloud-inventory/drivers/azure-driver.js +637 -0
- package/src/plugins/cloud-inventory/drivers/base-driver.js +99 -0
- package/src/plugins/cloud-inventory/drivers/cloudflare-driver.js +620 -0
- package/src/plugins/cloud-inventory/drivers/digitalocean-driver.js +698 -0
- package/src/plugins/cloud-inventory/drivers/gcp-driver.js +645 -0
- package/src/plugins/cloud-inventory/drivers/hetzner-driver.js +559 -0
- package/src/plugins/cloud-inventory/drivers/linode-driver.js +614 -0
- package/src/plugins/cloud-inventory/drivers/mock-drivers.js +449 -0
- package/src/plugins/cloud-inventory/drivers/mongodb-atlas-driver.js +771 -0
- package/src/plugins/cloud-inventory/drivers/oracle-driver.js +768 -0
- package/src/plugins/cloud-inventory/drivers/vultr-driver.js +636 -0
- package/src/plugins/cloud-inventory/index.js +20 -0
- package/src/plugins/cloud-inventory/registry.js +146 -0
- package/src/plugins/cloud-inventory/terraform-exporter.js +362 -0
- package/src/plugins/cloud-inventory.plugin.js +1333 -0
- package/src/plugins/concerns/plugin-dependencies.js +62 -2
- package/src/plugins/eventual-consistency/analytics.js +1 -0
- package/src/plugins/eventual-consistency/consolidation.js +2 -2
- package/src/plugins/eventual-consistency/garbage-collection.js +2 -2
- package/src/plugins/eventual-consistency/install.js +2 -2
- package/src/plugins/identity/README.md +335 -0
- package/src/plugins/identity/concerns/mfa-manager.js +204 -0
- package/src/plugins/identity/concerns/password.js +138 -0
- package/src/plugins/identity/concerns/resource-schemas.js +273 -0
- package/src/plugins/identity/concerns/token-generator.js +172 -0
- package/src/plugins/identity/email-service.js +422 -0
- package/src/plugins/identity/index.js +1052 -0
- package/src/plugins/identity/oauth2-server.js +1033 -0
- package/src/plugins/identity/oidc-discovery.js +285 -0
- package/src/plugins/identity/rsa-keys.js +323 -0
- package/src/plugins/identity/server.js +500 -0
- package/src/plugins/identity/session-manager.js +453 -0
- package/src/plugins/identity/ui/layouts/base.js +251 -0
- package/src/plugins/identity/ui/middleware.js +135 -0
- package/src/plugins/identity/ui/pages/admin/client-form.js +247 -0
- package/src/plugins/identity/ui/pages/admin/clients.js +179 -0
- package/src/plugins/identity/ui/pages/admin/dashboard.js +181 -0
- package/src/plugins/identity/ui/pages/admin/user-form.js +283 -0
- package/src/plugins/identity/ui/pages/admin/users.js +263 -0
- package/src/plugins/identity/ui/pages/consent.js +262 -0
- package/src/plugins/identity/ui/pages/forgot-password.js +104 -0
- package/src/plugins/identity/ui/pages/login.js +144 -0
- package/src/plugins/identity/ui/pages/mfa-backup-codes.js +180 -0
- package/src/plugins/identity/ui/pages/mfa-enrollment.js +187 -0
- package/src/plugins/identity/ui/pages/mfa-verification.js +178 -0
- package/src/plugins/identity/ui/pages/oauth-error.js +225 -0
- package/src/plugins/identity/ui/pages/profile.js +361 -0
- package/src/plugins/identity/ui/pages/register.js +226 -0
- package/src/plugins/identity/ui/pages/reset-password.js +128 -0
- package/src/plugins/identity/ui/pages/verify-email.js +172 -0
- package/src/plugins/identity/ui/routes.js +2541 -0
- package/src/plugins/identity/ui/styles/main.css +465 -0
- package/src/plugins/index.js +4 -1
- package/src/plugins/ml/base-model.class.js +65 -16
- package/src/plugins/ml/classification-model.class.js +1 -1
- package/src/plugins/ml/timeseries-model.class.js +3 -1
- package/src/plugins/ml.plugin.js +584 -31
- package/src/plugins/shared/error-handler.js +147 -0
- package/src/plugins/shared/index.js +9 -0
- package/src/plugins/shared/middlewares/compression.js +117 -0
- package/src/plugins/shared/middlewares/cors.js +49 -0
- package/src/plugins/shared/middlewares/index.js +11 -0
- package/src/plugins/shared/middlewares/logging.js +54 -0
- package/src/plugins/shared/middlewares/rate-limit.js +73 -0
- package/src/plugins/shared/middlewares/security.js +158 -0
- package/src/plugins/shared/response-formatter.js +264 -0
- package/src/plugins/state-machine.plugin.js +57 -2
- package/src/resource.class.js +140 -12
- package/src/schema.class.js +30 -1
- package/src/validator.class.js +57 -6
- package/dist/s3db.cjs.js.map +0 -1
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registration Page
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { html } from 'hono/html';
|
|
6
|
+
import { BaseLayout } from '../layouts/base.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Render registration page
|
|
10
|
+
* @param {Object} props - Page properties
|
|
11
|
+
* @param {string} [props.error] - Error message
|
|
12
|
+
* @param {string} [props.email] - Pre-filled email
|
|
13
|
+
* @param {string} [props.name] - Pre-filled name
|
|
14
|
+
* @param {Object} [props.passwordPolicy] - Password policy configuration
|
|
15
|
+
* @param {Object} [props.config] - UI configuration
|
|
16
|
+
* @returns {string} HTML string
|
|
17
|
+
*/
|
|
18
|
+
export function RegisterPage(props = {}) {
|
|
19
|
+
const { error = null, email = '', name = '', passwordPolicy = {}, config = {} } = props;
|
|
20
|
+
|
|
21
|
+
const companyName = config.companyName || 'S3DB';
|
|
22
|
+
|
|
23
|
+
// Extract password policy
|
|
24
|
+
const minLength = passwordPolicy.minLength || 8;
|
|
25
|
+
const maxLength = passwordPolicy.maxLength || 128;
|
|
26
|
+
const requireUppercase = passwordPolicy.requireUppercase !== false;
|
|
27
|
+
const requireLowercase = passwordPolicy.requireLowercase !== false;
|
|
28
|
+
const requireNumbers = passwordPolicy.requireNumbers !== false;
|
|
29
|
+
const requireSymbols = passwordPolicy.requireSymbols || false;
|
|
30
|
+
|
|
31
|
+
// Build password requirements text
|
|
32
|
+
const requirements = [];
|
|
33
|
+
requirements.push(`${minLength}-${maxLength} characters`);
|
|
34
|
+
if (requireUppercase) requirements.push('uppercase letter');
|
|
35
|
+
if (requireLowercase) requirements.push('lowercase letter');
|
|
36
|
+
if (requireNumbers) requirements.push('number');
|
|
37
|
+
if (requireSymbols) requirements.push('symbol');
|
|
38
|
+
|
|
39
|
+
const inputClasses = [
|
|
40
|
+
'block w-full rounded-2xl border bg-white/[0.08] px-4 py-3 text-base text-white',
|
|
41
|
+
'shadow-[0_1px_0_rgba(255,255,255,0.06)] transition placeholder:text-slate-300/70 focus:outline-none focus:ring-2',
|
|
42
|
+
error ? 'border-red-400/70 focus:border-red-400 focus:ring-red-400/40' : 'border-white/10 focus:border-white/40 focus:ring-white/30'
|
|
43
|
+
].join(' ');
|
|
44
|
+
|
|
45
|
+
const checkboxClasses = [
|
|
46
|
+
'h-5 w-5 rounded border-white/30 bg-slate-900/70 text-primary',
|
|
47
|
+
'focus:ring-2 focus:ring-primary/40 focus:ring-offset-0 focus:outline-none'
|
|
48
|
+
].join(' ');
|
|
49
|
+
|
|
50
|
+
const content = html`
|
|
51
|
+
<section class="mx-auto flex w-full max-w-5xl flex-col items-center gap-12 text-slate-100 md:flex-row md:items-start md:justify-between">
|
|
52
|
+
<div class="order-2 w-full max-w-xl md:order-1">
|
|
53
|
+
<div class="relative isolate overflow-hidden rounded-3xl border border-white/10 bg-slate-900/60 p-10 shadow-2xl shadow-slate-900/60 backdrop-blur">
|
|
54
|
+
<div class="pointer-events-none absolute -right-24 -top-28 h-64 w-64 rounded-full bg-primary/25 blur-3xl"></div>
|
|
55
|
+
<div class="pointer-events-none absolute -bottom-28 -left-24 h-56 w-56 rounded-full bg-secondary/20 blur-[120px]"></div>
|
|
56
|
+
|
|
57
|
+
<div class="relative z-10 space-y-8">
|
|
58
|
+
<header class="space-y-2 text-center">
|
|
59
|
+
<h2 class="text-2xl font-semibold tracking-tight text-white">
|
|
60
|
+
Create Account
|
|
61
|
+
</h2>
|
|
62
|
+
<p class="text-sm text-slate-300">
|
|
63
|
+
Join ${companyName} Identity to access all secure services.
|
|
64
|
+
</p>
|
|
65
|
+
</header>
|
|
66
|
+
|
|
67
|
+
${error ? html`
|
|
68
|
+
<div class="rounded-xl border border-red-500/40 bg-red-500/10 px-4 py-3 text-sm leading-6 text-red-100 shadow-md shadow-red-900/30">
|
|
69
|
+
${error}
|
|
70
|
+
</div>
|
|
71
|
+
` : ''}
|
|
72
|
+
|
|
73
|
+
<form method="POST" action="/register" class="space-y-6">
|
|
74
|
+
<div class="space-y-2">
|
|
75
|
+
<label for="name" class="text-sm font-semibold text-slate-200">
|
|
76
|
+
Full Name
|
|
77
|
+
</label>
|
|
78
|
+
<input
|
|
79
|
+
type="text"
|
|
80
|
+
class="${inputClasses}"
|
|
81
|
+
id="name"
|
|
82
|
+
name="name"
|
|
83
|
+
value="${name}"
|
|
84
|
+
required
|
|
85
|
+
autofocus
|
|
86
|
+
autocomplete="name"
|
|
87
|
+
minlength="2"
|
|
88
|
+
maxlength="100"
|
|
89
|
+
/>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<div class="space-y-2">
|
|
93
|
+
<label for="email" class="text-sm font-semibold text-slate-200">
|
|
94
|
+
Email address
|
|
95
|
+
</label>
|
|
96
|
+
<input
|
|
97
|
+
type="email"
|
|
98
|
+
class="${inputClasses}"
|
|
99
|
+
id="email"
|
|
100
|
+
name="email"
|
|
101
|
+
value="${email}"
|
|
102
|
+
required
|
|
103
|
+
autocomplete="email"
|
|
104
|
+
/>
|
|
105
|
+
<p class="text-xs text-slate-400">
|
|
106
|
+
We'll send you a verification email to confirm your account.
|
|
107
|
+
</p>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
<div class="space-y-2">
|
|
111
|
+
<label for="password" class="text-sm font-semibold text-slate-200">
|
|
112
|
+
Password
|
|
113
|
+
</label>
|
|
114
|
+
<input
|
|
115
|
+
type="password"
|
|
116
|
+
class="${inputClasses}"
|
|
117
|
+
id="password"
|
|
118
|
+
name="password"
|
|
119
|
+
required
|
|
120
|
+
autocomplete="new-password"
|
|
121
|
+
minlength="${minLength}"
|
|
122
|
+
maxlength="${maxLength}"
|
|
123
|
+
/>
|
|
124
|
+
<div class="rounded-2xl border border-white/5 bg-white/[0.06] px-4 py-3 text-xs leading-5 text-slate-200">
|
|
125
|
+
<span class="font-semibold text-white/80">Must include:</span>
|
|
126
|
+
<ul class="mt-2 list-disc space-y-1 pl-5 text-slate-300">
|
|
127
|
+
${requirements.map(req => html`<li>${req}</li>`)}
|
|
128
|
+
</ul>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
<div class="space-y-2">
|
|
133
|
+
<label for="confirm_password" class="text-sm font-semibold text-slate-200">
|
|
134
|
+
Confirm Password
|
|
135
|
+
</label>
|
|
136
|
+
<input
|
|
137
|
+
type="password"
|
|
138
|
+
class="${inputClasses}"
|
|
139
|
+
id="confirm_password"
|
|
140
|
+
name="confirm_password"
|
|
141
|
+
required
|
|
142
|
+
autocomplete="new-password"
|
|
143
|
+
/>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<label class="flex items-start gap-3 text-sm text-slate-300">
|
|
147
|
+
<input
|
|
148
|
+
type="checkbox"
|
|
149
|
+
class="${checkboxClasses} mt-0.5"
|
|
150
|
+
id="agree_terms"
|
|
151
|
+
name="agree_terms"
|
|
152
|
+
value="1"
|
|
153
|
+
required
|
|
154
|
+
/>
|
|
155
|
+
<span>
|
|
156
|
+
I agree to the
|
|
157
|
+
<a href="/terms" class="font-semibold text-primary transition hover:text-white" target="_blank">Terms of Service</a>
|
|
158
|
+
and
|
|
159
|
+
<a href="/privacy" class="font-semibold text-primary transition hover:text-white" target="_blank">Privacy Policy</a>.
|
|
160
|
+
</span>
|
|
161
|
+
</label>
|
|
162
|
+
|
|
163
|
+
<button
|
|
164
|
+
type="submit"
|
|
165
|
+
class="inline-flex w-full items-center justify-center rounded-2xl bg-gradient-to-r from-primary via-primary to-secondary px-4 py-3 text-base font-semibold text-white transition duration-200 hover:-translate-y-0.5 focus:outline-none focus:ring-2 focus:ring-white/30"
|
|
166
|
+
style="box-shadow: 0 18px 45px var(--color-primary-glow);"
|
|
167
|
+
>
|
|
168
|
+
Create Account
|
|
169
|
+
</button>
|
|
170
|
+
</form>
|
|
171
|
+
|
|
172
|
+
<p class="text-center text-sm text-slate-300">
|
|
173
|
+
Already have an account?
|
|
174
|
+
<a href="/login" class="font-semibold text-primary transition hover:text-white">
|
|
175
|
+
Sign in
|
|
176
|
+
</a>
|
|
177
|
+
</p>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
|
|
182
|
+
<div class="order-1 max-w-xl text-center md:order-2 md:text-left">
|
|
183
|
+
${config.logoUrl ? html`
|
|
184
|
+
<div class="mb-6 flex justify-center md:justify-start">
|
|
185
|
+
<img src="${config.logoUrl}" alt="${config.title || 'Identity Logo'}" class="h-12 w-auto" />
|
|
186
|
+
</div>
|
|
187
|
+
` : ''}
|
|
188
|
+
<h1 class="text-3xl font-semibold tracking-tight text-white md:text-4xl">
|
|
189
|
+
Welcome to ${config.title || 'S3DB Identity'}
|
|
190
|
+
</h1>
|
|
191
|
+
<p class="mt-4 text-base text-slate-300 md:text-lg">
|
|
192
|
+
${config.tagline || 'Create a secure identity to access your workspace and applications.'}
|
|
193
|
+
</p>
|
|
194
|
+
<div class="mt-8 grid gap-4 text-left text-sm text-slate-300">
|
|
195
|
+
<div class="rounded-2xl border border-white/5 bg-white/[0.04] px-4 py-3">
|
|
196
|
+
<span class="font-semibold text-white">✅ Fast onboarding</span>
|
|
197
|
+
<p class="mt-1 text-slate-300">
|
|
198
|
+
Start collaborating in minutes with instant verification.
|
|
199
|
+
</p>
|
|
200
|
+
</div>
|
|
201
|
+
<div class="rounded-2xl border border-white/5 bg-white/[0.04] px-4 py-3">
|
|
202
|
+
<span class="font-semibold text-white">🔐 Enterprise-grade security</span>
|
|
203
|
+
<p class="mt-1 text-slate-300">
|
|
204
|
+
Backed by multi-layer encryption and continuous monitoring.
|
|
205
|
+
</p>
|
|
206
|
+
</div>
|
|
207
|
+
<div class="rounded-2xl border border-white/5 bg-white/[0.04] px-4 py-3">
|
|
208
|
+
<span class="font-semibold text-white">💬 Live support</span>
|
|
209
|
+
<p class="mt-1 text-slate-300">
|
|
210
|
+
Our team is ready to help you with any registration issues.
|
|
211
|
+
</p>
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
215
|
+
</section>
|
|
216
|
+
`;
|
|
217
|
+
|
|
218
|
+
return BaseLayout({
|
|
219
|
+
title: 'Create Account',
|
|
220
|
+
content,
|
|
221
|
+
config,
|
|
222
|
+
error: null // Error shown in form
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export default RegisterPage;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reset Password Page
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { html } from 'hono/html';
|
|
6
|
+
import { BaseLayout } from '../layouts/base.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Render reset password page
|
|
10
|
+
* @param {Object} props - Page properties
|
|
11
|
+
* @param {string} [props.error] - Error message
|
|
12
|
+
* @param {string} [props.token] - Reset token from URL
|
|
13
|
+
* @param {Object} [props.passwordPolicy] - Password policy configuration
|
|
14
|
+
* @param {Object} [props.config] - UI configuration
|
|
15
|
+
* @returns {string} HTML string
|
|
16
|
+
*/
|
|
17
|
+
export function ResetPasswordPage(props = {}) {
|
|
18
|
+
const { error = null, token = '', passwordPolicy = {}, config = {} } = props;
|
|
19
|
+
|
|
20
|
+
// Extract password policy
|
|
21
|
+
const minLength = passwordPolicy.minLength || 8;
|
|
22
|
+
const maxLength = passwordPolicy.maxLength || 128;
|
|
23
|
+
const requireUppercase = passwordPolicy.requireUppercase !== false;
|
|
24
|
+
const requireLowercase = passwordPolicy.requireLowercase !== false;
|
|
25
|
+
const requireNumbers = passwordPolicy.requireNumbers !== false;
|
|
26
|
+
const requireSymbols = passwordPolicy.requireSymbols || false;
|
|
27
|
+
|
|
28
|
+
// Build password requirements text
|
|
29
|
+
const requirements = [];
|
|
30
|
+
requirements.push(`${minLength}-${maxLength} characters`);
|
|
31
|
+
if (requireUppercase) requirements.push('uppercase letter');
|
|
32
|
+
if (requireLowercase) requirements.push('lowercase letter');
|
|
33
|
+
if (requireNumbers) requirements.push('number');
|
|
34
|
+
if (requireSymbols) requirements.push('symbol');
|
|
35
|
+
|
|
36
|
+
const inputClasses = [
|
|
37
|
+
'block w-full rounded-2xl border bg-white/[0.08] px-4 py-3 text-base text-white',
|
|
38
|
+
'shadow-[0_1px_0_rgba(255,255,255,0.06)] transition placeholder:text-slate-300/70 focus:outline-none focus:ring-2',
|
|
39
|
+
error ? 'border-red-400/70 focus:border-red-400 focus:ring-red-400/40' : 'border-white/10 focus:border-white/40 focus:ring-white/30'
|
|
40
|
+
].join(' ');
|
|
41
|
+
|
|
42
|
+
const content = html`
|
|
43
|
+
<section class="mx-auto w-full max-w-lg text-slate-100">
|
|
44
|
+
<div class="relative isolate overflow-hidden rounded-3xl border border-white/10 bg-slate-900/60 p-10 shadow-2xl shadow-slate-900/60 backdrop-blur">
|
|
45
|
+
<div class="pointer-events-none absolute -right-20 -top-28 h-60 w-60 rounded-full bg-primary/25 blur-3xl"></div>
|
|
46
|
+
<div class="pointer-events-none absolute -bottom-24 -left-28 h-56 w-56 rounded-full bg-secondary/20 blur-[120px]"></div>
|
|
47
|
+
|
|
48
|
+
<div class="relative z-10 space-y-6">
|
|
49
|
+
<header class="space-y-2 text-center">
|
|
50
|
+
<h2 class="text-2xl font-semibold tracking-tight text-white">
|
|
51
|
+
Set New Password
|
|
52
|
+
</h2>
|
|
53
|
+
<p class="text-sm text-slate-300">
|
|
54
|
+
Choose a strong password to secure your account.
|
|
55
|
+
</p>
|
|
56
|
+
</header>
|
|
57
|
+
|
|
58
|
+
<form method="POST" action="/reset-password" class="space-y-6">
|
|
59
|
+
<input type="hidden" name="token" value="${token}" />
|
|
60
|
+
|
|
61
|
+
<div class="space-y-2">
|
|
62
|
+
<label for="password" class="text-sm font-semibold text-slate-200">
|
|
63
|
+
New Password
|
|
64
|
+
</label>
|
|
65
|
+
<input
|
|
66
|
+
type="password"
|
|
67
|
+
class="${inputClasses}"
|
|
68
|
+
id="password"
|
|
69
|
+
name="password"
|
|
70
|
+
required
|
|
71
|
+
autofocus
|
|
72
|
+
autocomplete="new-password"
|
|
73
|
+
minlength="${minLength}"
|
|
74
|
+
maxlength="${maxLength}"
|
|
75
|
+
/>
|
|
76
|
+
<div class="rounded-2xl border border-white/5 bg-white/[0.06] px-4 py-3 text-xs leading-5 text-slate-200">
|
|
77
|
+
<span class="font-semibold text-white/80">Must include:</span>
|
|
78
|
+
<ul class="mt-2 list-disc space-y-1 pl-5 text-slate-300">
|
|
79
|
+
${requirements.map(req => html`<li>${req}</li>`)}
|
|
80
|
+
</ul>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<div class="space-y-2">
|
|
85
|
+
<label for="confirm_password" class="text-sm font-semibold text-slate-200">
|
|
86
|
+
Confirm New Password
|
|
87
|
+
</label>
|
|
88
|
+
<input
|
|
89
|
+
type="password"
|
|
90
|
+
class="${inputClasses}"
|
|
91
|
+
id="confirm_password"
|
|
92
|
+
name="confirm_password"
|
|
93
|
+
required
|
|
94
|
+
autocomplete="new-password"
|
|
95
|
+
/>
|
|
96
|
+
${error ? html`<p class="text-xs text-red-200">${error}</p>` : ''}
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<button
|
|
100
|
+
type="submit"
|
|
101
|
+
class="inline-flex w-full items-center justify-center rounded-2xl bg-gradient-to-r from-primary via-primary to-secondary px-4 py-3 text-base font-semibold text-white transition duration-200 hover:-translate-y-0.5 focus:outline-none focus:ring-2 focus:ring-white/30"
|
|
102
|
+
style="box-shadow: 0 18px 45px var(--color-primary-glow);"
|
|
103
|
+
>
|
|
104
|
+
Reset Password
|
|
105
|
+
</button>
|
|
106
|
+
</form>
|
|
107
|
+
|
|
108
|
+
<p class="text-center text-sm text-slate-300">
|
|
109
|
+
Remember your password?
|
|
110
|
+
<a href="/login" class="font-semibold text-primary transition hover:text-white">
|
|
111
|
+
Sign in
|
|
112
|
+
</a>
|
|
113
|
+
</p>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
</section>
|
|
117
|
+
`;
|
|
118
|
+
|
|
119
|
+
return BaseLayout({
|
|
120
|
+
title: 'Set New Password',
|
|
121
|
+
content,
|
|
122
|
+
config,
|
|
123
|
+
error: null, // Error shown in form
|
|
124
|
+
success: null
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export default ResetPasswordPage;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Email Verification Page
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { html } from 'hono/html';
|
|
6
|
+
import { BaseLayout } from '../layouts/base.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Render email verification page
|
|
10
|
+
* @param {Object} props - Page properties
|
|
11
|
+
* @param {string} [props.status] - Verification status (success, error, expired, pending)
|
|
12
|
+
* @param {string} [props.email] - User email (for resend)
|
|
13
|
+
* @param {string} [props.message] - Status message
|
|
14
|
+
* @param {Object} [props.config] - UI configuration
|
|
15
|
+
* @returns {string} HTML string
|
|
16
|
+
*/
|
|
17
|
+
export function VerifyEmailPage(props = {}) {
|
|
18
|
+
const { status = 'pending', email = '', message = '', config = {} } = props;
|
|
19
|
+
|
|
20
|
+
const statusConfig = {
|
|
21
|
+
success: {
|
|
22
|
+
icon: '✅',
|
|
23
|
+
title: 'Email Verified!',
|
|
24
|
+
color: 'var(--color-success)',
|
|
25
|
+
defaultMessage: 'Your email address has been successfully verified.'
|
|
26
|
+
},
|
|
27
|
+
error: {
|
|
28
|
+
icon: '❌',
|
|
29
|
+
title: 'Verification Failed',
|
|
30
|
+
color: 'var(--color-danger)',
|
|
31
|
+
defaultMessage: 'The verification link is invalid or has already been used.'
|
|
32
|
+
},
|
|
33
|
+
expired: {
|
|
34
|
+
icon: '⏰',
|
|
35
|
+
title: 'Link Expired',
|
|
36
|
+
color: 'var(--color-warning)',
|
|
37
|
+
defaultMessage: 'This verification link has expired. Please request a new one.'
|
|
38
|
+
},
|
|
39
|
+
pending: {
|
|
40
|
+
icon: '📧',
|
|
41
|
+
title: 'Verify Your Email',
|
|
42
|
+
color: 'var(--color-primary)',
|
|
43
|
+
defaultMessage: 'Please check your email for a verification link.'
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const currentStatus = statusConfig[status] || statusConfig.pending;
|
|
48
|
+
const displayMessage = message || currentStatus.defaultMessage;
|
|
49
|
+
|
|
50
|
+
const primaryButtonClasses = [
|
|
51
|
+
'inline-flex items-center justify-center rounded-2xl bg-gradient-to-r',
|
|
52
|
+
'from-primary via-primary to-secondary px-6 py-3 text-sm font-semibold text-white',
|
|
53
|
+
'transition duration-200 hover:-translate-y-0.5 focus:outline-none focus:ring-2 focus:ring-white/30'
|
|
54
|
+
].join(' ');
|
|
55
|
+
|
|
56
|
+
const secondaryButtonClasses = [
|
|
57
|
+
'inline-flex items-center justify-center rounded-2xl border border-white/15 bg-white/[0.06]',
|
|
58
|
+
'px-6 py-3 text-sm font-semibold text-white transition duration-200 hover:bg-white/[0.12]',
|
|
59
|
+
'focus:outline-none focus:ring-2 focus:ring-white/20'
|
|
60
|
+
].join(' ');
|
|
61
|
+
|
|
62
|
+
const content = html`
|
|
63
|
+
<section class="mx-auto w-full max-w-3xl text-slate-100">
|
|
64
|
+
<div class="flex flex-col items-center gap-4 pb-8 text-center">
|
|
65
|
+
${config.logoUrl ? html`
|
|
66
|
+
<img src="${config.logoUrl}" alt="${config.title || 'Identity Logo'}" class="h-14 w-auto" />
|
|
67
|
+
` : ''}
|
|
68
|
+
<h1 class="text-3xl font-semibold tracking-tight text-white md:text-4xl">
|
|
69
|
+
${currentStatus.title}
|
|
70
|
+
</h1>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<div class="relative isolate overflow-hidden rounded-3xl border border-white/10 bg-slate-900/60 px-10 py-12 text-center shadow-2xl shadow-slate-900/60 backdrop-blur">
|
|
74
|
+
<div class="pointer-events-none absolute -left-24 top-10 h-64 w-64 rounded-full bg-primary/25 blur-3xl"></div>
|
|
75
|
+
<div class="pointer-events-none absolute -right-16 -top-20 h-56 w-56 rounded-full bg-secondary/25 blur-[120px]"></div>
|
|
76
|
+
|
|
77
|
+
<div class="relative z-10 space-y-8">
|
|
78
|
+
<div class="mx-auto flex h-20 w-20 items-center justify-center rounded-full border border-white/10 bg-white/[0.08] text-4xl">
|
|
79
|
+
${currentStatus.icon}
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<p class="text-base text-slate-200 md:text-lg" style="color: ${currentStatus.color};">
|
|
83
|
+
${displayMessage}
|
|
84
|
+
</p>
|
|
85
|
+
|
|
86
|
+
${status === 'success' ? html`
|
|
87
|
+
<div class="space-y-4">
|
|
88
|
+
<a href="/login" class="${primaryButtonClasses}" style="box-shadow: 0 18px 45px var(--color-primary-glow);">
|
|
89
|
+
Sign In
|
|
90
|
+
</a>
|
|
91
|
+
<div class="text-sm text-slate-300">
|
|
92
|
+
<a href="/profile" class="font-semibold text-primary transition hover:text-white">
|
|
93
|
+
Go to your profile
|
|
94
|
+
</a>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
` : status === 'error' || status === 'expired' ? html`
|
|
98
|
+
<div class="space-y-6">
|
|
99
|
+
${email ? html`
|
|
100
|
+
<form method="POST" action="/verify-email/resend">
|
|
101
|
+
<input type="hidden" name="email" value="${email}" />
|
|
102
|
+
<button type="submit" class="${primaryButtonClasses}" style="box-shadow: 0 18px 45px var(--color-primary-glow);">
|
|
103
|
+
Send New Verification Email
|
|
104
|
+
</button>
|
|
105
|
+
</form>
|
|
106
|
+
` : html`
|
|
107
|
+
<a href="/login" class="${primaryButtonClasses}" style="box-shadow: 0 18px 45px var(--color-primary-glow);">
|
|
108
|
+
Sign In to Resend
|
|
109
|
+
</a>
|
|
110
|
+
`}
|
|
111
|
+
<div class="text-sm text-slate-300">
|
|
112
|
+
<a href="/login" class="font-semibold text-primary transition hover:text-white">
|
|
113
|
+
Sign in
|
|
114
|
+
</a>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
` : html`
|
|
118
|
+
<div class="space-y-6">
|
|
119
|
+
<div class="rounded-2xl border border-white/10 bg-white/[0.06] px-6 py-5 text-left text-sm text-slate-200">
|
|
120
|
+
<p class="mb-2 font-semibold text-white/90">
|
|
121
|
+
Didn't receive the email?
|
|
122
|
+
</p>
|
|
123
|
+
<ul class="list-disc space-y-1 pl-5 text-slate-300">
|
|
124
|
+
<li>Check your spam or junk folder</li>
|
|
125
|
+
<li>Make sure the email address is correct</li>
|
|
126
|
+
<li>Wait a few minutes and check again</li>
|
|
127
|
+
</ul>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
${email ? html`
|
|
131
|
+
<form method="POST" action="/verify-email/resend">
|
|
132
|
+
<input type="hidden" name="email" value="${email}" />
|
|
133
|
+
<button type="submit" class="${secondaryButtonClasses}">
|
|
134
|
+
Resend Verification Email
|
|
135
|
+
</button>
|
|
136
|
+
</form>
|
|
137
|
+
` : html`
|
|
138
|
+
<a href="/login" class="${secondaryButtonClasses}">
|
|
139
|
+
Sign In to Resend
|
|
140
|
+
</a>
|
|
141
|
+
`}
|
|
142
|
+
|
|
143
|
+
<div class="text-sm text-slate-300">
|
|
144
|
+
<a href="/login" class="font-semibold text-primary transition hover:text-white">
|
|
145
|
+
Back to sign in
|
|
146
|
+
</a>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
`}
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
${status === 'success' ? html`
|
|
154
|
+
<div class="mt-6 rounded-2xl border border-emerald-400/40 bg-emerald-500/10 px-6 py-5 text-center text-sm leading-6 text-emerald-100 shadow-lg shadow-emerald-900/30">
|
|
155
|
+
<strong>Your account is now fully activated!</strong><br>
|
|
156
|
+
You can now access all features and services.
|
|
157
|
+
</div>
|
|
158
|
+
` : ''}
|
|
159
|
+
</section>
|
|
160
|
+
`;
|
|
161
|
+
|
|
162
|
+
return BaseLayout({
|
|
163
|
+
title: currentStatus.title,
|
|
164
|
+
content,
|
|
165
|
+
config,
|
|
166
|
+
user: null,
|
|
167
|
+
error: null,
|
|
168
|
+
success: null
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export default VerifyEmailPage;
|