@owox/idp-better-auth 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 +183 -0
- package/dist/adapters/database.d.ts +29 -0
- package/dist/adapters/database.d.ts.map +1 -0
- package/dist/adapters/database.js +85 -0
- package/dist/auth/auth-config.d.ts +4 -0
- package/dist/auth/auth-config.d.ts.map +1 -0
- package/dist/auth/auth-config.js +72 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/providers/better-auth-provider.d.ts +34 -0
- package/dist/providers/better-auth-provider.d.ts.map +1 -0
- package/dist/providers/better-auth-provider.js +99 -0
- package/dist/services/authentication-service.d.ts +23 -0
- package/dist/services/authentication-service.d.ts.map +1 -0
- package/dist/services/authentication-service.js +199 -0
- package/dist/services/crypto-service.d.ts +18 -0
- package/dist/services/crypto-service.d.ts.map +1 -0
- package/dist/services/crypto-service.js +100 -0
- package/dist/services/database-service.d.ts +14 -0
- package/dist/services/database-service.d.ts.map +1 -0
- package/dist/services/database-service.js +142 -0
- package/dist/services/magic-link-service.d.ts +10 -0
- package/dist/services/magic-link-service.d.ts.map +1 -0
- package/dist/services/magic-link-service.js +42 -0
- package/dist/services/middleware-service.d.ts +15 -0
- package/dist/services/middleware-service.d.ts.map +1 -0
- package/dist/services/middleware-service.js +38 -0
- package/dist/services/page-service.d.ts +23 -0
- package/dist/services/page-service.d.ts.map +1 -0
- package/dist/services/page-service.js +331 -0
- package/dist/services/request-handler-service.d.ts +10 -0
- package/dist/services/request-handler-service.d.ts.map +1 -0
- package/dist/services/request-handler-service.js +50 -0
- package/dist/services/template-service.d.ts +28 -0
- package/dist/services/template-service.d.ts.map +1 -0
- package/dist/services/template-service.js +198 -0
- package/dist/services/token-service.d.ts +15 -0
- package/dist/services/token-service.d.ts.map +1 -0
- package/dist/services/token-service.js +108 -0
- package/dist/services/user-management-service.d.ts +74 -0
- package/dist/services/user-management-service.d.ts.map +1 -0
- package/dist/services/user-management-service.js +396 -0
- package/dist/templates/admin-add-user.html +298 -0
- package/dist/templates/admin-dashboard.html +214 -0
- package/dist/templates/admin-user-details.html +278 -0
- package/dist/templates/password-setup.html +267 -0
- package/dist/templates/password-success.html +197 -0
- package/dist/templates/sign-in.html +201 -0
- package/dist/types/auth-session.d.ts +25 -0
- package/dist/types/auth-session.d.ts.map +1 -0
- package/dist/types/auth-session.js +1 -0
- package/dist/types/database-models.d.ts +54 -0
- package/dist/types/database-models.d.ts.map +1 -0
- package/dist/types/database-models.js +1 -0
- package/dist/types/index.d.ts +45 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/package.json +69 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>User Details - IDP Better Auth</title>
|
|
7
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
8
|
+
|
|
9
|
+
<script>
|
|
10
|
+
tailwind.config = {
|
|
11
|
+
theme: {
|
|
12
|
+
extend: {
|
|
13
|
+
colors: {
|
|
14
|
+
// OWOX Design System Colors
|
|
15
|
+
background: "oklch(1 0 0)",
|
|
16
|
+
foreground: "oklch(0.3346 0.0123 279.25)",
|
|
17
|
+
card: "oklch(1 0 0)",
|
|
18
|
+
'card-foreground': "oklch(0.3346 0.0123 279.25)",
|
|
19
|
+
popover: "oklch(1 0 0)",
|
|
20
|
+
'popover-foreground': "oklch(0.3346 0.0123 279.25)",
|
|
21
|
+
primary: {
|
|
22
|
+
DEFAULT: "oklch(0.6179 0.2295 250.87)",
|
|
23
|
+
hover: "oklch(0.54 0.23 250.87)",
|
|
24
|
+
foreground: "oklch(0.985 0 0)",
|
|
25
|
+
},
|
|
26
|
+
secondary: {
|
|
27
|
+
DEFAULT: "oklch(0.97 0 0)",
|
|
28
|
+
foreground: "oklch(0.205 0 0)",
|
|
29
|
+
},
|
|
30
|
+
muted: {
|
|
31
|
+
DEFAULT: "oklch(0.97 0 0)",
|
|
32
|
+
foreground: "oklch(0.5148 0.0128 274.72)",
|
|
33
|
+
},
|
|
34
|
+
accent: {
|
|
35
|
+
DEFAULT: "oklch(0.97 0 0)",
|
|
36
|
+
foreground: "oklch(0.205 0 0)",
|
|
37
|
+
},
|
|
38
|
+
destructive: {
|
|
39
|
+
DEFAULT: "oklch(0.577 0.245 27.325)",
|
|
40
|
+
foreground: "oklch(0.985 0 0)",
|
|
41
|
+
},
|
|
42
|
+
border: "oklch(0.922 0 0)",
|
|
43
|
+
input: "oklch(0.922 0 0)",
|
|
44
|
+
ring: "oklch(0.708 0 0)",
|
|
45
|
+
success: {
|
|
46
|
+
DEFAULT: "oklch(0.647 0.165 142.495)",
|
|
47
|
+
foreground: "oklch(0.985 0 0)",
|
|
48
|
+
},
|
|
49
|
+
'brand-blue': {
|
|
50
|
+
50: "oklch(0.985 0.03 250.87)",
|
|
51
|
+
100: "oklch(0.955 0.05 250.87)",
|
|
52
|
+
200: "oklch(0.9 0.08 250.87)",
|
|
53
|
+
300: "oklch(0.8 0.14 250.87)",
|
|
54
|
+
400: "oklch(0.7 0.19 250.87)",
|
|
55
|
+
500: "oklch(0.6179 0.2295 250.87)",
|
|
56
|
+
600: "oklch(0.54 0.23 250.87)",
|
|
57
|
+
700: "oklch(0.44 0.2 250.87)",
|
|
58
|
+
800: "oklch(0.36 0.17 250.87)",
|
|
59
|
+
900: "oklch(0.28 0.14 250.87)",
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
</script>
|
|
66
|
+
</head>
|
|
67
|
+
<body class="bg-white min-h-screen">
|
|
68
|
+
<!-- Header -->
|
|
69
|
+
<header class="bg-white shadow-sm border-b border-border">
|
|
70
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
71
|
+
<div class="flex justify-between items-center h-16">
|
|
72
|
+
<div class="flex items-center space-x-4">
|
|
73
|
+
<a href="/auth/dashboard" class="text-muted-foreground hover:text-card-foreground">
|
|
74
|
+
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
75
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
|
|
76
|
+
</svg>
|
|
77
|
+
</a>
|
|
78
|
+
<h1 class="text-xl font-semibold text-foreground">User Details</h1>
|
|
79
|
+
</div>
|
|
80
|
+
<div class="flex items-center space-x-4">
|
|
81
|
+
<a href="/" class="text-sm text-primary hover:text-primary/80 transition-colors">
|
|
82
|
+
<svg class="inline w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
83
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path>
|
|
84
|
+
</svg>
|
|
85
|
+
Back to Site
|
|
86
|
+
</a>
|
|
87
|
+
<a href="/auth/sign-out" class="text-sm text-destructive hover:text-destructive/80 transition-colors">Sign Out</a>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
</header>
|
|
92
|
+
|
|
93
|
+
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
|
94
|
+
<!-- User Profile Card -->
|
|
95
|
+
<div class="bg-white shadow overflow-hidden sm:rounded-lg mb-8 border border-border">
|
|
96
|
+
<div class="px-4 py-5 sm:px-6 flex justify-between items-center">
|
|
97
|
+
<div>
|
|
98
|
+
<h3 class="text-lg leading-6 font-medium text-foreground">User Information</h3>
|
|
99
|
+
<p class="mt-1 max-w-2xl text-sm text-muted-foreground">Personal details and account information</p>
|
|
100
|
+
</div>
|
|
101
|
+
<div class="flex space-x-3">
|
|
102
|
+
<button onclick="generateMagicLink()" class="inline-flex items-center px-3 py-2 border border-input shadow-sm text-sm leading-4 font-medium rounded-md text-foreground bg-white hover:bg-muted/30 transition-colors">
|
|
103
|
+
<svg class="-ml-0.5 mr-2 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
104
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"></path>
|
|
105
|
+
</svg>
|
|
106
|
+
Generate Magic Link
|
|
107
|
+
</button>
|
|
108
|
+
<button onclick="confirmDelete()" class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-primary-foreground bg-destructive hover:bg-destructive/90 transition-colors" {{DELETE_BUTTON_STYLE}}>
|
|
109
|
+
<svg class="-ml-0.5 mr-2 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
110
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
|
|
111
|
+
</svg>
|
|
112
|
+
Delete User
|
|
113
|
+
</button>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
<div class="border-t border-border">
|
|
117
|
+
<dl>
|
|
118
|
+
<div class="bg-muted/20 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
|
119
|
+
<dt class="text-sm font-medium text-muted-foreground">Full name</dt>
|
|
120
|
+
<dd class="mt-1 text-sm text-foreground sm:mt-0 sm:col-span-2">{{USER_NAME}}</dd>
|
|
121
|
+
</div>
|
|
122
|
+
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
|
123
|
+
<dt class="text-sm font-medium text-muted-foreground">Email address</dt>
|
|
124
|
+
<dd class="mt-1 text-sm text-foreground sm:mt-0 sm:col-span-2">{{USER_EMAIL}}</dd>
|
|
125
|
+
</div>
|
|
126
|
+
<div class="bg-muted/20 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
|
127
|
+
<dt class="text-sm font-medium text-muted-foreground">Role</dt>
|
|
128
|
+
<dd class="mt-1 text-sm text-foreground sm:mt-0 sm:col-span-2">
|
|
129
|
+
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium {{ROLE_BADGE_CLASSES}}">
|
|
130
|
+
{{USER_ROLE}}
|
|
131
|
+
</span>
|
|
132
|
+
</dd>
|
|
133
|
+
</div>
|
|
134
|
+
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
|
135
|
+
<dt class="text-sm font-medium text-muted-foreground">User ID</dt>
|
|
136
|
+
<dd class="mt-1 text-sm text-foreground sm:mt-0 sm:col-span-2 font-mono">{{USER_ID}}</dd>
|
|
137
|
+
</div>
|
|
138
|
+
<div class="bg-muted/20 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
|
139
|
+
<dt class="text-sm font-medium text-muted-foreground">Organization</dt>
|
|
140
|
+
<dd class="mt-1 text-sm text-foreground sm:mt-0 sm:col-span-2">{{ORGANIZATION_ID}}</dd>
|
|
141
|
+
</div>
|
|
142
|
+
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
|
143
|
+
<dt class="text-sm font-medium text-muted-foreground">Created</dt>
|
|
144
|
+
<dd class="mt-1 text-sm text-foreground sm:mt-0 sm:col-span-2">{{CREATED_AT}}</dd>
|
|
145
|
+
</div>
|
|
146
|
+
<div class="bg-muted/20 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
|
147
|
+
<dt class="text-sm font-medium text-muted-foreground">Last updated</dt>
|
|
148
|
+
<dd class="mt-1 text-sm text-foreground sm:mt-0 sm:col-span-2">{{UPDATED_AT}}</dd>
|
|
149
|
+
</div>
|
|
150
|
+
</dl>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
<!-- Magic Link Result (hidden by default) -->
|
|
155
|
+
<div id="magic-link-result" class="bg-success/10 border border-success/20 rounded-md p-4 mb-4 hidden">
|
|
156
|
+
<div class="flex">
|
|
157
|
+
<div class="flex-shrink-0">
|
|
158
|
+
<svg class="h-5 w-5 text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
159
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
160
|
+
</svg>
|
|
161
|
+
</div>
|
|
162
|
+
<div class="ml-3 flex-1">
|
|
163
|
+
<h3 class="text-sm font-medium text-success-foreground">Magic Link Generated</h3>
|
|
164
|
+
<div class="mt-2">
|
|
165
|
+
<p class="text-sm text-success">The magic link has been generated successfully:</p>
|
|
166
|
+
<div class="mt-2 flex items-center space-x-2">
|
|
167
|
+
<input id="magic-link-url" type="text" readonly class="flex-1 min-w-0 px-3 py-2 border border-success/30 rounded-md text-sm bg-white font-mono">
|
|
168
|
+
<button onclick="copyMagicLink()" class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-success-foreground bg-success hover:bg-success/90 transition-colors">
|
|
169
|
+
Copy
|
|
170
|
+
</button>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
|
|
178
|
+
<!-- Delete Confirmation Modal -->
|
|
179
|
+
<div id="delete-modal" class="fixed inset-0 bg-black/50 overflow-y-auto h-full w-full hidden">
|
|
180
|
+
<div class="relative top-20 mx-auto p-5 border border-border w-96 shadow-lg rounded-md bg-white">
|
|
181
|
+
<div class="mt-3 text-center">
|
|
182
|
+
<div class="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-destructive/10">
|
|
183
|
+
<svg class="h-6 w-6 text-destructive" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
184
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16c-.77.833.192 2.5 1.732 2.5z"></path>
|
|
185
|
+
</svg>
|
|
186
|
+
</div>
|
|
187
|
+
<h3 class="text-lg font-medium text-foreground mt-5">Delete User</h3>
|
|
188
|
+
<div class="mt-2 px-7 py-3">
|
|
189
|
+
<p class="text-sm text-muted-foreground">Are you sure you want to delete this user? This action cannot be undone.</p>
|
|
190
|
+
</div>
|
|
191
|
+
<div class="items-center px-4 py-3">
|
|
192
|
+
<button id="delete-confirm" onclick="deleteUser()" class="px-4 py-2 bg-destructive text-primary-foreground text-base font-medium rounded-md w-24 mr-2 hover:bg-destructive/90 transition-colors">
|
|
193
|
+
Delete
|
|
194
|
+
</button>
|
|
195
|
+
<button onclick="closeDeleteModal()" class="px-4 py-2 bg-white border border-input text-foreground text-base font-medium rounded-md w-24 hover:bg-muted/30 transition-colors">
|
|
196
|
+
Cancel
|
|
197
|
+
</button>
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
|
|
203
|
+
<script>
|
|
204
|
+
function confirmDelete() {
|
|
205
|
+
document.getElementById('delete-modal').classList.remove('hidden');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function closeDeleteModal() {
|
|
209
|
+
document.getElementById('delete-modal').classList.add('hidden');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function deleteUser() {
|
|
213
|
+
try {
|
|
214
|
+
const response = await fetch('/auth/delete-user/{{USER_ID}}', {
|
|
215
|
+
method: 'POST',
|
|
216
|
+
credentials: 'include'
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
if (response.ok) {
|
|
220
|
+
window.location.href = '/auth/dashboard';
|
|
221
|
+
} else {
|
|
222
|
+
alert('Failed to delete user');
|
|
223
|
+
}
|
|
224
|
+
} catch (error) {
|
|
225
|
+
alert('Error deleting user: ' + error.message);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async function generateMagicLink() {
|
|
230
|
+
try {
|
|
231
|
+
const response = await fetch('/auth/generate-magic-link', {
|
|
232
|
+
method: 'POST',
|
|
233
|
+
headers: {
|
|
234
|
+
'Content-Type': 'application/json',
|
|
235
|
+
},
|
|
236
|
+
credentials: 'include',
|
|
237
|
+
body: JSON.stringify({
|
|
238
|
+
userId: '{{USER_ID}}',
|
|
239
|
+
email: '{{USER_EMAIL}}',
|
|
240
|
+
role: '{{USER_ROLE}}'
|
|
241
|
+
})
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
if (response.ok) {
|
|
245
|
+
const result = await response.json();
|
|
246
|
+
document.getElementById('magic-link-url').value = result.magicLink;
|
|
247
|
+
document.getElementById('magic-link-result').classList.remove('hidden');
|
|
248
|
+
} else {
|
|
249
|
+
alert('Failed to generate magic link');
|
|
250
|
+
}
|
|
251
|
+
} catch (error) {
|
|
252
|
+
alert('Error generating magic link: ' + error.message);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function copyMagicLink() {
|
|
257
|
+
const input = document.getElementById('magic-link-url');
|
|
258
|
+
input.select();
|
|
259
|
+
document.execCommand('copy');
|
|
260
|
+
|
|
261
|
+
// Show feedback
|
|
262
|
+
const button = event.target;
|
|
263
|
+
const originalText = button.textContent;
|
|
264
|
+
button.textContent = 'Copied!';
|
|
265
|
+
setTimeout(() => {
|
|
266
|
+
button.textContent = originalText;
|
|
267
|
+
}, 2000);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Close modal when clicking outside
|
|
271
|
+
document.getElementById('delete-modal').addEventListener('click', function(e) {
|
|
272
|
+
if (e.target === this) {
|
|
273
|
+
closeDeleteModal();
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
</script>
|
|
277
|
+
</body>
|
|
278
|
+
</html>
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Setup Password - IDP Better Auth</title>
|
|
7
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
8
|
+
<script>
|
|
9
|
+
tailwind.config = {
|
|
10
|
+
theme: {
|
|
11
|
+
extend: {
|
|
12
|
+
colors: {
|
|
13
|
+
// OWOX Design System Colors
|
|
14
|
+
background: "oklch(1 0 0)",
|
|
15
|
+
foreground: "oklch(0.3346 0.0123 279.25)",
|
|
16
|
+
card: "oklch(1 0 0)",
|
|
17
|
+
'card-foreground': "oklch(0.3346 0.0123 279.25)",
|
|
18
|
+
primary: {
|
|
19
|
+
DEFAULT: "oklch(0.6179 0.2295 250.87)",
|
|
20
|
+
hover: "oklch(0.54 0.23 250.87)",
|
|
21
|
+
foreground: "oklch(0.985 0 0)",
|
|
22
|
+
},
|
|
23
|
+
secondary: {
|
|
24
|
+
DEFAULT: "oklch(0.97 0 0)",
|
|
25
|
+
foreground: "oklch(0.205 0 0)",
|
|
26
|
+
},
|
|
27
|
+
muted: {
|
|
28
|
+
DEFAULT: "oklch(0.97 0 0)",
|
|
29
|
+
foreground: "oklch(0.5148 0.0128 274.72)",
|
|
30
|
+
},
|
|
31
|
+
destructive: {
|
|
32
|
+
DEFAULT: "oklch(0.577 0.245 27.325)",
|
|
33
|
+
foreground: "oklch(0.985 0 0)",
|
|
34
|
+
},
|
|
35
|
+
border: "oklch(0.922 0 0)",
|
|
36
|
+
input: "oklch(0.922 0 0)",
|
|
37
|
+
ring: "oklch(0.708 0 0)",
|
|
38
|
+
success: {
|
|
39
|
+
DEFAULT: "oklch(0.647 0.165 142.495)",
|
|
40
|
+
foreground: "oklch(0.985 0 0)",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
</script>
|
|
47
|
+
</head>
|
|
48
|
+
<body class="min-h-screen bg-background flex flex-col justify-center py-12 sm:px-6 lg:px-8">
|
|
49
|
+
<!-- Header -->
|
|
50
|
+
<div class="sm:mx-auto sm:w-full sm:max-w-md">
|
|
51
|
+
<div class="flex justify-center">
|
|
52
|
+
<div class="w-12 h-12 bg-success rounded-lg flex items-center justify-center">
|
|
53
|
+
<svg class="w-8 h-8 text-success-foreground" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
54
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"></path>
|
|
55
|
+
</svg>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<!-- Success Message -->
|
|
60
|
+
<div class="mt-4 mx-auto max-w-md">
|
|
61
|
+
<div class="bg-green-50 border border-green-200 rounded-md p-4">
|
|
62
|
+
<div class="flex items-center">
|
|
63
|
+
<svg class="w-5 h-5 text-green-400 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
64
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
65
|
+
</svg>
|
|
66
|
+
<p class="text-sm font-medium text-green-800">Magic link verified successfully!</p>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<h2 class="mt-6 text-center text-3xl font-bold tracking-tight text-gray-900">
|
|
72
|
+
Setup your password
|
|
73
|
+
</h2>
|
|
74
|
+
<p class="mt-2 text-center text-sm text-gray-600">
|
|
75
|
+
Create a secure password to complete your account setup
|
|
76
|
+
</p>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<!-- Setup Form -->
|
|
80
|
+
<div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
|
81
|
+
<div class="bg-white py-8 px-4 shadow-sm rounded-lg sm:px-10 border border-gray-200">
|
|
82
|
+
<form class="space-y-6" action="/auth/set-password" method="post" id="passwordForm">
|
|
83
|
+
<div>
|
|
84
|
+
<label for="password" class="block text-sm font-medium text-gray-700">
|
|
85
|
+
New Password
|
|
86
|
+
</label>
|
|
87
|
+
<div class="mt-1 relative">
|
|
88
|
+
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
89
|
+
<svg class="h-4 w-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
90
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path>
|
|
91
|
+
</svg>
|
|
92
|
+
</div>
|
|
93
|
+
<input
|
|
94
|
+
id="password"
|
|
95
|
+
name="password"
|
|
96
|
+
type="password"
|
|
97
|
+
required
|
|
98
|
+
minlength="6"
|
|
99
|
+
class="appearance-none block w-full pl-10 pr-10 py-2 border border-gray-300 rounded-md placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-primary focus:border-primary sm:text-sm transition-colors"
|
|
100
|
+
placeholder="Enter at least 6 characters"
|
|
101
|
+
onkeyup="validatePassword()"
|
|
102
|
+
>
|
|
103
|
+
<button
|
|
104
|
+
type="button"
|
|
105
|
+
onclick="togglePassword('password')"
|
|
106
|
+
class="absolute inset-y-0 right-0 pr-3 flex items-center"
|
|
107
|
+
>
|
|
108
|
+
<svg id="eye-password" class="h-4 w-4 text-gray-400 hover:text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
109
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
|
110
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
|
|
111
|
+
</svg>
|
|
112
|
+
</button>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
<div>
|
|
117
|
+
<label for="confirmPassword" class="block text-sm font-medium text-gray-700">
|
|
118
|
+
Confirm Password
|
|
119
|
+
</label>
|
|
120
|
+
<div class="mt-1 relative">
|
|
121
|
+
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
122
|
+
<svg class="h-4 w-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
123
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
124
|
+
</svg>
|
|
125
|
+
</div>
|
|
126
|
+
<input
|
|
127
|
+
id="confirmPassword"
|
|
128
|
+
name="confirmPassword"
|
|
129
|
+
type="password"
|
|
130
|
+
required
|
|
131
|
+
minlength="8"
|
|
132
|
+
class="appearance-none block w-full pl-10 pr-10 py-2 border border-gray-300 rounded-md placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-primary focus:border-primary sm:text-sm transition-colors"
|
|
133
|
+
placeholder="Confirm your password"
|
|
134
|
+
onkeyup="validatePassword()"
|
|
135
|
+
>
|
|
136
|
+
<button
|
|
137
|
+
type="button"
|
|
138
|
+
onclick="togglePassword('confirmPassword')"
|
|
139
|
+
class="absolute inset-y-0 right-0 pr-3 flex items-center"
|
|
140
|
+
>
|
|
141
|
+
<svg id="eye-confirm" class="h-4 w-4 text-gray-400 hover:text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
142
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
|
143
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
|
|
144
|
+
</svg>
|
|
145
|
+
</button>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
<!-- Password Requirements -->
|
|
150
|
+
<div class="bg-blue-50 border border-blue-200 rounded-md p-4">
|
|
151
|
+
<div class="flex">
|
|
152
|
+
<svg class="w-4 h-4 text-blue-400 mt-0.5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
153
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
154
|
+
</svg>
|
|
155
|
+
<div>
|
|
156
|
+
<h3 class="text-sm font-medium text-blue-800">Password requirements:</h3>
|
|
157
|
+
<ul class="mt-1 text-sm text-blue-700 space-y-1">
|
|
158
|
+
<li id="req-length" class="flex items-center">
|
|
159
|
+
<svg class="w-3 h-3 mr-1 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
160
|
+
<circle cx="12" cy="12" r="10"></circle>
|
|
161
|
+
</svg>
|
|
162
|
+
At least 6 characters
|
|
163
|
+
</li>
|
|
164
|
+
<li id="req-match" class="flex items-center">
|
|
165
|
+
<svg class="w-3 h-3 mr-1 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
166
|
+
<circle cx="12" cy="12" r="10"></circle>
|
|
167
|
+
</svg>
|
|
168
|
+
Passwords must match
|
|
169
|
+
</li>
|
|
170
|
+
</ul>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
<div>
|
|
176
|
+
<button
|
|
177
|
+
type="submit"
|
|
178
|
+
id="submitBtn"
|
|
179
|
+
class="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-primary hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
|
180
|
+
disabled
|
|
181
|
+
>
|
|
182
|
+
<span class="absolute left-0 inset-y-0 flex items-center pl-3">
|
|
183
|
+
<svg id="submit-icon" class="h-4 w-4 text-primary-foreground group-hover:text-primary-foreground/90" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
184
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1721 9z"></path>
|
|
185
|
+
</svg>
|
|
186
|
+
<svg id="loading-spinner" class="h-4 w-4 text-primary-foreground animate-spin hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
187
|
+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
188
|
+
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
189
|
+
</svg>
|
|
190
|
+
</span>
|
|
191
|
+
<span id="submit-text">Set Password</span>
|
|
192
|
+
</button>
|
|
193
|
+
</div>
|
|
194
|
+
</form>
|
|
195
|
+
|
|
196
|
+
<div class="mt-6 text-center">
|
|
197
|
+
<a href="/" class="text-sm text-primary hover:text-primary/80">
|
|
198
|
+
← Back to main site
|
|
199
|
+
</a>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
<!-- Footer -->
|
|
205
|
+
<div class="mt-8 text-center">
|
|
206
|
+
<p class="text-xs text-gray-500">
|
|
207
|
+
Powered by <strong>OWOX Data Marts</strong>
|
|
208
|
+
</p>
|
|
209
|
+
</div>
|
|
210
|
+
|
|
211
|
+
<script>
|
|
212
|
+
function togglePassword(inputId) {
|
|
213
|
+
const input = document.getElementById(inputId);
|
|
214
|
+
input.type = input.type === 'password' ? 'text' : 'password';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function validatePassword() {
|
|
218
|
+
const password = document.getElementById('password').value;
|
|
219
|
+
const confirmPassword = document.getElementById('confirmPassword').value;
|
|
220
|
+
const submitBtn = document.getElementById('submitBtn');
|
|
221
|
+
const reqLength = document.getElementById('req-length');
|
|
222
|
+
const reqMatch = document.getElementById('req-match');
|
|
223
|
+
|
|
224
|
+
// Check length requirement
|
|
225
|
+
const isLengthValid = password.length >= 6;
|
|
226
|
+
updateRequirement(reqLength, isLengthValid);
|
|
227
|
+
|
|
228
|
+
// Check match requirement
|
|
229
|
+
const isMatchValid = password === confirmPassword && password.length > 0;
|
|
230
|
+
updateRequirement(reqMatch, isMatchValid);
|
|
231
|
+
|
|
232
|
+
// Enable/disable submit button
|
|
233
|
+
submitBtn.disabled = !(isLengthValid && isMatchValid);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function updateRequirement(element, isValid) {
|
|
237
|
+
const icon = element.querySelector('svg');
|
|
238
|
+
if (isValid) {
|
|
239
|
+
element.classList.add('text-green-700');
|
|
240
|
+
element.classList.remove('text-blue-700');
|
|
241
|
+
icon.innerHTML = '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>';
|
|
242
|
+
icon.classList.add('text-green-400');
|
|
243
|
+
icon.classList.remove('text-gray-400');
|
|
244
|
+
} else {
|
|
245
|
+
element.classList.remove('text-green-700');
|
|
246
|
+
element.classList.add('text-blue-700');
|
|
247
|
+
icon.innerHTML = '<circle cx="12" cy="12" r="10"></circle>';
|
|
248
|
+
icon.classList.remove('text-green-400');
|
|
249
|
+
icon.classList.add('text-gray-400');
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Form submission with loading state
|
|
254
|
+
document.getElementById('passwordForm').addEventListener('submit', function() {
|
|
255
|
+
const submitBtn = document.getElementById('submitBtn');
|
|
256
|
+
const submitText = document.getElementById('submit-text');
|
|
257
|
+
const submitIcon = document.getElementById('submit-icon');
|
|
258
|
+
const loadingSpinner = document.getElementById('loading-spinner');
|
|
259
|
+
|
|
260
|
+
submitBtn.disabled = true;
|
|
261
|
+
submitText.textContent = 'Setting password...';
|
|
262
|
+
submitIcon.classList.add('hidden');
|
|
263
|
+
loadingSpinner.classList.remove('hidden');
|
|
264
|
+
});
|
|
265
|
+
</script>
|
|
266
|
+
</body>
|
|
267
|
+
</html>
|