@flaredev/cli 0.12.1 → 0.13.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/dist/index.js +114 -25
- package/package.json +2 -2
- package/templates/app/README.md +1 -1
- package/templates/app/_gitignore +7 -0
- package/templates/app/app/dashboard/layout.tsx +2 -2
- package/templates/app/app/dashboard/page.tsx +101 -26
- package/templates/app/app/dashboard/users/actions.ts +86 -0
- package/templates/app/app/dashboard/users/page.tsx +54 -0
- package/templates/app/components/dashboard/dashboard-header.tsx +2 -2
- package/templates/app/components/dashboard/dashboard-sidebar.tsx +13 -10
- package/templates/app/components/dashboard/user-actions.tsx +171 -0
- package/templates/app/components/dashboard/user-menu.tsx +17 -23
- package/templates/app/components/dashboard/users-table.tsx +109 -0
- package/templates/app/lib/api-docs.ts +1 -1
- package/templates/app/lib/auth.ts +3 -3
- package/templates/app/lib/dashboard.ts +5 -1
- package/templates/app/lib/mail.ts +2 -2
- package/templates/app/lib/site.ts +2 -2
- package/templates/app/vite.config.ts +3 -1
- package/templates/locks/next/package-lock.json +92 -91
- package/templates/locks/next/pnpm-lock.yaml +84 -93
- package/templates/locks/next/yarn.lock +83 -95
- package/templates/next/README.md +1 -1
- package/templates/next/app/dashboard/layout.tsx +2 -2
- package/templates/next/app/dashboard/observability/page.tsx +156 -0
- package/templates/next/app/dashboard/page.tsx +87 -23
- package/templates/next/app/dashboard/users/page.tsx +51 -0
- package/templates/next/lib/auth.ts +3 -3
- package/templates/next/lib/dashboard-nav.ts +29 -0
- package/templates/next/lib/dashboard.ts +5 -1
- package/templates/next/lib/mail.ts +2 -2
- package/templates/next/prisma/schema/base.prisma +6 -0
- package/templates/next/tsconfig.json +2 -2
- /package/templates/app/app/{forgot-password → (auth)/forgot-password}/page.tsx +0 -0
- /package/templates/app/app/{invite → (auth)/invite}/[token]/route.ts +0 -0
- /package/templates/app/app/{reset-password → (auth)/reset-password}/page.tsx +0 -0
- /package/templates/app/app/{sign-in → (auth)/sign-in}/page.tsx +0 -0
- /package/templates/app/app/{sign-up → (auth)/sign-up}/page.tsx +0 -0
- /package/templates/app/app/{two-factor → (auth)/two-factor}/page.tsx +0 -0
- /package/templates/app/app/{verify-email → (auth)/verify-email}/page.tsx +0 -0
|
@@ -1343,42 +1343,42 @@
|
|
|
1343
1343
|
tslib "^2.8.1"
|
|
1344
1344
|
tsyringe "^4.10.0"
|
|
1345
1345
|
|
|
1346
|
-
"@prisma/adapter-pg@7.10.
|
|
1347
|
-
version "7.10.
|
|
1348
|
-
resolved "https://registry.yarnpkg.com/@prisma/adapter-pg/-/adapter-pg-7.10.
|
|
1349
|
-
integrity sha512-
|
|
1346
|
+
"@prisma/adapter-pg@7.10.1":
|
|
1347
|
+
version "7.10.1"
|
|
1348
|
+
resolved "https://registry.yarnpkg.com/@prisma/adapter-pg/-/adapter-pg-7.10.1.tgz#7cfcbaa00bb620bc4530df80220de7b5795b90d3"
|
|
1349
|
+
integrity sha512-EP9ruPYHjKP80WZlb5qVaEXKgZjJieBFjAxcBtQnDAHD6+16UN4keVmBh6L/RUpuDQT2FQRbvKNgq5d4Ix+IkQ==
|
|
1350
1350
|
dependencies:
|
|
1351
|
-
"@prisma/driver-adapter-utils" "7.10.
|
|
1351
|
+
"@prisma/driver-adapter-utils" "7.10.1"
|
|
1352
1352
|
"@types/pg" "^8.16.0"
|
|
1353
1353
|
pg "^8.16.3"
|
|
1354
1354
|
postgres-array "3.0.4"
|
|
1355
1355
|
|
|
1356
|
-
"@prisma/client-runtime-utils@7.10.
|
|
1357
|
-
version "7.10.
|
|
1358
|
-
resolved "https://registry.yarnpkg.com/@prisma/client-runtime-utils/-/client-runtime-utils-7.10.
|
|
1359
|
-
integrity sha512-
|
|
1356
|
+
"@prisma/client-runtime-utils@7.10.1":
|
|
1357
|
+
version "7.10.1"
|
|
1358
|
+
resolved "https://registry.yarnpkg.com/@prisma/client-runtime-utils/-/client-runtime-utils-7.10.1.tgz#af603fe5cbcd44e381772a97fedbb1ee1d363a58"
|
|
1359
|
+
integrity sha512-AzoATVIgmO3lcZfM443+9SI7+cyLMW2+Psw4op8WQHpHvHCX//CKxv8fwhiL0yGWF8DjdQrFewxrZo5McBdB8g==
|
|
1360
1360
|
|
|
1361
|
-
"@prisma/client@7.10.
|
|
1362
|
-
version "7.10.
|
|
1363
|
-
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-7.10.
|
|
1364
|
-
integrity sha512-
|
|
1361
|
+
"@prisma/client@7.10.1":
|
|
1362
|
+
version "7.10.1"
|
|
1363
|
+
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-7.10.1.tgz#c3a6acd14b79ccd788fc93e150df26bae4be5c83"
|
|
1364
|
+
integrity sha512-5tgAbFGh//zVOC6WPNVgziOErJHYkUE+wmqCwbIyq6IrTXUYtv9y2iBFGh/Owo99Mx0+cr0j1FOrxkYBMuyhaQ==
|
|
1365
1365
|
dependencies:
|
|
1366
|
-
"@prisma/client-runtime-utils" "7.10.
|
|
1366
|
+
"@prisma/client-runtime-utils" "7.10.1"
|
|
1367
1367
|
|
|
1368
|
-
"@prisma/config@7.10.
|
|
1369
|
-
version "7.10.
|
|
1370
|
-
resolved "https://registry.yarnpkg.com/@prisma/config/-/config-7.10.
|
|
1371
|
-
integrity sha512
|
|
1368
|
+
"@prisma/config@7.10.1":
|
|
1369
|
+
version "7.10.1"
|
|
1370
|
+
resolved "https://registry.yarnpkg.com/@prisma/config/-/config-7.10.1.tgz#769d708a197eefbc6dd2769d7251d5f8a23a3f19"
|
|
1371
|
+
integrity sha512-+0swF5ELUa3P/9cIR26+gpYjjCbGE0qvEBZhV4zMDkYsrtHjZ884MQbOnWFWXj600N0sSJNnHbtBqe12yDngWg==
|
|
1372
1372
|
dependencies:
|
|
1373
1373
|
c12 "3.3.4"
|
|
1374
|
-
deepmerge-ts "
|
|
1374
|
+
deepmerge-ts "8.0.2"
|
|
1375
1375
|
effect "3.20.0"
|
|
1376
1376
|
empathic "2.0.0"
|
|
1377
1377
|
|
|
1378
|
-
"@prisma/debug@7.10.
|
|
1379
|
-
version "7.10.
|
|
1380
|
-
resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-7.10.
|
|
1381
|
-
integrity sha512-
|
|
1378
|
+
"@prisma/debug@7.10.1":
|
|
1379
|
+
version "7.10.1"
|
|
1380
|
+
resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-7.10.1.tgz#6ef089971593d98668e762c5810ea335461773e8"
|
|
1381
|
+
integrity sha512-eFCAkGZ13D/qPg48JBlG5IWi9cSBBOC7oG3abDPcAkYioDWlIjh0YJJLK2J7qo5S+xWWD8I6egAN/8KbCaiMCA==
|
|
1382
1382
|
|
|
1383
1383
|
"@prisma/debug@7.2.0":
|
|
1384
1384
|
version "7.2.0"
|
|
@@ -1406,43 +1406,43 @@
|
|
|
1406
1406
|
valibot "1.4.2"
|
|
1407
1407
|
zeptomatch "2.1.0"
|
|
1408
1408
|
|
|
1409
|
-
"@prisma/driver-adapter-utils@7.10.
|
|
1410
|
-
version "7.10.
|
|
1411
|
-
resolved "https://registry.yarnpkg.com/@prisma/driver-adapter-utils/-/driver-adapter-utils-7.10.
|
|
1412
|
-
integrity sha512-
|
|
1409
|
+
"@prisma/driver-adapter-utils@7.10.1":
|
|
1410
|
+
version "7.10.1"
|
|
1411
|
+
resolved "https://registry.yarnpkg.com/@prisma/driver-adapter-utils/-/driver-adapter-utils-7.10.1.tgz#72c9bc9f5a7489e15ca7aefd1c195643924d42a3"
|
|
1412
|
+
integrity sha512-DJ4c6xvVnm3s9ZUyql9nCUicADSqyvH+CjXRCM9L6m5zJxCNmebCElG6X4irICq3rosBwlX+S19Dxq/4IrZk3A==
|
|
1413
1413
|
dependencies:
|
|
1414
|
-
"@prisma/debug" "7.10.
|
|
1414
|
+
"@prisma/debug" "7.10.1"
|
|
1415
1415
|
|
|
1416
|
-
"@prisma/engines-version@
|
|
1417
|
-
version "
|
|
1418
|
-
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-
|
|
1419
|
-
integrity sha512-
|
|
1416
|
+
"@prisma/engines-version@8.1.0-1.d1b3f377ca835d73fd50954a5e22128b9ebdd6b8":
|
|
1417
|
+
version "8.1.0-1.d1b3f377ca835d73fd50954a5e22128b9ebdd6b8"
|
|
1418
|
+
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-8.1.0-1.d1b3f377ca835d73fd50954a5e22128b9ebdd6b8.tgz#2f8c62e2799a4cd36f2d65b0db5452e9667461ad"
|
|
1419
|
+
integrity sha512-XjXmwjN2NE340RpXoXv8ZF8aNAvr0FE+7gNfkKeW6vyjXs7F8uxvEbWQoVrhioXDQoIsnNmv7r+wxvzfdEZaWA==
|
|
1420
1420
|
|
|
1421
|
-
"@prisma/engines@7.10.
|
|
1422
|
-
version "7.10.
|
|
1423
|
-
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-7.10.
|
|
1424
|
-
integrity sha512-
|
|
1421
|
+
"@prisma/engines@7.10.1":
|
|
1422
|
+
version "7.10.1"
|
|
1423
|
+
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-7.10.1.tgz#ed58180bb8b3eeb42bac83e33bec61c54bbdd081"
|
|
1424
|
+
integrity sha512-R6/ghv1Bu36FhCdQLFof1pNtyoWoeDzJLYeTKjgqb4eGtOAJx7ZqJtcm4pzkahLfZ9rYz8AkmsMDwkyiYbXPCQ==
|
|
1425
1425
|
dependencies:
|
|
1426
|
-
"@prisma/debug" "7.10.
|
|
1427
|
-
"@prisma/engines-version" "
|
|
1428
|
-
"@prisma/fetch-engine" "7.10.
|
|
1429
|
-
"@prisma/get-platform" "7.10.
|
|
1426
|
+
"@prisma/debug" "7.10.1"
|
|
1427
|
+
"@prisma/engines-version" "8.1.0-1.d1b3f377ca835d73fd50954a5e22128b9ebdd6b8"
|
|
1428
|
+
"@prisma/fetch-engine" "7.10.1"
|
|
1429
|
+
"@prisma/get-platform" "7.10.1"
|
|
1430
1430
|
|
|
1431
|
-
"@prisma/fetch-engine@7.10.
|
|
1432
|
-
version "7.10.
|
|
1433
|
-
resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-7.10.
|
|
1434
|
-
integrity sha512-
|
|
1431
|
+
"@prisma/fetch-engine@7.10.1":
|
|
1432
|
+
version "7.10.1"
|
|
1433
|
+
resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-7.10.1.tgz#3100866a89302bc58784c0e8baba97a20b808018"
|
|
1434
|
+
integrity sha512-qZezeaExLWG2ptbHOrMxdiWluKHgqEclTdJbvY6pk6+Wg7EiVkeq5U3cXz9ti8nd0bngNvsDgledLHVRg5LF1w==
|
|
1435
1435
|
dependencies:
|
|
1436
|
-
"@prisma/debug" "7.10.
|
|
1437
|
-
"@prisma/engines-version" "
|
|
1438
|
-
"@prisma/get-platform" "7.10.
|
|
1436
|
+
"@prisma/debug" "7.10.1"
|
|
1437
|
+
"@prisma/engines-version" "8.1.0-1.d1b3f377ca835d73fd50954a5e22128b9ebdd6b8"
|
|
1438
|
+
"@prisma/get-platform" "7.10.1"
|
|
1439
1439
|
|
|
1440
|
-
"@prisma/get-platform@7.10.
|
|
1441
|
-
version "7.10.
|
|
1442
|
-
resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-7.10.
|
|
1443
|
-
integrity sha512-
|
|
1440
|
+
"@prisma/get-platform@7.10.1":
|
|
1441
|
+
version "7.10.1"
|
|
1442
|
+
resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-7.10.1.tgz#536225b4e7b9c6e93678ef45047fb47536dbf767"
|
|
1443
|
+
integrity sha512-iVnghHne5vcaobBjPVCMEjh5r/1UCj394EIGTUclasFRatpR/MQwcM2veLv5kgoFSyYP54nch/lNFCQRYcDxvg==
|
|
1444
1444
|
dependencies:
|
|
1445
|
-
"@prisma/debug" "7.10.
|
|
1445
|
+
"@prisma/debug" "7.10.1"
|
|
1446
1446
|
|
|
1447
1447
|
"@prisma/get-platform@7.2.0":
|
|
1448
1448
|
version "7.2.0"
|
|
@@ -3226,7 +3226,7 @@ async-sema@^3.1.1:
|
|
|
3226
3226
|
resolved "https://registry.yarnpkg.com/async-sema/-/async-sema-3.1.1.tgz#e527c08758a0f8f6f9f15f799a173ff3c40ea808"
|
|
3227
3227
|
integrity sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==
|
|
3228
3228
|
|
|
3229
|
-
aws-ssl-profiles@^1.1.
|
|
3229
|
+
aws-ssl-profiles@^1.1.2:
|
|
3230
3230
|
version "1.1.2"
|
|
3231
3231
|
resolved "https://registry.yarnpkg.com/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz#157dd77e9f19b1d123678e93f120e6f193022641"
|
|
3232
3232
|
integrity sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==
|
|
@@ -3610,10 +3610,10 @@ debug@4.3.4:
|
|
|
3610
3610
|
dependencies:
|
|
3611
3611
|
ms "2.1.2"
|
|
3612
3612
|
|
|
3613
|
-
deepmerge-ts@
|
|
3614
|
-
version "
|
|
3615
|
-
resolved "https://registry.yarnpkg.com/deepmerge-ts/-/deepmerge-ts-
|
|
3616
|
-
integrity sha512-
|
|
3613
|
+
deepmerge-ts@8.0.2:
|
|
3614
|
+
version "8.0.2"
|
|
3615
|
+
resolved "https://registry.yarnpkg.com/deepmerge-ts/-/deepmerge-ts-8.0.2.tgz#4f3e5588c3a7c59ea80d54b583948d6f404cf13e"
|
|
3616
|
+
integrity sha512-uqbvqLUMrc6p0MO+WBRtTxY55hmyh94WRwI5a++PZe54X+bfVh59FSN7uWCBCW1CCVjzjnrwzfI8zidE2obMMw==
|
|
3617
3617
|
|
|
3618
3618
|
define-lazy-prop@^2.0.0:
|
|
3619
3619
|
version "2.0.0"
|
|
@@ -3632,11 +3632,6 @@ delaunator@5:
|
|
|
3632
3632
|
dependencies:
|
|
3633
3633
|
robust-predicates "^3.0.2"
|
|
3634
3634
|
|
|
3635
|
-
denque@^2.1.0:
|
|
3636
|
-
version "2.1.0"
|
|
3637
|
-
resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1"
|
|
3638
|
-
integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==
|
|
3639
|
-
|
|
3640
3635
|
depd@~1.1.2:
|
|
3641
3636
|
version "1.1.2"
|
|
3642
3637
|
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
|
|
@@ -4115,7 +4110,7 @@ iconv-lite@0.4.24:
|
|
|
4115
4110
|
dependencies:
|
|
4116
4111
|
safer-buffer ">= 2.1.2 < 3"
|
|
4117
4112
|
|
|
4118
|
-
iconv-lite@^0.7.
|
|
4113
|
+
iconv-lite@^0.7.3:
|
|
4119
4114
|
version "0.7.3"
|
|
4120
4115
|
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.7.3.tgz#84ee12f963e7de50bc01a13e160a078b3b0f415f"
|
|
4121
4116
|
integrity sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==
|
|
@@ -4334,7 +4329,7 @@ lodash@^4.17.21:
|
|
|
4334
4329
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.18.1.tgz#ff2b66c1f6326d59513de2407bf881439812771c"
|
|
4335
4330
|
integrity sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==
|
|
4336
4331
|
|
|
4337
|
-
long@^5.2
|
|
4332
|
+
long@^5.3.2:
|
|
4338
4333
|
version "5.3.2"
|
|
4339
4334
|
resolved "https://registry.yarnpkg.com/long/-/long-5.3.2.tgz#1d84463095999262d7d7b7f8bfd4a8cc55167f83"
|
|
4340
4335
|
integrity sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==
|
|
@@ -4356,7 +4351,7 @@ lru-cache@^6.0.0:
|
|
|
4356
4351
|
dependencies:
|
|
4357
4352
|
yallist "^4.0.0"
|
|
4358
4353
|
|
|
4359
|
-
lru.min@^1.
|
|
4354
|
+
lru.min@^1.1.0, lru.min@^1.1.4:
|
|
4360
4355
|
version "1.1.5"
|
|
4361
4356
|
resolved "https://registry.yarnpkg.com/lru.min/-/lru.min-1.1.5.tgz#bd32483f4e342261d0eb011b06d2219e6b5ef818"
|
|
4362
4357
|
integrity sha512-5J9ysMYUpYIg9RF2vJpy9SinEmSviFSe0GyPpCQ4L5QSkLAgeLXlTAOu2ZwWUU5m+0SBl6gUU1R1ZQB3aKypfA==
|
|
@@ -4480,22 +4475,20 @@ ms@2.1.3, ms@^2.1.3:
|
|
|
4480
4475
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
|
4481
4476
|
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
|
4482
4477
|
|
|
4483
|
-
mysql2@3.
|
|
4484
|
-
version "3.
|
|
4485
|
-
resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-3.
|
|
4486
|
-
integrity sha512-
|
|
4478
|
+
mysql2@3.24.4:
|
|
4479
|
+
version "3.24.4"
|
|
4480
|
+
resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-3.24.4.tgz#7752de0cf10b1d2984103bdbc60f696c60756f31"
|
|
4481
|
+
integrity sha512-A2olluVlj0mvgyIRRISMEzXc51m+21mRtcMVjJyIpt2GG98+XrC9m9HzsqcMsX2LcnfccJvY5NB22g8fENBnOA==
|
|
4487
4482
|
dependencies:
|
|
4488
|
-
aws-ssl-profiles "^1.1.
|
|
4489
|
-
denque "^2.1.0"
|
|
4483
|
+
aws-ssl-profiles "^1.1.2"
|
|
4490
4484
|
generate-function "^2.3.1"
|
|
4491
|
-
iconv-lite "^0.7.
|
|
4492
|
-
long "^5.2
|
|
4493
|
-
lru.min "^1.
|
|
4494
|
-
named-placeholders "^1.1.
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
named-placeholders@^1.1.3:
|
|
4485
|
+
iconv-lite "^0.7.3"
|
|
4486
|
+
long "^5.3.2"
|
|
4487
|
+
lru.min "^1.1.4"
|
|
4488
|
+
named-placeholders "^1.1.6"
|
|
4489
|
+
sql-escaper "^1.5.1"
|
|
4490
|
+
|
|
4491
|
+
named-placeholders@^1.1.6:
|
|
4499
4492
|
version "1.1.6"
|
|
4500
4493
|
resolved "https://registry.yarnpkg.com/named-placeholders/-/named-placeholders-1.1.6.tgz#c50c6920b43f258f59c16add1e56654f5cc02bb5"
|
|
4501
4494
|
integrity sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==
|
|
@@ -4873,16 +4866,16 @@ pretty-ms@7.0.1:
|
|
|
4873
4866
|
dependencies:
|
|
4874
4867
|
parse-ms "^2.1.0"
|
|
4875
4868
|
|
|
4876
|
-
prisma@7.10.
|
|
4877
|
-
version "7.10.
|
|
4878
|
-
resolved "https://registry.yarnpkg.com/prisma/-/prisma-7.10.
|
|
4879
|
-
integrity sha512-
|
|
4869
|
+
prisma@7.10.1:
|
|
4870
|
+
version "7.10.1"
|
|
4871
|
+
resolved "https://registry.yarnpkg.com/prisma/-/prisma-7.10.1.tgz#d4051bf0b65463509e641dbaa73c4dc076cc9f5b"
|
|
4872
|
+
integrity sha512-GoVeErHGGtsSjsflQ1r2BA3RKTXtxNraQJz8OLkLhoLr7OgCscdx4qw4WMM4HnERYc+a1cq2VnMNbep+vOjNcQ==
|
|
4880
4873
|
dependencies:
|
|
4881
|
-
"@prisma/config" "7.10.
|
|
4874
|
+
"@prisma/config" "7.10.1"
|
|
4882
4875
|
"@prisma/dev" "0.24.17"
|
|
4883
|
-
"@prisma/engines" "7.10.
|
|
4876
|
+
"@prisma/engines" "7.10.1"
|
|
4884
4877
|
"@prisma/studio-core" "0.33.0"
|
|
4885
|
-
mysql2 "3.
|
|
4878
|
+
mysql2 "3.24.4"
|
|
4886
4879
|
postgres "3.4.7"
|
|
4887
4880
|
|
|
4888
4881
|
promisepipe@3.0.0:
|
|
@@ -5204,11 +5197,6 @@ semver@^7.5.3, semver@^7.8.5:
|
|
|
5204
5197
|
resolved "https://registry.yarnpkg.com/semver/-/semver-7.8.6.tgz#67c0cda133a3ceb6f70b803e67625bf969000424"
|
|
5205
5198
|
integrity sha512-9lpSSU4Mly3mrtruwLs1eLK6+gzbzqeBFwRNjIjE+9LuDpWdYctrQlQ3ej1Ilgi7gknpsTRnPoLP8DpCHqHIJw==
|
|
5206
5199
|
|
|
5207
|
-
seq-queue@^0.0.5:
|
|
5208
|
-
version "0.0.5"
|
|
5209
|
-
resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e"
|
|
5210
|
-
integrity sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==
|
|
5211
|
-
|
|
5212
5200
|
set-cookie-parser@^3.1.2:
|
|
5213
5201
|
version "3.2.2"
|
|
5214
5202
|
resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-3.2.2.tgz#e1aded19f953adcc2a7bc2904837c32067df46a8"
|
|
@@ -5301,10 +5289,10 @@ split2@^4.1.0:
|
|
|
5301
5289
|
resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4"
|
|
5302
5290
|
integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==
|
|
5303
5291
|
|
|
5304
|
-
|
|
5305
|
-
version "
|
|
5306
|
-
resolved "https://registry.yarnpkg.com/
|
|
5307
|
-
integrity sha512-
|
|
5292
|
+
sql-escaper@^1.5.1:
|
|
5293
|
+
version "1.5.2"
|
|
5294
|
+
resolved "https://registry.yarnpkg.com/sql-escaper/-/sql-escaper-1.5.2.tgz#5b59840dc4034bd23a9b7d00501ba947a90364b7"
|
|
5295
|
+
integrity sha512-6CKD38c31SENivxOADeMNLdukOnUxUcflKtzVWzace7Riv1v7cAEym5Cx9Q7gYZ3ezIJI7ZpqecQq8cqNeSSFg==
|
|
5308
5296
|
|
|
5309
5297
|
srvx@0.11.16:
|
|
5310
5298
|
version "0.11.16"
|
package/templates/next/README.md
CHANGED
|
@@ -53,9 +53,9 @@ export default async function DashboardLayout({ children }: { children: React.Re
|
|
|
53
53
|
|
|
54
54
|
return (
|
|
55
55
|
<SidebarProvider defaultOpen={!collapsed}>
|
|
56
|
-
<DashboardSidebar appName="
|
|
56
|
+
<DashboardSidebar appName="__APP_TITLE__" resources={resources} links={dashboardLinks} isAdmin={ADMIN_ROLES.includes(role)} user={account} />
|
|
57
57
|
<SidebarInset>
|
|
58
|
-
<DashboardHeader user={account} initialTheme={theme} notices={notices} />
|
|
58
|
+
<DashboardHeader user={account} initialTheme={theme} notices={notices} isAdmin={ADMIN_ROLES.includes(role)} />
|
|
59
59
|
<main className="flex flex-1 flex-col gap-6 p-4 md:p-6 lg:p-8">{children}</main>
|
|
60
60
|
</SidebarInset>
|
|
61
61
|
<Toaster theme={theme} />
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import Link from "next/link";
|
|
2
|
+
import { DatabaseIcon, HistoryIcon, KeyRoundIcon, UsersIcon } from "lucide-react";
|
|
3
|
+
import { Badge } from "@/components/ui/badge";
|
|
4
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
5
|
+
import { Empty, EmptyDescription, EmptyHeader, EmptyTitle } from "@/components/ui/empty";
|
|
6
|
+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
|
7
|
+
import { PageHeader } from "@/components/dashboard/page-header";
|
|
8
|
+
import { LocalTime } from "@/components/dashboard/local-time";
|
|
9
|
+
import { StatCards, type Stat } from "@/components/dashboard/stat-card";
|
|
10
|
+
import { auditCount, recentAudit } from "@/lib/audit";
|
|
11
|
+
import { recordCount, requireDashboard, resourcePath, visibleResources } from "@/lib/dashboard";
|
|
12
|
+
import { prisma } from "@/lib/db";
|
|
13
|
+
|
|
14
|
+
export const metadata = { title: "Observability" };
|
|
15
|
+
|
|
16
|
+
const ACTION_TONE: Record<string, "secondary" | "destructive" | "outline"> = {
|
|
17
|
+
create: "secondary",
|
|
18
|
+
update: "outline",
|
|
19
|
+
delete: "destructive",
|
|
20
|
+
"bulk-delete": "destructive",
|
|
21
|
+
import: "secondary",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* How the app is behaving, from what this app can actually see: the database, the
|
|
26
|
+
* accounts and the record of who changed what.
|
|
27
|
+
*
|
|
28
|
+
* Request traffic, errors and latency are Vercel's to measure — they happen at their
|
|
29
|
+
* edge, before a byte reaches this code — so the page links there rather than printing
|
|
30
|
+
* three dashes and calling them "not connected".
|
|
31
|
+
*/
|
|
32
|
+
export default async function ObservabilityPage() {
|
|
33
|
+
await requireDashboard("/dashboard/observability");
|
|
34
|
+
|
|
35
|
+
const [resources, changes, audit, people, sessions] = await Promise.all([
|
|
36
|
+
visibleResources(),
|
|
37
|
+
auditCount(7),
|
|
38
|
+
recentAudit(20),
|
|
39
|
+
prisma.user.count(),
|
|
40
|
+
prisma.session.count({ where: { expiresAt: { gt: new Date() } } }),
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
const tables = await Promise.all(resources.map(async (resource) => ({ resource, total: await recordCount(resource.name) })));
|
|
44
|
+
const records = tables.reduce((total, table) => total + table.total, 0);
|
|
45
|
+
|
|
46
|
+
const stats: Stat[] = [
|
|
47
|
+
{ label: "Users", value: people, icon: UsersIcon, href: "/dashboard/users" },
|
|
48
|
+
// Users are records too — the hint counts their table, so the number must as well.
|
|
49
|
+
{ label: "Records", value: records + people, hint: `across ${tables.length + 1} ${tables.length === 0 ? "table" : "tables"}`, icon: DatabaseIcon },
|
|
50
|
+
{ label: "Signed-in sessions", value: sessions, hint: "not yet expired", icon: KeyRoundIcon },
|
|
51
|
+
{ label: "Changes this week", value: changes, hint: "creates, edits and deletes", icon: HistoryIcon },
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<>
|
|
56
|
+
<PageHeader
|
|
57
|
+
title="Observability"
|
|
58
|
+
description="The database, the accounts, and the record of who changed what."
|
|
59
|
+
crumbs={[{ label: "Dashboard", href: "/dashboard" }, { label: "Observability" }]}
|
|
60
|
+
/>
|
|
61
|
+
|
|
62
|
+
<StatCards stats={stats} />
|
|
63
|
+
|
|
64
|
+
<div className="grid gap-6 lg:grid-cols-[minmax(0,1fr)_minmax(0,1.6fr)]">
|
|
65
|
+
<Card className="h-fit">
|
|
66
|
+
<CardHeader>
|
|
67
|
+
<CardTitle className="flex items-center gap-2 text-base">
|
|
68
|
+
<DatabaseIcon className="size-4" />
|
|
69
|
+
Database
|
|
70
|
+
</CardTitle>
|
|
71
|
+
<CardDescription>What each table holds right now.</CardDescription>
|
|
72
|
+
</CardHeader>
|
|
73
|
+
<CardContent className="flex flex-col">
|
|
74
|
+
<div className="flex items-center justify-between gap-3 border-b py-2.5 text-sm last:border-b-0 last:pb-0">
|
|
75
|
+
<Link href="/dashboard/users" className="hover:underline">
|
|
76
|
+
Users
|
|
77
|
+
</Link>
|
|
78
|
+
<span className="tabular-nums text-muted-foreground">{people.toLocaleString()}</span>
|
|
79
|
+
</div>
|
|
80
|
+
{tables.map(({ resource, total }) => (
|
|
81
|
+
<div key={resource.name} className="flex items-center justify-between gap-3 border-b py-2.5 text-sm last:border-b-0 last:pb-0">
|
|
82
|
+
<Link href={resourcePath(resource)} className="hover:underline">
|
|
83
|
+
{resource.pluralLabel}
|
|
84
|
+
</Link>
|
|
85
|
+
<span className="tabular-nums text-muted-foreground">{total.toLocaleString()}</span>
|
|
86
|
+
</div>
|
|
87
|
+
))}
|
|
88
|
+
<p className="mt-4 text-xs text-muted-foreground">
|
|
89
|
+
Request traffic, errors and latency happen at Vercel's edge before they reach this code, so they live in{" "}
|
|
90
|
+
<a href="https://vercel.com/dashboard" target="_blank" rel="noreferrer" className="underline underline-offset-2">
|
|
91
|
+
your Vercel dashboard
|
|
92
|
+
</a>{" "}
|
|
93
|
+
under Observability.
|
|
94
|
+
</p>
|
|
95
|
+
</CardContent>
|
|
96
|
+
</Card>
|
|
97
|
+
|
|
98
|
+
<Card>
|
|
99
|
+
<CardHeader>
|
|
100
|
+
<CardTitle className="flex items-center gap-2 text-base">
|
|
101
|
+
<HistoryIcon className="size-4" />
|
|
102
|
+
Recent changes
|
|
103
|
+
</CardTitle>
|
|
104
|
+
<CardDescription>Every write through the dashboard, newest first.</CardDescription>
|
|
105
|
+
</CardHeader>
|
|
106
|
+
<CardContent>
|
|
107
|
+
{audit.length === 0 ? (
|
|
108
|
+
<Empty>
|
|
109
|
+
<EmptyHeader>
|
|
110
|
+
<EmptyTitle>Nothing yet</EmptyTitle>
|
|
111
|
+
<EmptyDescription>Creates, edits, deletes and imports show up here as they happen.</EmptyDescription>
|
|
112
|
+
</EmptyHeader>
|
|
113
|
+
</Empty>
|
|
114
|
+
) : (
|
|
115
|
+
<div className="overflow-x-auto">
|
|
116
|
+
<Table>
|
|
117
|
+
<TableHeader>
|
|
118
|
+
<TableRow>
|
|
119
|
+
<TableHead>What</TableHead>
|
|
120
|
+
<TableHead>Record</TableHead>
|
|
121
|
+
<TableHead>Who</TableHead>
|
|
122
|
+
<TableHead className="text-right">When</TableHead>
|
|
123
|
+
</TableRow>
|
|
124
|
+
</TableHeader>
|
|
125
|
+
<TableBody>
|
|
126
|
+
{audit.map((entry) => (
|
|
127
|
+
<TableRow key={entry.id}>
|
|
128
|
+
<TableCell>
|
|
129
|
+
<Badge variant={ACTION_TONE[entry.action] ?? "secondary"}>{entry.action}</Badge>
|
|
130
|
+
<span className="ml-2 text-muted-foreground">{entry.resource}</span>
|
|
131
|
+
</TableCell>
|
|
132
|
+
<TableCell className="max-w-56 truncate">{entry.recordLabel ?? describeBulk(entry.changes)}</TableCell>
|
|
133
|
+
<TableCell className="max-w-48 truncate text-muted-foreground">{entry.userEmail ?? "—"}</TableCell>
|
|
134
|
+
<TableCell className="text-right whitespace-nowrap text-muted-foreground tabular-nums">
|
|
135
|
+
<LocalTime value={entry.createdAt} />
|
|
136
|
+
</TableCell>
|
|
137
|
+
</TableRow>
|
|
138
|
+
))}
|
|
139
|
+
</TableBody>
|
|
140
|
+
</Table>
|
|
141
|
+
</div>
|
|
142
|
+
)}
|
|
143
|
+
</CardContent>
|
|
144
|
+
</Card>
|
|
145
|
+
</div>
|
|
146
|
+
</>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Bulk actions have no single record, so say how many they touched. */
|
|
151
|
+
function describeBulk(changes: Record<string, unknown> | null): string {
|
|
152
|
+
if (!changes) return "—";
|
|
153
|
+
if (typeof changes.deleted === "number") return `${changes.deleted} deleted`;
|
|
154
|
+
if (typeof changes.created === "number") return `${changes.created} imported`;
|
|
155
|
+
return "—";
|
|
156
|
+
}
|
|
@@ -1,22 +1,31 @@
|
|
|
1
1
|
import Link from "next/link";
|
|
2
|
-
import { ArrowRightIcon, CheckCircle2Icon, CircleIcon, PlusIcon } from "lucide-react";
|
|
2
|
+
import { ArrowRightIcon, CheckCircle2Icon, CircleIcon, PlusIcon, UsersIcon } from "lucide-react";
|
|
3
3
|
import { Button } from "@/components/ui/button";
|
|
4
4
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
5
|
-
import { Empty, EmptyDescription, EmptyHeader, EmptyTitle } from "@/components/ui/empty";
|
|
6
5
|
import { PageHeader } from "@/components/dashboard/page-header";
|
|
7
6
|
import { resourceIcon } from "@/components/dashboard/resource-icon";
|
|
8
7
|
import { StatCards, type Stat } from "@/components/dashboard/stat-card";
|
|
9
8
|
import { prisma } from "@/lib/db";
|
|
10
9
|
import { authConfig, twoFactorAvailable } from "@/lib/auth-config";
|
|
11
|
-
import { adminPermissions, resourcePath, resourceStats, trend, visibleResources } from "@/lib/dashboard";
|
|
10
|
+
import { ADMIN_ROLES, adminPermissions, dashboardSession, resourcePath, resourceStats, trend, visibleResources } from "@/lib/dashboard";
|
|
12
11
|
import { requireSession } from "@/lib/session";
|
|
13
12
|
|
|
14
13
|
export const metadata = { title: "Dashboard" };
|
|
15
14
|
|
|
15
|
+
/** The three commands that turn an empty app into one with data, with the stack's real steps. */
|
|
16
|
+
const FIRST_RESOURCE_STEPS = [
|
|
17
|
+
{ command: "npx flare gen resource Contact --fields 'name:string, email:email!'", note: "the table, API and admin screens" },
|
|
18
|
+
{ command: "npx flare migrate", note: "create its table" },
|
|
19
|
+
{ command: "npx flare seed:resource Contact 1000", note: "fill it with sample rows" },
|
|
20
|
+
];
|
|
21
|
+
|
|
16
22
|
/** The first thing you see when you sign in: how much data there is, and what's left to set up. */
|
|
17
23
|
export default async function DashboardPage() {
|
|
18
24
|
const { user } = await requireSession("/dashboard");
|
|
25
|
+
const { role } = await dashboardSession();
|
|
26
|
+
const isAdmin = role !== null && ADMIN_ROLES.includes(role);
|
|
19
27
|
const resources = await visibleResources();
|
|
28
|
+
const week = new Date(Date.now() - 7 * 86_400_000);
|
|
20
29
|
|
|
21
30
|
const cards = await Promise.all(
|
|
22
31
|
resources.map(async (resource) => {
|
|
@@ -26,14 +35,33 @@ export default async function DashboardPage() {
|
|
|
26
35
|
}),
|
|
27
36
|
);
|
|
28
37
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
// The one table every app has from its first sign-in. Admin only, because it links to
|
|
39
|
+
// the accounts page, which is.
|
|
40
|
+
const [people, peopleThisWeek] = isAdmin
|
|
41
|
+
? await Promise.all([prisma.user.count(), prisma.user.count({ where: { createdAt: { gte: week } } })])
|
|
42
|
+
: [0, 0];
|
|
43
|
+
|
|
44
|
+
const stats: Stat[] = [
|
|
45
|
+
...(isAdmin
|
|
46
|
+
? [
|
|
47
|
+
{
|
|
48
|
+
label: "Users",
|
|
49
|
+
value: people,
|
|
50
|
+
hint: peopleThisWeek > 0 ? `${peopleThisWeek.toLocaleString()} this week` : "none this week",
|
|
51
|
+
icon: UsersIcon,
|
|
52
|
+
href: "/dashboard/users",
|
|
53
|
+
} satisfies Stat,
|
|
54
|
+
]
|
|
55
|
+
: []),
|
|
56
|
+
...cards.slice(0, isAdmin ? 3 : 4).map(({ resource, total, recent }) => ({
|
|
57
|
+
label: resource.pluralLabel,
|
|
58
|
+
value: total,
|
|
59
|
+
hint: recent.current > 0 ? `${recent.current.toLocaleString()} this week` : "none this week",
|
|
60
|
+
change: trend(recent.current, recent.previous),
|
|
61
|
+
icon: resourceIcon(resource.icon),
|
|
62
|
+
href: resourcePath(resource),
|
|
63
|
+
})),
|
|
64
|
+
];
|
|
37
65
|
|
|
38
66
|
const passkeys = await prisma.passkey.count({ where: { userId: user.id } });
|
|
39
67
|
const twoFactorOn = Boolean((user as { twoFactorEnabled?: boolean | null }).twoFactorEnabled);
|
|
@@ -48,31 +76,67 @@ export default async function DashboardPage() {
|
|
|
48
76
|
<>
|
|
49
77
|
<PageHeader
|
|
50
78
|
title={`Welcome${user.name ? `, ${user.name.split(" ")[0]}` : ""}`}
|
|
51
|
-
description={resources.length > 0 ? "Everything in your app, and how it's moving." : "Your app is running. Add a resource
|
|
79
|
+
description={resources.length > 0 ? "Everything in your app, and how it's moving." : "Your app is running. Add a resource and this page fills in."}
|
|
52
80
|
/>
|
|
53
81
|
|
|
54
82
|
<StatCards stats={stats} />
|
|
55
83
|
|
|
56
84
|
{resources.length === 0 && (
|
|
57
|
-
<
|
|
58
|
-
<
|
|
59
|
-
<
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
</
|
|
64
|
-
</
|
|
65
|
-
|
|
85
|
+
<Card>
|
|
86
|
+
<CardHeader>
|
|
87
|
+
<CardTitle className="text-base">Add your first resource</CardTitle>
|
|
88
|
+
<CardDescription>
|
|
89
|
+
A resource is a table, its API and its admin screens, generated from one line. Three commands from a terminal in this
|
|
90
|
+
app:
|
|
91
|
+
</CardDescription>
|
|
92
|
+
</CardHeader>
|
|
93
|
+
<CardContent className="flex flex-col">
|
|
94
|
+
{FIRST_RESOURCE_STEPS.map((step, index) => (
|
|
95
|
+
<div key={step.command} className="flex flex-wrap items-baseline gap-x-3 gap-y-1 border-b py-3 last:border-b-0 last:pb-0">
|
|
96
|
+
<span className="grid size-6 shrink-0 translate-y-0.5 place-items-center rounded-full bg-muted text-xs font-medium text-muted-foreground">
|
|
97
|
+
{index + 1}
|
|
98
|
+
</span>
|
|
99
|
+
<code className="rounded bg-muted px-2 py-1 font-mono text-[13px]">{step.command}</code>
|
|
100
|
+
<span className="text-xs text-muted-foreground">{step.note}</span>
|
|
101
|
+
</div>
|
|
102
|
+
))}
|
|
103
|
+
<p className="mt-4 text-xs text-muted-foreground">
|
|
104
|
+
Then refresh this page: the sidebar, these cards and the admin screens are already wired to it.
|
|
105
|
+
</p>
|
|
106
|
+
</CardContent>
|
|
107
|
+
</Card>
|
|
66
108
|
)}
|
|
67
109
|
|
|
68
110
|
<div className="grid gap-6 lg:grid-cols-[minmax(0,2fr)_minmax(0,1fr)]">
|
|
69
|
-
{cards.length > 0 && (
|
|
111
|
+
{(cards.length > 0 || isAdmin) && (
|
|
70
112
|
<Card>
|
|
71
113
|
<CardHeader>
|
|
72
114
|
<CardTitle className="text-base">Your data</CardTitle>
|
|
73
|
-
<CardDescription>Every
|
|
115
|
+
<CardDescription>Every table in the app, and who it belongs to.</CardDescription>
|
|
74
116
|
</CardHeader>
|
|
75
117
|
<CardContent className="flex flex-col">
|
|
118
|
+
{isAdmin && (
|
|
119
|
+
<div className="flex items-center gap-3 border-b py-3 last:border-b-0 last:pb-0">
|
|
120
|
+
<span className="grid size-9 shrink-0 place-items-center rounded-md bg-muted text-muted-foreground">
|
|
121
|
+
<UsersIcon className="size-4.5" />
|
|
122
|
+
</span>
|
|
123
|
+
<div className="flex min-w-0 flex-col">
|
|
124
|
+
<Link href="/dashboard/users" className="truncate text-sm font-medium hover:underline">
|
|
125
|
+
Users
|
|
126
|
+
</Link>
|
|
127
|
+
<span className="text-xs text-muted-foreground">
|
|
128
|
+
{people.toLocaleString()} {people === 1 ? "account" : "accounts"}
|
|
129
|
+
</span>
|
|
130
|
+
</div>
|
|
131
|
+
<div className="ml-auto flex items-center gap-1">
|
|
132
|
+
<Button variant="ghost" size="icon" aria-label="Open users" asChild>
|
|
133
|
+
<Link href="/dashboard/users">
|
|
134
|
+
<ArrowRightIcon />
|
|
135
|
+
</Link>
|
|
136
|
+
</Button>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
)}
|
|
76
140
|
{cards.map(({ resource, total, permissions }) => {
|
|
77
141
|
const Icon = resourceIcon(resource.icon);
|
|
78
142
|
return (
|