@udhuong/admin-core 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 udhuong
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,407 @@
1
+ # @udhuong/admin-core
2
+
3
+ > Reusable admin core cho Node.js app — auth, RBAC, OAuth, user management. Ship trong **1 package duy nhất** với subpath exports: frontend (Next.js) và backend (NestJS) cùng trong 1 `npm install`.
4
+
5
+ [![npm](https://img.shields.io/npm/v/@udhuong/admin-core.svg)](https://www.npmjs.com/package/@udhuong/admin-core)
6
+ [![license](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
7
+
8
+ ## Tính năng
9
+
10
+ - 🔐 **Auth** — email/password + JWT (access + refresh) + 2FA/TOTP
11
+ - 👥 **User management** — CRUD user, session, preferences (theme/language/notifications)
12
+ - 🛡️ **RBAC dynamic** — core permissions mặc định + consumer extend `extraPermissions`/`extraRoles` qua `forRoot()`
13
+ - 🔗 **OAuth 2.0** — client credentials, authorization code, refresh token; social login Google/GitHub/Facebook
14
+ - 📧 **Mail events** — verification, password reset, welcome (consumer tự cài mail sender)
15
+ - 🎨 **UI ready-to-use** — `<LoginPage />`, `<AdminLayout />`, `<UsersPage />`, `<RolesPage />`, `<PermissionsPage />`… với Tailwind v4 + DaisyUI
16
+ - 🧰 **NestJS module** — `AdminCoreModule.forRoot()` / `forRootAsync()` (global), auto-seed nếu import `OAuthSeederModule`
17
+
18
+ ---
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ npm install @udhuong/admin-core
24
+ ```
25
+
26
+ ### Peer dependencies (optional theo phía bạn dùng)
27
+
28
+ **Client (Next.js):**
29
+ ```bash
30
+ npm install next react react-dom
31
+ npm install --save-dev tailwindcss @tailwindcss/postcss daisyui
32
+ ```
33
+
34
+ **Server (NestJS):**
35
+ ```bash
36
+ npm install @nestjs/common @nestjs/core @nestjs/config @nestjs/event-emitter \
37
+ @nestjs/jwt @nestjs/passport @nestjs/schedule @nestjs/typeorm \
38
+ typeorm pg reflect-metadata rxjs
39
+ ```
40
+
41
+ Tất cả `@nestjs/*` được khai báo là `peerDependenciesMeta.optional`, nên nếu bạn chỉ dùng client thì **không bị warn** về peer deps server (và ngược lại).
42
+
43
+ ---
44
+
45
+ ## Quick start — Client (Next.js 16 App Router)
46
+
47
+ ### 1. Wrap providers
48
+
49
+ ```tsx
50
+ // src/app/providers.tsx
51
+ 'use client'
52
+
53
+ import { CoreProviders } from '@udhuong/admin-core'
54
+
55
+ export function Providers({ children }: { children: React.ReactNode }) {
56
+ return (
57
+ <CoreProviders
58
+ config={{
59
+ apiUrl: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000/api',
60
+ tokenStorageKey: 'admin_token',
61
+ routes: {
62
+ LOGIN: '/login',
63
+ DASHBOARD: '/dashboard',
64
+ UNAUTHORIZED: '/unauthorized',
65
+ CHANGE_PASSWORD: '/change-password',
66
+ },
67
+ }}
68
+ >
69
+ {children}
70
+ </CoreProviders>
71
+ )
72
+ }
73
+ ```
74
+
75
+ `CoreProviders` đã bao gồm `QueryClientProvider`, `ThemeProvider`, `AuthProvider`, `AdminConfigProvider`, `Toaster`. **Không** wrap lại các provider này bên ngoài.
76
+
77
+ ### 2. Setup styles (Tailwind v4 + DaisyUI)
78
+
79
+ Tạo `src/app/globals.css`:
80
+
81
+ ```css
82
+ @import "tailwindcss";
83
+ @source "../../src/**/*.{ts,tsx}";
84
+ @source "../../node_modules/@udhuong/admin-core/dist/client/**/*.{js,d.ts}";
85
+ @plugin "daisyui" {
86
+ themes: light --default, dark --prefersdark;
87
+ }
88
+ ```
89
+
90
+ `postcss.config.js`:
91
+ ```js
92
+ module.exports = {
93
+ plugins: { '@tailwindcss/postcss': {} },
94
+ }
95
+ ```
96
+
97
+ `next.config.ts`:
98
+ ```ts
99
+ export default {
100
+ transpilePackages: ['@udhuong/admin-core'],
101
+ }
102
+ ```
103
+
104
+ Import vào layout:
105
+ ```tsx
106
+ // src/app/layout.tsx
107
+ import './globals.css'
108
+ import { Providers } from './providers'
109
+ ```
110
+
111
+ ### 3. Dùng các trang có sẵn
112
+
113
+ ```tsx
114
+ // src/app/login/page.tsx
115
+ import { LoginPage } from '@udhuong/admin-core'
116
+ export default LoginPage
117
+
118
+ // src/app/(admin)/users/page.tsx
119
+ import { UsersPage } from '@udhuong/admin-core'
120
+ export default UsersPage
121
+ ```
122
+
123
+ Các trang khác: `RolesPage`, `PermissionsPage`, `ProfilePage`, `UserPreferencesPage`, `ChangePasswordPage`, `ForgotPasswordPage`, `ResetPasswordPage`, `VerifyEmailPage`, `UnauthorizedPage`, `UserDetailPage`.
124
+
125
+ ### 4. Dùng hooks
126
+
127
+ ```tsx
128
+ 'use client'
129
+ import { useAuth } from '@udhuong/admin-core'
130
+
131
+ export default function Dashboard() {
132
+ const { user, logout, isAuthenticated } = useAuth()
133
+ if (!isAuthenticated) return <a href="/login">Login</a>
134
+ return <div>Hi {user?.displayName} <button onClick={logout}>Logout</button></div>
135
+ }
136
+ ```
137
+
138
+ ---
139
+
140
+ ## Quick start — Server (NestJS 10)
141
+
142
+ ### 1. Chuẩn bị env vars (BẮT BUỘC)
143
+
144
+ Trước khi boot, cấu hình `.env`:
145
+
146
+ ```bash
147
+ # Root account — password >= 12 ký tự
148
+ ROOT_EMAIL=admin@example.com
149
+ ROOT_PASSWORD=your-strong-root-password
150
+
151
+ # OAuth client secrets — mỗi secret >= 16 ký tự
152
+ # Sinh: openssl rand -base64 32
153
+ OAUTH_WEB_ADMIN_SECRET=<random-32-bytes-base64>
154
+ OAUTH_WEB_PUBLIC_SECRET=<random-32-bytes-base64>
155
+ OAUTH_THIRD_PARTY_DEMO_SECRET=<random-32-bytes-base64>
156
+ OAUTH_API_SERVICE_SECRET=<random-32-bytes-base64>
157
+
158
+ JWT_ACCESS_SECRET=<random-long-string>
159
+ ```
160
+
161
+ ### 2. Wire AdminCoreModule + seeder
162
+
163
+ ```ts
164
+ // src/app.module.ts
165
+ import 'reflect-metadata'
166
+ import { Module } from '@nestjs/common'
167
+ import { ConfigModule, ConfigService } from '@nestjs/config'
168
+ import { EventEmitterModule } from '@nestjs/event-emitter'
169
+ import { TypeOrmModule } from '@nestjs/typeorm'
170
+ import {
171
+ AdminCoreModule,
172
+ OAuthSeederModule,
173
+ PermissionDef,
174
+ RoleDef,
175
+ } from '@udhuong/admin-core/server'
176
+ import { join } from 'path'
177
+
178
+ // Extra permissions + roles riêng của app bạn
179
+ const EXTRA_PERMISSIONS: PermissionDef[] = [
180
+ { name: 'blog.publish', resource: 'blog', action: 'publish', description: 'Publish blog posts' },
181
+ ]
182
+ const EXTRA_ROLES: RoleDef[] = [
183
+ { name: 'editor', description: 'Blog editor', permissions: ['blog.publish', 'user.read'] },
184
+ ]
185
+
186
+ @Module({
187
+ imports: [
188
+ ConfigModule.forRoot({ isGlobal: true }),
189
+ EventEmitterModule.forRoot(),
190
+
191
+ TypeOrmModule.forRoot({
192
+ type: 'postgres',
193
+ host: 'localhost',
194
+ port: 5432,
195
+ username: 'postgres',
196
+ password: 'postgres',
197
+ database: 'myapp',
198
+ autoLoadEntities: true,
199
+ synchronize: true, // Chỉ dev, production dùng migrations
200
+ // Entities từ admin-core
201
+ entities: [
202
+ join(__dirname, '../node_modules/@udhuong/admin-core/dist/server/**/*.entity.js'),
203
+ ],
204
+ }),
205
+
206
+ AdminCoreModule.forRootAsync({
207
+ imports: [ConfigModule],
208
+ inject: [ConfigService],
209
+ useFactory: (config: ConfigService) => ({
210
+ jwt: {
211
+ accessSecret: config.get('JWT_ACCESS_SECRET')!,
212
+ accessExpiresIn: '1h',
213
+ },
214
+ rootEmail: config.get('ROOT_EMAIL')!,
215
+ rootPassword: config.get('ROOT_PASSWORD')!,
216
+ // BẮT BUỘC — seeder sẽ throw nếu thiếu hoặc <16 ký tự
217
+ seeder: {
218
+ clientSecrets: {
219
+ webAdmin: config.get('OAUTH_WEB_ADMIN_SECRET')!,
220
+ webPublic: config.get('OAUTH_WEB_PUBLIC_SECRET')!,
221
+ thirdPartyDemo: config.get('OAUTH_THIRD_PARTY_DEMO_SECRET')!,
222
+ apiService: config.get('OAUTH_API_SERVICE_SECRET')!,
223
+ },
224
+ },
225
+ oauth: {
226
+ google: {
227
+ clientId: config.get('GOOGLE_CLIENT_ID')!,
228
+ clientSecret: config.get('GOOGLE_CLIENT_SECRET')!,
229
+ callbackURL: 'http://localhost:4000/api/auth/google/callback',
230
+ },
231
+ // github, facebook tương tự
232
+ },
233
+ extraPermissions: EXTRA_PERMISSIONS,
234
+ extraRoles: EXTRA_ROLES,
235
+ }),
236
+ }),
237
+
238
+ // Seed permissions + roles + OAuth scopes + clients khi boot
239
+ OAuthSeederModule,
240
+ ],
241
+ })
242
+ export class AppModule {}
243
+ ```
244
+
245
+ ### 2. Enable CORS + bootstrap
246
+
247
+ ```ts
248
+ // src/main.ts
249
+ import 'reflect-metadata'
250
+ import { NestFactory } from '@nestjs/core'
251
+ import { AppModule } from './app.module'
252
+
253
+ async function bootstrap() {
254
+ const app = await NestFactory.create(AppModule)
255
+ app.enableCors({
256
+ origin: process.env.CLIENT_URL || 'http://localhost:3000',
257
+ credentials: true,
258
+ })
259
+ app.setGlobalPrefix('api')
260
+ await app.listen(4000)
261
+ }
262
+ bootstrap()
263
+ ```
264
+
265
+ ### 3. tsconfig.json
266
+
267
+ ```jsonc
268
+ {
269
+ "compilerOptions": {
270
+ "module": "node16",
271
+ "moduleResolution": "node16",
272
+ "experimentalDecorators": true,
273
+ "emitDecoratorMetadata": true,
274
+ "target": "ES2023"
275
+ }
276
+ }
277
+ ```
278
+
279
+ ### 4. Protect endpoint với permission
280
+
281
+ ```ts
282
+ import { Controller, Get, UseGuards } from '@nestjs/common'
283
+ import {
284
+ JwtAuthGuard,
285
+ PermissionGuard,
286
+ RequirePermissions,
287
+ CORE_PERMISSIONS,
288
+ } from '@udhuong/admin-core/server'
289
+
290
+ @Controller('admin/blog')
291
+ @UseGuards(JwtAuthGuard, PermissionGuard)
292
+ export class AdminBlogController {
293
+ @Get()
294
+ @RequirePermissions(CORE_PERMISSIONS.USER_READ.name) // hoặc truyền trực tiếp 'blog.publish'
295
+ list() {
296
+ return []
297
+ }
298
+ }
299
+ ```
300
+
301
+ ---
302
+
303
+ ## Dynamic module API
304
+
305
+ ### `AdminCoreOptions`
306
+
307
+ | Key | Type | Mô tả |
308
+ |-----|------|-------|
309
+ | `jwt.accessSecret` | `string` | JWT signing secret — bắt buộc |
310
+ | `jwt.accessExpiresIn` | `string` | mặc định `'1h'` |
311
+ | `jwt.refreshSecret` | `string` | secret cho refresh token |
312
+ | `jwt.refreshExpiresIn` | `string` | mặc định `'30d'` |
313
+ | `rootEmail` | `string` | Email root account tạo tự động khi boot |
314
+ | `rootPassword` | `string` | Mật khẩu root — **bắt buộc khi root chưa tồn tại**, >= 12 ký tự, đọc từ env `ROOT_PASSWORD`. Seeder throw nếu thiếu |
315
+ | `seeder.clientSecrets.webAdmin` | `string` | Secret cho OAuth client `web-admin` — **bắt buộc**, >= 16 ký tự |
316
+ | `seeder.clientSecrets.webPublic` | `string` | Secret cho `web-public` — **bắt buộc** |
317
+ | `seeder.clientSecrets.thirdPartyDemo` | `string` | Secret cho `third-party-demo` — **bắt buộc** |
318
+ | `seeder.clientSecrets.apiService` | `string` | Secret cho `api-service` — **bắt buộc** |
319
+ | `oauth.google` | `{ clientId, clientSecret, callbackURL }` | Config OAuth Google (optional) |
320
+ | `oauth.github` | tương tự | Config GitHub |
321
+ | `oauth.facebook` | `{ appId, appSecret, callbackURL }` | Config Facebook |
322
+ | `seeder.adminRedirectUris` | `string[]` | Redirect URIs cho OAuth `web-admin` client |
323
+ | `seeder.webRedirectUris` | `string[]` | cho `web-public` |
324
+ | `seeder.mobileRedirectUris` | `string[]` | cho `mobile-app` |
325
+ | `extraPermissions` | `PermissionDef[]` | Permissions domain của bạn, merge vào core khi seed |
326
+ | `extraRoles` | `RoleDef[]` | Roles tuỳ biến, merge vào core. Dùng `permissions: '*'` để gán tất cả |
327
+
328
+ ### Core defaults
329
+
330
+ 11 core permissions built-in (luôn có sau khi seed):
331
+
332
+ - `user.read`, `user.create`, `user.update`, `user.delete`
333
+ - `role.read`, `role.create`, `role.update`, `role.delete`
334
+ - `admin.dashboard`, `admin.settings`, `admin.analytics`
335
+
336
+ 3 core roles:
337
+
338
+ - `super-admin` — tất cả permissions (core + extras)
339
+ - `admin` — 11 core permissions
340
+ - `viewer` — read-only: `user.read`, `role.read`, `admin.dashboard`
341
+
342
+ ---
343
+
344
+ ## Gotchas — lỗi hay gặp
345
+
346
+ ### 1. `Cannot find module '@udhuong/admin-core/server'`
347
+ NestJS tsconfig chưa dùng `moduleResolution: node16`. Đổi cả `module` và `moduleResolution` thành `node16`.
348
+
349
+ ### 2. `Data type "timestamp" is not supported by "better-sqlite3"`
350
+ Entities trong admin-core dùng Postgres-specific types. Dùng `pg` driver.
351
+
352
+ ### 3. CORS bị block ở client
353
+ Server phải `app.enableCors({ origin: CLIENT_URL, credentials: true })`.
354
+
355
+ ### 4. Login trả `Invalid client`
356
+ OAuth clients chưa seed. Import `OAuthSeederModule` vào `AppModule` và đảm bảo đã cấu hình `seeder.clientSecrets`. Login với `clientCode` + `clientSecret` do **bạn tự set qua env var**:
357
+ ```json
358
+ {"email":"...","password":"...","clientCode":"web-admin","clientSecret":"<OAUTH_WEB_ADMIN_SECRET>"}
359
+ ```
360
+
361
+ ### 4a. Seeder throw `Thiếu hoặc quá ngắn (<16 ký tự) client secrets`
362
+ Bạn chưa cấu hình env vars `OAUTH_*_SECRET` hoặc truyền chúng qua `seeder.clientSecrets` trong `forRootAsync`. Sinh secret: `openssl rand -base64 32`. Tuyệt đối không dùng giá trị mẫu trong docs/tests cho production.
363
+
364
+ ### 4b. `RootAccountService` throw `Thiếu hoặc quá ngắn rootPassword`
365
+ Phải cung cấp `rootPassword` qua config hoặc env `ROOT_PASSWORD` (>= 12 ký tự) khi boot lần đầu. Sau khi root account đã tạo, có thể bỏ nếu muốn.
366
+
367
+ ### 5. Tailwind chỉ ship vài KB, UI vỡ
368
+ `@source` trong CSS của package không quét được consumer source. Viết local `globals.css` với `@source` trỏ cả `../src/**/*.{ts,tsx}` và `../node_modules/@udhuong/admin-core/dist/client/**/*.{js,d.ts}`.
369
+
370
+ ### 6. SSR hydration warning với `data-theme`
371
+ Thêm `suppressHydrationWarning` vào `<html>` và `<body>` trong `RootLayout`.
372
+
373
+ ### 7. `UserSettingsPage` không còn
374
+ Đã rename thành `UserPreferencesPage`. `SettingsPage` (system-wide settings) đã bị xoá khỏi package — tự implement theo nhu cầu.
375
+
376
+ ### 8. Next.js cần `transpilePackages`
377
+ Vì package ship raw CSS: `transpilePackages: ['@udhuong/admin-core']` trong `next.config.ts`.
378
+
379
+ ### 9. Consumer chỉ dùng client vẫn bị cảnh báo `@nestjs/*`
380
+ Chạy `npm install` với npm ≥ 7 — peer deps được mark optional qua `peerDependenciesMeta`, không cần cài.
381
+
382
+ ### 10. Permissions fetch từ API trả `[]` cho user root
383
+ Root được tạo không tự assign role. Query DB gán role `super-admin` cho user root sau khi seed, hoặc thêm flow assignRootRole trong bootstrap của bạn.
384
+
385
+ ### 11. Migration chạy tay trên DB production
386
+ Admin-core đã có migration `CreateOAuthSystem` trong `dist/server/auth/infrastructure/database/postgresql/migrations/`. TypeORM CLI:
387
+ ```ts
388
+ migrations: ['node_modules/@udhuong/admin-core/dist/server/**/migrations/*.js']
389
+ ```
390
+
391
+ ---
392
+
393
+ ## Example project
394
+
395
+ Xem full setup client + server tại [admin-core-example](https://github.com/ungdinhhuong/admin-core-example) (sẽ publish riêng), hoặc tham khảo integration-test snapshot trong `/examples` folder của repo.
396
+
397
+ ---
398
+
399
+ ## Support
400
+
401
+ Issues: https://github.com/ungdinhhuong/admin-core/issues
402
+
403
+ ---
404
+
405
+ ## License
406
+
407
+ MIT © 2026 [udhuong](mailto:l19htm4n@gmail.com)
package/package.json ADDED
@@ -0,0 +1,161 @@
1
+ {
2
+ "name": "@udhuong/admin-core",
3
+ "version": "0.1.0",
4
+ "description": "Reusable admin core (client + server) — auth, RBAC, OAuth, user management. Built với NestJS + Next.js, ship trong 1 package với subpath exports.",
5
+ "license": "MIT",
6
+ "author": "udhuong <l19htm4n@gmail.com>",
7
+ "homepage": "https://github.com/ungdinhhuong/admin-core#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/ungdinhhuong/admin-core.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/ungdinhhuong/admin-core/issues"
14
+ },
15
+ "keywords": [
16
+ "admin",
17
+ "admin-panel",
18
+ "nestjs",
19
+ "nextjs",
20
+ "react",
21
+ "auth",
22
+ "authentication",
23
+ "authorization",
24
+ "rbac",
25
+ "oauth",
26
+ "oauth2",
27
+ "jwt",
28
+ "2fa",
29
+ "totp",
30
+ "user-management"
31
+ ],
32
+ "engines": {
33
+ "node": ">=18.0.0"
34
+ },
35
+ "sideEffects": [
36
+ "**/*.css"
37
+ ],
38
+ "files": [
39
+ "dist",
40
+ "src/client/styles/globals.css",
41
+ "README.md",
42
+ "LICENSE"
43
+ ],
44
+ "exports": {
45
+ ".": {
46
+ "types": "./dist/client/index.d.ts",
47
+ "import": "./dist/client/index.js",
48
+ "default": "./dist/client/index.js"
49
+ },
50
+ "./server": {
51
+ "types": "./dist/server/index.d.ts",
52
+ "require": "./dist/server/index.js",
53
+ "default": "./dist/server/index.js"
54
+ },
55
+ "./styles": "./src/client/styles/globals.css",
56
+ "./tailwind-preset": {
57
+ "types": "./dist/tailwind.preset.d.ts",
58
+ "default": "./dist/tailwind.preset.js"
59
+ },
60
+ "./package.json": "./package.json"
61
+ },
62
+ "main": "./dist/client/index.js",
63
+ "types": "./dist/client/index.d.ts",
64
+ "scripts": {
65
+ "build": "npm run build:client && npm run build:server && npm run build:preset",
66
+ "build:client": "tsc -p tsconfig.client.json",
67
+ "build:server": "tsc -p tsconfig.server.json",
68
+ "build:preset": "tsc tailwind.preset.ts --outDir dist --declaration --module commonjs --esModuleInterop --target ES2020 --skipLibCheck",
69
+ "type-check": "tsc -p tsconfig.client.json --noEmit && tsc -p tsconfig.server.json --noEmit",
70
+ "clean": "rm -rf dist"
71
+ },
72
+ "dependencies": {
73
+ "@hookform/resolvers": "^5.2.2",
74
+ "@tanstack/react-query": "^5.90.7",
75
+ "axios": "^1.13.2",
76
+ "bcrypt": "^6.0.0",
77
+ "class-transformer": "^0.5.1",
78
+ "class-validator": "^0.14.1",
79
+ "clsx": "^2.1.1",
80
+ "google-auth-library": "^10.5.0",
81
+ "lucide-react": "^0.552.0",
82
+ "next-themes": "^0.4.6",
83
+ "passport": "^0.7.0",
84
+ "passport-facebook": "^3.0.0",
85
+ "passport-github2": "^0.1.12",
86
+ "passport-google-oauth20": "^2.0.0",
87
+ "passport-jwt": "^4.0.1",
88
+ "passport-local": "^1.0.0",
89
+ "qrcode": "^1.5.4",
90
+ "react-hook-form": "^7.72.1",
91
+ "react-hot-toast": "^2.6.0",
92
+ "speakeasy": "^2.0.0",
93
+ "tailwind-merge": "^3.3.1",
94
+ "zod": "^4.3.6"
95
+ },
96
+ "peerDependencies": {
97
+ "@nestjs/common": "^10.0.0",
98
+ "@nestjs/config": "^3.0.0",
99
+ "@nestjs/core": "^10.0.0",
100
+ "@nestjs/event-emitter": "^3.0.0",
101
+ "@nestjs/jwt": "^11.0.0",
102
+ "@nestjs/passport": "^11.0.0",
103
+ "@nestjs/schedule": "^6.0.0",
104
+ "@nestjs/typeorm": "^10.0.0",
105
+ "next": ">=15.0.0",
106
+ "react": ">=18.0.0",
107
+ "react-dom": ">=18.0.0",
108
+ "reflect-metadata": ">=0.1.0",
109
+ "rxjs": ">=7.0.0",
110
+ "typeorm": "^0.3.0"
111
+ },
112
+ "peerDependenciesMeta": {
113
+ "@nestjs/common": { "optional": true },
114
+ "@nestjs/config": { "optional": true },
115
+ "@nestjs/core": { "optional": true },
116
+ "@nestjs/event-emitter": { "optional": true },
117
+ "@nestjs/jwt": { "optional": true },
118
+ "@nestjs/passport": { "optional": true },
119
+ "@nestjs/schedule": { "optional": true },
120
+ "@nestjs/typeorm": { "optional": true },
121
+ "next": { "optional": true },
122
+ "react": { "optional": true },
123
+ "react-dom": { "optional": true },
124
+ "reflect-metadata": { "optional": true },
125
+ "rxjs": { "optional": true },
126
+ "typeorm": { "optional": true }
127
+ },
128
+ "devDependencies": {
129
+ "@nestjs/common": "^10.4.8",
130
+ "@nestjs/config": "^3.3.0",
131
+ "@nestjs/core": "^10.4.8",
132
+ "@nestjs/event-emitter": "^3.0.1",
133
+ "@nestjs/jwt": "^11.0.1",
134
+ "@nestjs/passport": "^11.0.5",
135
+ "@nestjs/schedule": "^6.1.0",
136
+ "@nestjs/typeorm": "^10.0.2",
137
+ "@types/bcrypt": "^6.0.0",
138
+ "@types/node": "^22",
139
+ "@types/passport-facebook": "^3.0.4",
140
+ "@types/passport-github2": "^1.2.9",
141
+ "@types/passport-google-oauth20": "^2.0.17",
142
+ "@types/passport-jwt": "^4.0.1",
143
+ "@types/passport-local": "^1.0.38",
144
+ "@types/qrcode": "^1.5.6",
145
+ "@types/react": "^19",
146
+ "@types/react-dom": "^19",
147
+ "@types/speakeasy": "^2.0.10",
148
+ "daisyui": "^5.4.7",
149
+ "next": "^16.0.7",
150
+ "react": "^19.0.0",
151
+ "react-dom": "^19.0.0",
152
+ "reflect-metadata": "^0.2.2",
153
+ "rxjs": "^7.8.1",
154
+ "tailwindcss": "^4.0.0",
155
+ "typeorm": "^0.3.27",
156
+ "typescript": "^5.7.2"
157
+ },
158
+ "publishConfig": {
159
+ "access": "public"
160
+ }
161
+ }
@@ -0,0 +1,42 @@
1
+ @import "tailwindcss";
2
+ @source "../";
3
+ @plugin "daisyui";
4
+
5
+
6
+ html {
7
+ transition: color 200ms, background-color 200ms;
8
+ }
9
+
10
+ body {
11
+ margin: 0;
12
+ font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
13
+ "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans",
14
+ "Helvetica Neue", sans-serif;
15
+ }
16
+
17
+ *, ::before, ::after {
18
+ box-sizing: border-box;
19
+ }
20
+
21
+ /* DaisyUI input focus customization */
22
+ .input,
23
+ .textarea,
24
+ .select,
25
+ .input:focus,
26
+ .textarea:focus,
27
+ .select:focus {
28
+ outline: none;
29
+ outline-offset: 0;
30
+ }
31
+
32
+ :root {
33
+ /* Light Theme Colors */
34
+ --color-primary: #10172c;
35
+ --color-secondary: #1f2a44;
36
+ }
37
+
38
+ [data-theme="dark"] {
39
+ /* Dark Theme Colors */
40
+ --color-primary: #4a8eff;
41
+ --color-secondary: #1f3a93;
42
+ }