mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 20:41:09 +02:00
✨ feat(auth): add avatar upload with S3/MinIO and subscription plans seed
- Add StorageModule for avatar uploads via S3/MinIO - Create presigned URL endpoint for direct browser uploads - Create direct upload endpoint (multipart/form-data) - Add manacore-storage bucket to shared-storage package - Add manacore-storage bucket to docker-compose.dev.yml - Create subscription plans seed script (pnpm db:seed:plans) - Plans: Free (150 credits), Pro (2000/€9.99/mo), Enterprise (10000/€49/mo) - Update TODO list with completed tasks
This commit is contained in:
parent
1e025b7e72
commit
c2842e2546
15 changed files with 756 additions and 102 deletions
|
|
@ -91,9 +91,9 @@
|
||||||
- [x] Credits-Seite: Stripe Checkout Integration
|
- [x] Credits-Seite: Stripe Checkout Integration
|
||||||
- [x] Loading-States und Toast-Benachrichtigungen
|
- [x] Loading-States und Toast-Benachrichtigungen
|
||||||
|
|
||||||
**Noch offen:**
|
**Rechnungs-PDFs:**
|
||||||
|
|
||||||
- [ ] Rechnungs-PDF generieren
|
- [x] Stripe Invoice PDFs werden automatisch über Webhooks synchronisiert (`invoicePdfUrl`)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -167,9 +167,9 @@ Archivierte Apps (memoro, storyteller) wurden bereits entfernt.
|
||||||
|
|
||||||
| Feature | Backend | Frontend | Priorität |
|
| Feature | Backend | Frontend | Priorität |
|
||||||
| ----------------- | ------- | -------- | --------- |
|
| ----------------- | ------- | -------- | --------- |
|
||||||
| Profil bearbeiten | ✅ | ❌ | Hoch |
|
| Profil bearbeiten | ✅ | ✅ | Hoch |
|
||||||
| Passwort ändern | ✅ | ❌ | Hoch |
|
| Passwort ändern | ✅ | ✅ | Hoch |
|
||||||
| Konto löschen | ✅ | ❌ | Mittel |
|
| Konto löschen | ✅ | ✅ | Mittel |
|
||||||
| Avatar hochladen | ✅ | ❌ | Niedrig |
|
| Avatar hochladen | ✅ | ❌ | Niedrig |
|
||||||
| 2FA aktivieren | ❌ | ❌ | Niedrig |
|
| 2FA aktivieren | ❌ | ❌ | Niedrig |
|
||||||
|
|
||||||
|
|
@ -186,7 +186,14 @@ Archivierte Apps (memoro, storyteller) wurden bereits entfernt.
|
||||||
- [x] Profil-Edit Modal erstellt (`EditProfileModal.svelte`)
|
- [x] Profil-Edit Modal erstellt (`EditProfileModal.svelte`)
|
||||||
- [x] Passwort-Ändern Dialog erstellt (`ChangePasswordModal.svelte`)
|
- [x] Passwort-Ändern Dialog erstellt (`ChangePasswordModal.svelte`)
|
||||||
- [x] Konto-Löschung mit Bestätigung (`DeleteAccountModal.svelte`)
|
- [x] Konto-Löschung mit Bestätigung (`DeleteAccountModal.svelte`)
|
||||||
- [ ] Avatar-Upload mit S3/MinIO Integration (noch offen)
|
|
||||||
|
**Avatar-Upload (Implementiert 2026-02-13):**
|
||||||
|
|
||||||
|
- [x] Storage-Modul für S3/MinIO (`services/mana-core-auth/src/storage/`)
|
||||||
|
- [x] Presigned URL Endpoint: `POST /api/v1/storage/avatar/upload-url`
|
||||||
|
- [x] Direct Upload Endpoint: `POST /api/v1/storage/avatar`
|
||||||
|
- [x] `manacore-storage` Bucket konfiguriert
|
||||||
|
- [ ] Frontend-Integration (EditProfileModal) noch offen
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -257,7 +264,8 @@ GET /api/v1/subscriptions/invoices # Rechnungen
|
||||||
- [x] Stripe Checkout Integration für Subscriptions
|
- [x] Stripe Checkout Integration für Subscriptions
|
||||||
- [x] Billing Portal Integration
|
- [x] Billing Portal Integration
|
||||||
- [x] Rechnungsübersicht
|
- [x] Rechnungsübersicht
|
||||||
- [ ] Stripe Price IDs in DB eintragen (nach Stripe-Setup)
|
- [x] Subscription-Plans Seed-Script erstellt (`pnpm db:seed:plans`)
|
||||||
|
- [ ] Stripe Products/Prices erstellen und ENV-Variablen setzen
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -406,4 +414,4 @@ Diese Tasks können schnell erledigt werden:
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
_Zuletzt aktualisiert: 2026-02-13 (Profile + Subscription + Credits Frontend)_
|
_Zuletzt aktualisiert: 2026-02-13 (Avatar Storage Backend + Subscription Plans Seed)_
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ services:
|
||||||
entrypoint: >
|
entrypoint: >
|
||||||
/bin/sh -c "
|
/bin/sh -c "
|
||||||
mc alias set myminio http://minio:9000 $${MINIO_ROOT_USER:-minioadmin} $${MINIO_ROOT_PASSWORD:-minioadmin};
|
mc alias set myminio http://minio:9000 $${MINIO_ROOT_USER:-minioadmin} $${MINIO_ROOT_PASSWORD:-minioadmin};
|
||||||
|
mc mb --ignore-existing myminio/manacore-storage;
|
||||||
mc mb --ignore-existing myminio/picture-storage;
|
mc mb --ignore-existing myminio/picture-storage;
|
||||||
mc mb --ignore-existing myminio/chat-storage;
|
mc mb --ignore-existing myminio/chat-storage;
|
||||||
mc mb --ignore-existing myminio/manadeck-storage;
|
mc mb --ignore-existing myminio/manadeck-storage;
|
||||||
|
|
@ -87,6 +88,7 @@ services:
|
||||||
mc mb --ignore-existing myminio/inventory-storage;
|
mc mb --ignore-existing myminio/inventory-storage;
|
||||||
mc mb --ignore-existing myminio/planta-storage;
|
mc mb --ignore-existing myminio/planta-storage;
|
||||||
mc mb --ignore-existing myminio/projectdoc-storage;
|
mc mb --ignore-existing myminio/projectdoc-storage;
|
||||||
|
mc anonymous set download myminio/manacore-storage;
|
||||||
mc anonymous set download myminio/picture-storage;
|
mc anonymous set download myminio/picture-storage;
|
||||||
mc anonymous set download myminio/planta-storage;
|
mc anonymous set download myminio/planta-storage;
|
||||||
mc anonymous set download myminio/inventory-storage;
|
mc anonymous set download myminio/inventory-storage;
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,16 @@ export function createStorageClient(
|
||||||
return new StorageClient(storageConfig, bucketConfig);
|
return new StorageClient(storageConfig, bucketConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a storage client for the Mana Core Auth project (avatars, etc.)
|
||||||
|
*/
|
||||||
|
export function createManaCoreStorage(publicUrl?: string): StorageClient {
|
||||||
|
return createStorageClient({
|
||||||
|
name: BUCKETS.MANACORE,
|
||||||
|
publicUrl: publicUrl ?? process.env.MANACORE_STORAGE_PUBLIC_URL,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a storage client for the Picture project
|
* Create a storage client for the Picture project
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ export { StorageClient } from './client';
|
||||||
export {
|
export {
|
||||||
createStorageClient,
|
createStorageClient,
|
||||||
getStorageConfig,
|
getStorageConfig,
|
||||||
|
createManaCoreStorage,
|
||||||
createPictureStorage,
|
createPictureStorage,
|
||||||
createChatStorage,
|
createChatStorage,
|
||||||
createManaDeckStorage,
|
createManaDeckStorage,
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ export interface FileInfo {
|
||||||
* Predefined bucket names for each project
|
* Predefined bucket names for each project
|
||||||
*/
|
*/
|
||||||
export const BUCKETS = {
|
export const BUCKETS = {
|
||||||
|
MANACORE: 'manacore-storage',
|
||||||
PICTURE: 'picture-storage',
|
PICTURE: 'picture-storage',
|
||||||
CHAT: 'chat-storage',
|
CHAT: 'chat-storage',
|
||||||
MANADECK: 'manadeck-storage',
|
MANADECK: 'manadeck-storage',
|
||||||
|
|
|
||||||
317
pnpm-lock.yaml
generated
317
pnpm-lock.yaml
generated
|
|
@ -196,7 +196,7 @@ importers:
|
||||||
version: 0.5.21
|
version: 0.5.21
|
||||||
ts-jest:
|
ts-jest:
|
||||||
specifier: ^29.2.5
|
specifier: ^29.2.5
|
||||||
version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3)
|
version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.19.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3)
|
||||||
ts-loader:
|
ts-loader:
|
||||||
specifier: ^9.5.1
|
specifier: ^9.5.1
|
||||||
version: 9.5.4(typescript@5.9.3)(webpack@5.100.2)
|
version: 9.5.4(typescript@5.9.3)(webpack@5.100.2)
|
||||||
|
|
@ -2371,7 +2371,7 @@ importers:
|
||||||
version: 0.5.21
|
version: 0.5.21
|
||||||
ts-jest:
|
ts-jest:
|
||||||
specifier: ^29.2.5
|
specifier: ^29.2.5
|
||||||
version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3)
|
version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.19.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3)
|
||||||
ts-loader:
|
ts-loader:
|
||||||
specifier: ^9.5.1
|
specifier: ^9.5.1
|
||||||
version: 9.5.4(typescript@5.9.3)(webpack@5.100.2)
|
version: 9.5.4(typescript@5.9.3)(webpack@5.100.2)
|
||||||
|
|
@ -5656,7 +5656,7 @@ importers:
|
||||||
version: 1.57.0
|
version: 1.57.0
|
||||||
jest:
|
jest:
|
||||||
specifier: ^29.0.0
|
specifier: ^29.0.0
|
||||||
version: 29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3))
|
version: 29.7.0(@types/node@24.10.1)
|
||||||
vitest:
|
vitest:
|
||||||
specifier: ^3.0.0
|
specifier: ^3.0.0
|
||||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)
|
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)
|
||||||
|
|
@ -5803,7 +5803,7 @@ importers:
|
||||||
version: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
|
version: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
|
||||||
ts-jest:
|
ts-jest:
|
||||||
specifier: ^29.2.5
|
specifier: ^29.2.5
|
||||||
version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3)
|
version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.19.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3)
|
||||||
ts-node:
|
ts-node:
|
||||||
specifier: ^10.9.2
|
specifier: ^10.9.2
|
||||||
version: 10.9.2(@types/node@22.19.1)(typescript@5.9.3)
|
version: 10.9.2(@types/node@22.19.1)(typescript@5.9.3)
|
||||||
|
|
@ -5819,6 +5819,9 @@ importers:
|
||||||
'@google/generative-ai':
|
'@google/generative-ai':
|
||||||
specifier: ^0.24.1
|
specifier: ^0.24.1
|
||||||
version: 0.24.1
|
version: 0.24.1
|
||||||
|
'@manacore/shared-storage':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../packages/shared-storage
|
||||||
'@nestjs/axios':
|
'@nestjs/axios':
|
||||||
specifier: ^4.0.1
|
specifier: ^4.0.1
|
||||||
version: 4.0.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2)
|
version: 4.0.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2)
|
||||||
|
|
@ -5843,6 +5846,9 @@ importers:
|
||||||
'@nestjs/throttler':
|
'@nestjs/throttler':
|
||||||
specifier: ^6.2.1
|
specifier: ^6.2.1
|
||||||
version: 6.4.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20)(reflect-metadata@0.2.2)
|
version: 6.4.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20)(reflect-metadata@0.2.2)
|
||||||
|
'@types/multer':
|
||||||
|
specifier: ^2.0.0
|
||||||
|
version: 2.0.0
|
||||||
axios:
|
axios:
|
||||||
specifier: ^1.7.2
|
specifier: ^1.7.2
|
||||||
version: 1.13.2
|
version: 1.13.2
|
||||||
|
|
@ -5885,6 +5891,9 @@ importers:
|
||||||
jsonwebtoken:
|
jsonwebtoken:
|
||||||
specifier: ^9.0.2
|
specifier: ^9.0.2
|
||||||
version: 9.0.3
|
version: 9.0.3
|
||||||
|
multer:
|
||||||
|
specifier: ^1.4.5-lts.1
|
||||||
|
version: 1.4.5-lts.2
|
||||||
nanoid:
|
nanoid:
|
||||||
specifier: ^5.0.9
|
specifier: ^5.0.9
|
||||||
version: 5.1.6
|
version: 5.1.6
|
||||||
|
|
@ -5918,7 +5927,7 @@ importers:
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@nestjs/cli':
|
'@nestjs/cli':
|
||||||
specifier: ^11.0.0
|
specifier: ^11.0.0
|
||||||
version: 11.0.12(@types/node@22.19.1)
|
version: 11.0.12(@types/node@22.19.1)(esbuild@0.19.12)
|
||||||
'@nestjs/schematics':
|
'@nestjs/schematics':
|
||||||
specifier: ^11.0.0
|
specifier: ^11.0.0
|
||||||
version: 11.0.9(chokidar@4.0.3)(typescript@5.9.3)
|
version: 11.0.9(chokidar@4.0.3)(typescript@5.9.3)
|
||||||
|
|
@ -5978,10 +5987,10 @@ importers:
|
||||||
version: 7.1.4
|
version: 7.1.4
|
||||||
ts-jest:
|
ts-jest:
|
||||||
specifier: ^29.2.5
|
specifier: ^29.2.5
|
||||||
version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3)
|
version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.19.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3)
|
||||||
ts-loader:
|
ts-loader:
|
||||||
specifier: ^9.5.1
|
specifier: ^9.5.1
|
||||||
version: 9.5.4(typescript@5.9.3)(webpack@5.100.2)
|
version: 9.5.4(typescript@5.9.3)(webpack@5.100.2(esbuild@0.19.12))
|
||||||
ts-node:
|
ts-node:
|
||||||
specifier: ^10.9.2
|
specifier: ^10.9.2
|
||||||
version: 10.9.2(@types/node@22.19.1)(typescript@5.9.3)
|
version: 10.9.2(@types/node@22.19.1)(typescript@5.9.3)
|
||||||
|
|
@ -6090,7 +6099,7 @@ importers:
|
||||||
version: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
|
version: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
|
||||||
ts-jest:
|
ts-jest:
|
||||||
specifier: ^29.2.5
|
specifier: ^29.2.5
|
||||||
version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3)
|
version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.19.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3)
|
||||||
ts-node:
|
ts-node:
|
||||||
specifier: ^10.9.2
|
specifier: ^10.9.2
|
||||||
version: 10.9.2(@types/node@22.19.1)(typescript@5.9.3)
|
version: 10.9.2(@types/node@22.19.1)(typescript@5.9.3)
|
||||||
|
|
@ -6280,7 +6289,7 @@ importers:
|
||||||
version: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
|
version: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
|
||||||
ts-jest:
|
ts-jest:
|
||||||
specifier: ^29.2.5
|
specifier: ^29.2.5
|
||||||
version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3)
|
version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.19.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3)
|
||||||
ts-node:
|
ts-node:
|
||||||
specifier: ^10.9.2
|
specifier: ^10.9.2
|
||||||
version: 10.9.2(@types/node@22.19.1)(typescript@5.9.3)
|
version: 10.9.2(@types/node@22.19.1)(typescript@5.9.3)
|
||||||
|
|
@ -6353,7 +6362,7 @@ importers:
|
||||||
version: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
|
version: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
|
||||||
ts-jest:
|
ts-jest:
|
||||||
specifier: ^29.2.5
|
specifier: ^29.2.5
|
||||||
version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3)
|
version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.19.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3)
|
||||||
ts-node:
|
ts-node:
|
||||||
specifier: ^10.9.2
|
specifier: ^10.9.2
|
||||||
version: 10.9.2(@types/node@22.19.1)(typescript@5.9.3)
|
version: 10.9.2(@types/node@22.19.1)(typescript@5.9.3)
|
||||||
|
|
@ -27388,7 +27397,7 @@ snapshots:
|
||||||
wrap-ansi: 7.0.0
|
wrap-ansi: 7.0.0
|
||||||
ws: 8.18.3
|
ws: 8.18.3
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
expo-router: 6.0.15(g2vconqrtzzmzlh6ymhbjirn5e)
|
expo-router: 6.0.15(5ll7ovd7i5kd7vxhny3dgbs3xy)
|
||||||
react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)
|
react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@modelcontextprotocol/sdk'
|
- '@modelcontextprotocol/sdk'
|
||||||
|
|
@ -28696,7 +28705,7 @@ snapshots:
|
||||||
jest-util: 30.2.0
|
jest-util: 30.2.0
|
||||||
slash: 3.0.0
|
slash: 3.0.0
|
||||||
|
|
||||||
'@jest/core@29.7.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))':
|
'@jest/core@29.7.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jest/console': 29.7.0
|
'@jest/console': 29.7.0
|
||||||
'@jest/reporters': 29.7.0
|
'@jest/reporters': 29.7.0
|
||||||
|
|
@ -28710,7 +28719,7 @@ snapshots:
|
||||||
exit: 0.1.2
|
exit: 0.1.2
|
||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
jest-changed-files: 29.7.0
|
jest-changed-files: 29.7.0
|
||||||
jest-config: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
|
jest-config: 29.7.0(@types/node@22.19.1)
|
||||||
jest-haste-map: 29.7.0
|
jest-haste-map: 29.7.0
|
||||||
jest-message-util: 29.7.0
|
jest-message-util: 29.7.0
|
||||||
jest-regex-util: 29.6.3
|
jest-regex-util: 29.6.3
|
||||||
|
|
@ -28731,7 +28740,7 @@ snapshots:
|
||||||
- supports-color
|
- supports-color
|
||||||
- ts-node
|
- ts-node
|
||||||
|
|
||||||
'@jest/core@29.7.0(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3))':
|
'@jest/core@29.7.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jest/console': 29.7.0
|
'@jest/console': 29.7.0
|
||||||
'@jest/reporters': 29.7.0
|
'@jest/reporters': 29.7.0
|
||||||
|
|
@ -28745,7 +28754,7 @@ snapshots:
|
||||||
exit: 0.1.2
|
exit: 0.1.2
|
||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
jest-changed-files: 29.7.0
|
jest-changed-files: 29.7.0
|
||||||
jest-config: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3))
|
jest-config: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
|
||||||
jest-haste-map: 29.7.0
|
jest-haste-map: 29.7.0
|
||||||
jest-message-util: 29.7.0
|
jest-message-util: 29.7.0
|
||||||
jest-regex-util: 29.6.3
|
jest-regex-util: 29.6.3
|
||||||
|
|
@ -29406,7 +29415,7 @@ snapshots:
|
||||||
- uglify-js
|
- uglify-js
|
||||||
- webpack-cli
|
- webpack-cli
|
||||||
|
|
||||||
'@nestjs/cli@11.0.12(@types/node@22.19.1)':
|
'@nestjs/cli@11.0.12(@types/node@22.19.1)(esbuild@0.19.12)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@angular-devkit/core': 19.2.19(chokidar@4.0.3)
|
'@angular-devkit/core': 19.2.19(chokidar@4.0.3)
|
||||||
'@angular-devkit/schematics': 19.2.19(chokidar@4.0.3)
|
'@angular-devkit/schematics': 19.2.19(chokidar@4.0.3)
|
||||||
|
|
@ -29417,14 +29426,14 @@ snapshots:
|
||||||
chokidar: 4.0.3
|
chokidar: 4.0.3
|
||||||
cli-table3: 0.6.5
|
cli-table3: 0.6.5
|
||||||
commander: 4.1.1
|
commander: 4.1.1
|
||||||
fork-ts-checker-webpack-plugin: 9.1.0(typescript@5.9.3)(webpack@5.100.2)
|
fork-ts-checker-webpack-plugin: 9.1.0(typescript@5.9.3)(webpack@5.100.2(esbuild@0.19.12))
|
||||||
glob: 12.0.0
|
glob: 12.0.0
|
||||||
node-emoji: 1.11.0
|
node-emoji: 1.11.0
|
||||||
ora: 5.4.1
|
ora: 5.4.1
|
||||||
tsconfig-paths: 4.2.0
|
tsconfig-paths: 4.2.0
|
||||||
tsconfig-paths-webpack-plugin: 4.2.0
|
tsconfig-paths-webpack-plugin: 4.2.0
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
webpack: 5.100.2
|
webpack: 5.100.2(esbuild@0.19.12)
|
||||||
webpack-node-externals: 3.0.0
|
webpack-node-externals: 3.0.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/node'
|
- '@types/node'
|
||||||
|
|
@ -33063,6 +33072,19 @@ snapshots:
|
||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
pretty-format: 27.5.1
|
pretty-format: 27.5.1
|
||||||
|
|
||||||
|
'@testing-library/react-native@13.3.3(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||||
|
dependencies:
|
||||||
|
jest-matcher-utils: 30.2.0
|
||||||
|
picocolors: 1.1.1
|
||||||
|
pretty-format: 30.2.0
|
||||||
|
react: 19.1.0
|
||||||
|
react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)
|
||||||
|
react-test-renderer: 19.1.0(react@19.1.0)
|
||||||
|
redent: 3.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
jest: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@testing-library/react-native@13.3.3(jest@30.2.0(@types/node@20.19.25)(esbuild-register@3.6.0(esbuild@0.27.0))(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)':
|
'@testing-library/react-native@13.3.3(jest@30.2.0(@types/node@20.19.25)(esbuild-register@3.6.0(esbuild@0.27.0))(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
jest-matcher-utils: 30.2.0
|
jest-matcher-utils: 30.2.0
|
||||||
|
|
@ -36483,13 +36505,13 @@ snapshots:
|
||||||
- supports-color
|
- supports-color
|
||||||
- ts-node
|
- ts-node
|
||||||
|
|
||||||
create-jest@29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)):
|
create-jest@29.7.0(@types/node@24.10.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
exit: 0.1.2
|
exit: 0.1.2
|
||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
jest-config: 29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3))
|
jest-config: 29.7.0(@types/node@24.10.1)
|
||||||
jest-util: 29.7.0
|
jest-util: 29.7.0
|
||||||
prompts: 2.4.2
|
prompts: 2.4.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
|
|
@ -39270,6 +39292,53 @@ snapshots:
|
||||||
- react-native
|
- react-native
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
expo-router@6.0.15(5ll7ovd7i5kd7vxhny3dgbs3xy):
|
||||||
|
dependencies:
|
||||||
|
'@expo/metro-runtime': 6.1.2(expo@54.0.25)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
|
||||||
|
'@expo/schema-utils': 0.1.7
|
||||||
|
'@radix-ui/react-slot': 1.2.0(@types/react@19.2.7)(react@19.1.0)
|
||||||
|
'@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
|
'@react-navigation/bottom-tabs': 7.8.6(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
|
||||||
|
'@react-navigation/native': 7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
|
||||||
|
'@react-navigation/native-stack': 7.8.0(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
|
||||||
|
client-only: 0.0.1
|
||||||
|
debug: 4.4.3
|
||||||
|
escape-string-regexp: 4.0.0
|
||||||
|
expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
|
||||||
|
expo-constants: 18.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))
|
||||||
|
expo-linking: 8.0.9(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
|
||||||
|
expo-server: 1.0.4
|
||||||
|
fast-deep-equal: 3.1.3
|
||||||
|
invariant: 2.2.4
|
||||||
|
nanoid: 3.3.11
|
||||||
|
query-string: 7.1.3
|
||||||
|
react: 19.1.0
|
||||||
|
react-fast-compare: 3.2.2
|
||||||
|
react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)
|
||||||
|
react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
|
||||||
|
react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
|
||||||
|
react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
|
||||||
|
semver: 7.6.3
|
||||||
|
server-only: 0.0.1
|
||||||
|
sf-symbols-typescript: 2.1.0
|
||||||
|
shallowequal: 1.1.0
|
||||||
|
use-latest-callback: 0.2.6(react@19.1.0)
|
||||||
|
vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
|
optionalDependencies:
|
||||||
|
'@react-navigation/drawer': 7.7.4(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
|
||||||
|
'@testing-library/react-native': 13.3.3(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
|
react-dom: 19.1.0(react@19.1.0)
|
||||||
|
react-native-gesture-handler: 2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
|
||||||
|
react-native-reanimated: 4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
|
||||||
|
react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
|
react-server-dom-webpack: 19.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(webpack@5.100.2(esbuild@0.19.12))
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@react-native-masked-view/masked-view'
|
||||||
|
- '@types/react'
|
||||||
|
- '@types/react-dom'
|
||||||
|
- supports-color
|
||||||
|
optional: true
|
||||||
|
|
||||||
expo-router@6.0.15(6hayu32hencph7rqfkncbd2qum):
|
expo-router@6.0.15(6hayu32hencph7rqfkncbd2qum):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@expo/metro-runtime': 6.1.2(expo@54.0.25)(react-dom@19.1.0(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1)
|
'@expo/metro-runtime': 6.1.2(expo@54.0.25)(react-dom@19.1.0(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1)
|
||||||
|
|
@ -40430,6 +40499,23 @@ snapshots:
|
||||||
typescript: 5.7.2
|
typescript: 5.7.2
|
||||||
webpack: 5.97.1
|
webpack: 5.97.1
|
||||||
|
|
||||||
|
fork-ts-checker-webpack-plugin@9.1.0(typescript@5.9.3)(webpack@5.100.2(esbuild@0.19.12)):
|
||||||
|
dependencies:
|
||||||
|
'@babel/code-frame': 7.27.1
|
||||||
|
chalk: 4.1.2
|
||||||
|
chokidar: 4.0.3
|
||||||
|
cosmiconfig: 8.3.6(typescript@5.9.3)
|
||||||
|
deepmerge: 4.3.1
|
||||||
|
fs-extra: 10.1.0
|
||||||
|
memfs: 3.5.3
|
||||||
|
minimatch: 3.1.2
|
||||||
|
node-abort-controller: 3.1.1
|
||||||
|
schema-utils: 3.3.0
|
||||||
|
semver: 7.7.3
|
||||||
|
tapable: 2.3.0
|
||||||
|
typescript: 5.9.3
|
||||||
|
webpack: 5.100.2(esbuild@0.19.12)
|
||||||
|
|
||||||
fork-ts-checker-webpack-plugin@9.1.0(typescript@5.9.3)(webpack@5.100.2(esbuild@0.27.0)):
|
fork-ts-checker-webpack-plugin@9.1.0(typescript@5.9.3)(webpack@5.100.2(esbuild@0.27.0)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/code-frame': 7.27.1
|
'@babel/code-frame': 7.27.1
|
||||||
|
|
@ -40447,23 +40533,6 @@ snapshots:
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
webpack: 5.100.2(esbuild@0.27.0)
|
webpack: 5.100.2(esbuild@0.27.0)
|
||||||
|
|
||||||
fork-ts-checker-webpack-plugin@9.1.0(typescript@5.9.3)(webpack@5.100.2):
|
|
||||||
dependencies:
|
|
||||||
'@babel/code-frame': 7.27.1
|
|
||||||
chalk: 4.1.2
|
|
||||||
chokidar: 4.0.3
|
|
||||||
cosmiconfig: 8.3.6(typescript@5.9.3)
|
|
||||||
deepmerge: 4.3.1
|
|
||||||
fs-extra: 10.1.0
|
|
||||||
memfs: 3.5.3
|
|
||||||
minimatch: 3.1.2
|
|
||||||
node-abort-controller: 3.1.1
|
|
||||||
schema-utils: 3.3.0
|
|
||||||
semver: 7.7.3
|
|
||||||
tapable: 2.3.0
|
|
||||||
typescript: 5.9.3
|
|
||||||
webpack: 5.100.2
|
|
||||||
|
|
||||||
form-data-encoder@1.7.2: {}
|
form-data-encoder@1.7.2: {}
|
||||||
|
|
||||||
form-data@2.3.3:
|
form-data@2.3.3:
|
||||||
|
|
@ -41875,16 +41944,16 @@ snapshots:
|
||||||
- supports-color
|
- supports-color
|
||||||
- ts-node
|
- ts-node
|
||||||
|
|
||||||
jest-cli@29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)):
|
jest-cli@29.7.0(@types/node@24.10.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jest/core': 29.7.0(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3))
|
'@jest/core': 29.7.0
|
||||||
'@jest/test-result': 29.7.0
|
'@jest/test-result': 29.7.0
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
create-jest: 29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3))
|
create-jest: 29.7.0(@types/node@24.10.1)
|
||||||
exit: 0.1.2
|
exit: 0.1.2
|
||||||
import-local: 3.2.0
|
import-local: 3.2.0
|
||||||
jest-config: 29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3))
|
jest-config: 29.7.0(@types/node@24.10.1)
|
||||||
jest-util: 29.7.0
|
jest-util: 29.7.0
|
||||||
jest-validate: 29.7.0
|
jest-validate: 29.7.0
|
||||||
yargs: 17.7.2
|
yargs: 17.7.2
|
||||||
|
|
@ -41973,6 +42042,36 @@ snapshots:
|
||||||
- ts-node
|
- ts-node
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
jest-config@29.7.0(@types/node@22.19.1):
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.28.5
|
||||||
|
'@jest/test-sequencer': 29.7.0
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
babel-jest: 29.7.0(@babel/core@7.28.5)
|
||||||
|
chalk: 4.1.2
|
||||||
|
ci-info: 3.9.0
|
||||||
|
deepmerge: 4.3.1
|
||||||
|
glob: 7.2.3
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
jest-circus: 29.7.0
|
||||||
|
jest-environment-node: 29.7.0
|
||||||
|
jest-get-type: 29.6.3
|
||||||
|
jest-regex-util: 29.6.3
|
||||||
|
jest-resolve: 29.7.0
|
||||||
|
jest-runner: 29.7.0
|
||||||
|
jest-util: 29.7.0
|
||||||
|
jest-validate: 29.7.0
|
||||||
|
micromatch: 4.0.8
|
||||||
|
parse-json: 5.2.0
|
||||||
|
pretty-format: 29.7.0
|
||||||
|
slash: 3.0.0
|
||||||
|
strip-json-comments: 3.1.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- babel-plugin-macros
|
||||||
|
- supports-color
|
||||||
|
|
||||||
jest-config@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)):
|
jest-config@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.28.5
|
'@babel/core': 7.28.5
|
||||||
|
|
@ -42004,38 +42103,7 @@ snapshots:
|
||||||
- babel-plugin-macros
|
- babel-plugin-macros
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
jest-config@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)):
|
jest-config@29.7.0(@types/node@24.10.1):
|
||||||
dependencies:
|
|
||||||
'@babel/core': 7.28.5
|
|
||||||
'@jest/test-sequencer': 29.7.0
|
|
||||||
'@jest/types': 29.6.3
|
|
||||||
babel-jest: 29.7.0(@babel/core@7.28.5)
|
|
||||||
chalk: 4.1.2
|
|
||||||
ci-info: 3.9.0
|
|
||||||
deepmerge: 4.3.1
|
|
||||||
glob: 7.2.3
|
|
||||||
graceful-fs: 4.2.11
|
|
||||||
jest-circus: 29.7.0
|
|
||||||
jest-environment-node: 29.7.0
|
|
||||||
jest-get-type: 29.6.3
|
|
||||||
jest-regex-util: 29.6.3
|
|
||||||
jest-resolve: 29.7.0
|
|
||||||
jest-runner: 29.7.0
|
|
||||||
jest-util: 29.7.0
|
|
||||||
jest-validate: 29.7.0
|
|
||||||
micromatch: 4.0.8
|
|
||||||
parse-json: 5.2.0
|
|
||||||
pretty-format: 29.7.0
|
|
||||||
slash: 3.0.0
|
|
||||||
strip-json-comments: 3.1.1
|
|
||||||
optionalDependencies:
|
|
||||||
'@types/node': 22.19.1
|
|
||||||
ts-node: 10.9.2(@types/node@24.10.1)(typescript@5.9.3)
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- babel-plugin-macros
|
|
||||||
- supports-color
|
|
||||||
|
|
||||||
jest-config@29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)):
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.28.5
|
'@babel/core': 7.28.5
|
||||||
'@jest/test-sequencer': 29.7.0
|
'@jest/test-sequencer': 29.7.0
|
||||||
|
|
@ -42061,7 +42129,6 @@ snapshots:
|
||||||
strip-json-comments: 3.1.1
|
strip-json-comments: 3.1.1
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/node': 24.10.1
|
'@types/node': 24.10.1
|
||||||
ts-node: 10.9.2(@types/node@24.10.1)(typescript@5.9.3)
|
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- babel-plugin-macros
|
- babel-plugin-macros
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
@ -42758,12 +42825,12 @@ snapshots:
|
||||||
- supports-color
|
- supports-color
|
||||||
- ts-node
|
- ts-node
|
||||||
|
|
||||||
jest@29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)):
|
jest@29.7.0(@types/node@24.10.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jest/core': 29.7.0(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3))
|
'@jest/core': 29.7.0
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
import-local: 3.2.0
|
import-local: 3.2.0
|
||||||
jest-cli: 29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3))
|
jest-cli: 29.7.0(@types/node@24.10.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/node'
|
- '@types/node'
|
||||||
- babel-plugin-macros
|
- babel-plugin-macros
|
||||||
|
|
@ -47155,6 +47222,16 @@ snapshots:
|
||||||
webpack-sources: 3.3.3
|
webpack-sources: 3.3.3
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
react-server-dom-webpack@19.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(webpack@5.100.2(esbuild@0.19.12)):
|
||||||
|
dependencies:
|
||||||
|
acorn-loose: 8.5.2
|
||||||
|
neo-async: 2.6.2
|
||||||
|
react: 19.1.0
|
||||||
|
react-dom: 19.1.0(react@19.1.0)
|
||||||
|
webpack: 5.100.2(esbuild@0.19.12)
|
||||||
|
webpack-sources: 3.3.3
|
||||||
|
optional: true
|
||||||
|
|
||||||
react-server-dom-webpack@19.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(webpack@5.100.2(esbuild@0.27.0)):
|
react-server-dom-webpack@19.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(webpack@5.100.2(esbuild@0.27.0)):
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn-loose: 8.5.2
|
acorn-loose: 8.5.2
|
||||||
|
|
@ -48665,6 +48742,17 @@ snapshots:
|
||||||
ansi-escapes: 4.3.2
|
ansi-escapes: 4.3.2
|
||||||
supports-hyperlinks: 2.3.0
|
supports-hyperlinks: 2.3.0
|
||||||
|
|
||||||
|
terser-webpack-plugin@5.3.14(esbuild@0.19.12)(webpack@5.100.2(esbuild@0.19.12)):
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/trace-mapping': 0.3.31
|
||||||
|
jest-worker: 27.5.1
|
||||||
|
schema-utils: 4.3.3
|
||||||
|
serialize-javascript: 6.0.2
|
||||||
|
terser: 5.44.1
|
||||||
|
webpack: 5.100.2(esbuild@0.19.12)
|
||||||
|
optionalDependencies:
|
||||||
|
esbuild: 0.19.12
|
||||||
|
|
||||||
terser-webpack-plugin@5.3.14(esbuild@0.27.0)(webpack@5.100.2(esbuild@0.27.0)):
|
terser-webpack-plugin@5.3.14(esbuild@0.27.0)(webpack@5.100.2(esbuild@0.27.0)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/trace-mapping': 0.3.31
|
'@jridgewell/trace-mapping': 0.3.31
|
||||||
|
|
@ -48873,6 +48961,27 @@ snapshots:
|
||||||
|
|
||||||
ts-interface-checker@0.1.13: {}
|
ts-interface-checker@0.1.13: {}
|
||||||
|
|
||||||
|
ts-jest@29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.19.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3):
|
||||||
|
dependencies:
|
||||||
|
bs-logger: 0.2.6
|
||||||
|
fast-json-stable-stringify: 2.1.0
|
||||||
|
handlebars: 4.7.8
|
||||||
|
jest: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
|
||||||
|
json5: 2.2.3
|
||||||
|
lodash.memoize: 4.1.2
|
||||||
|
make-error: 1.3.6
|
||||||
|
semver: 7.7.3
|
||||||
|
type-fest: 4.41.0
|
||||||
|
typescript: 5.9.3
|
||||||
|
yargs-parser: 21.1.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@babel/core': 7.28.5
|
||||||
|
'@jest/transform': 30.2.0
|
||||||
|
'@jest/types': 30.2.0
|
||||||
|
babel-jest: 30.2.0(@babel/core@7.28.5)
|
||||||
|
esbuild: 0.19.12
|
||||||
|
jest-util: 30.2.0
|
||||||
|
|
||||||
ts-jest@29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.27.0)(jest-util@30.2.0)(jest@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.0))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3):
|
ts-jest@29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.27.0)(jest-util@30.2.0)(jest@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.0))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
bs-logger: 0.2.6
|
bs-logger: 0.2.6
|
||||||
|
|
@ -48894,25 +49003,15 @@ snapshots:
|
||||||
esbuild: 0.27.0
|
esbuild: 0.27.0
|
||||||
jest-util: 30.2.0
|
jest-util: 30.2.0
|
||||||
|
|
||||||
ts-jest@29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3):
|
ts-loader@9.5.4(typescript@5.9.3)(webpack@5.100.2(esbuild@0.19.12)):
|
||||||
dependencies:
|
dependencies:
|
||||||
bs-logger: 0.2.6
|
chalk: 4.1.2
|
||||||
fast-json-stable-stringify: 2.1.0
|
enhanced-resolve: 5.18.3
|
||||||
handlebars: 4.7.8
|
micromatch: 4.0.8
|
||||||
jest: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
|
|
||||||
json5: 2.2.3
|
|
||||||
lodash.memoize: 4.1.2
|
|
||||||
make-error: 1.3.6
|
|
||||||
semver: 7.7.3
|
semver: 7.7.3
|
||||||
type-fest: 4.41.0
|
source-map: 0.7.6
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
yargs-parser: 21.1.1
|
webpack: 5.100.2(esbuild@0.19.12)
|
||||||
optionalDependencies:
|
|
||||||
'@babel/core': 7.28.5
|
|
||||||
'@jest/transform': 30.2.0
|
|
||||||
'@jest/types': 30.2.0
|
|
||||||
babel-jest: 30.2.0(@babel/core@7.28.5)
|
|
||||||
jest-util: 30.2.0
|
|
||||||
|
|
||||||
ts-loader@9.5.4(typescript@5.9.3)(webpack@5.100.2(esbuild@0.27.0)):
|
ts-loader@9.5.4(typescript@5.9.3)(webpack@5.100.2(esbuild@0.27.0)):
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -50157,6 +50256,38 @@ snapshots:
|
||||||
- esbuild
|
- esbuild
|
||||||
- uglify-js
|
- uglify-js
|
||||||
|
|
||||||
|
webpack@5.100.2(esbuild@0.19.12):
|
||||||
|
dependencies:
|
||||||
|
'@types/eslint-scope': 3.7.7
|
||||||
|
'@types/estree': 1.0.8
|
||||||
|
'@types/json-schema': 7.0.15
|
||||||
|
'@webassemblyjs/ast': 1.14.1
|
||||||
|
'@webassemblyjs/wasm-edit': 1.14.1
|
||||||
|
'@webassemblyjs/wasm-parser': 1.14.1
|
||||||
|
acorn: 8.15.0
|
||||||
|
acorn-import-phases: 1.0.4(acorn@8.15.0)
|
||||||
|
browserslist: 4.28.0
|
||||||
|
chrome-trace-event: 1.0.4
|
||||||
|
enhanced-resolve: 5.18.3
|
||||||
|
es-module-lexer: 1.7.0
|
||||||
|
eslint-scope: 5.1.1
|
||||||
|
events: 3.3.0
|
||||||
|
glob-to-regexp: 0.4.1
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
json-parse-even-better-errors: 2.3.1
|
||||||
|
loader-runner: 4.3.1
|
||||||
|
mime-types: 2.1.35
|
||||||
|
neo-async: 2.6.2
|
||||||
|
schema-utils: 4.3.3
|
||||||
|
tapable: 2.3.0
|
||||||
|
terser-webpack-plugin: 5.3.14(esbuild@0.19.12)(webpack@5.100.2(esbuild@0.19.12))
|
||||||
|
watchpack: 2.4.4
|
||||||
|
webpack-sources: 3.3.3
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@swc/core'
|
||||||
|
- esbuild
|
||||||
|
- uglify-js
|
||||||
|
|
||||||
webpack@5.100.2(esbuild@0.27.0):
|
webpack@5.100.2(esbuild@0.27.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/eslint-scope': 3.7.7
|
'@types/eslint-scope': 3.7.7
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,12 @@
|
||||||
"db:migrate": "tsx src/db/migrate.ts",
|
"db:migrate": "tsx src/db/migrate.ts",
|
||||||
"db:studio": "drizzle-kit studio",
|
"db:studio": "drizzle-kit studio",
|
||||||
"db:seed:dev": "tsx src/db/seed-dev-user.ts",
|
"db:seed:dev": "tsx src/db/seed-dev-user.ts",
|
||||||
"db:seed:oidc": "tsx src/db/seeds/seed-oidc-clients.ts"
|
"db:seed:oidc": "tsx src/db/seeds/seed-oidc-clients.ts",
|
||||||
|
"db:seed:plans": "tsx src/db/seeds/seed-subscription-plans.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@google/generative-ai": "^0.24.1",
|
"@google/generative-ai": "^0.24.1",
|
||||||
|
"@manacore/shared-storage": "workspace:*",
|
||||||
"@nestjs/axios": "^4.0.1",
|
"@nestjs/axios": "^4.0.1",
|
||||||
"@nestjs/common": "^10.4.15",
|
"@nestjs/common": "^10.4.15",
|
||||||
"@nestjs/config": "^3.3.0",
|
"@nestjs/config": "^3.3.0",
|
||||||
|
|
@ -33,6 +35,7 @@
|
||||||
"@nestjs/schedule": "^4.1.2",
|
"@nestjs/schedule": "^4.1.2",
|
||||||
"@nestjs/swagger": "^8.1.0",
|
"@nestjs/swagger": "^8.1.0",
|
||||||
"@nestjs/throttler": "^6.2.1",
|
"@nestjs/throttler": "^6.2.1",
|
||||||
|
"@types/multer": "^2.0.0",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"better-auth": "^1.4.3",
|
"better-auth": "^1.4.3",
|
||||||
|
|
@ -47,6 +50,7 @@
|
||||||
"helmet": "^8.0.0",
|
"helmet": "^8.0.0",
|
||||||
"jose": "^6.1.2",
|
"jose": "^6.1.2",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"multer": "^1.4.5-lts.1",
|
||||||
"nanoid": "^5.0.9",
|
"nanoid": "^5.0.9",
|
||||||
"nodemailer": "^7.0.12",
|
"nodemailer": "^7.0.12",
|
||||||
"postgres": "^3.4.5",
|
"postgres": "^3.4.5",
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import { FeedbackModule } from './feedback/feedback.module';
|
||||||
import { HealthModule } from './health/health.module';
|
import { HealthModule } from './health/health.module';
|
||||||
import { ReferralsModule } from './referrals/referrals.module';
|
import { ReferralsModule } from './referrals/referrals.module';
|
||||||
import { SettingsModule } from './settings/settings.module';
|
import { SettingsModule } from './settings/settings.module';
|
||||||
|
import { StorageModule } from './storage/storage.module';
|
||||||
import { TagsModule } from './tags/tags.module';
|
import { TagsModule } from './tags/tags.module';
|
||||||
import { MeModule } from './me/me.module';
|
import { MeModule } from './me/me.module';
|
||||||
import { SubscriptionsModule } from './subscriptions/subscriptions.module';
|
import { SubscriptionsModule } from './subscriptions/subscriptions.module';
|
||||||
|
|
@ -45,6 +46,7 @@ import { LoggerModule } from './common/logger';
|
||||||
HealthModule,
|
HealthModule,
|
||||||
ReferralsModule,
|
ReferralsModule,
|
||||||
SettingsModule,
|
SettingsModule,
|
||||||
|
StorageModule,
|
||||||
TagsModule,
|
TagsModule,
|
||||||
MeModule,
|
MeModule,
|
||||||
StripeModule,
|
StripeModule,
|
||||||
|
|
|
||||||
|
|
@ -72,5 +72,9 @@ export default () => ({
|
||||||
geminiApiKey: env.GOOGLE_GENAI_API_KEY || '',
|
geminiApiKey: env.GOOGLE_GENAI_API_KEY || '',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
storage: {
|
||||||
|
publicUrl: env.MANACORE_STORAGE_PUBLIC_URL || '',
|
||||||
|
},
|
||||||
|
|
||||||
baseUrl: env.BASE_URL || (isDevelopment() ? 'http://localhost:3001' : ''),
|
baseUrl: env.BASE_URL || (isDevelopment() ? 'http://localhost:3001' : ''),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,9 @@ const envSchema = z.object({
|
||||||
// AI
|
// AI
|
||||||
GOOGLE_GENAI_API_KEY: z.string().optional(),
|
GOOGLE_GENAI_API_KEY: z.string().optional(),
|
||||||
|
|
||||||
|
// Storage
|
||||||
|
MANACORE_STORAGE_PUBLIC_URL: z.string().optional(),
|
||||||
|
|
||||||
// Base URL for callbacks
|
// Base URL for callbacks
|
||||||
BASE_URL: z.string().url().optional(),
|
BASE_URL: z.string().url().optional(),
|
||||||
|
|
||||||
|
|
|
||||||
184
services/mana-core-auth/src/db/seeds/seed-subscription-plans.ts
Normal file
184
services/mana-core-auth/src/db/seeds/seed-subscription-plans.ts
Normal file
|
|
@ -0,0 +1,184 @@
|
||||||
|
/**
|
||||||
|
* Seed subscription plans with Stripe Price IDs
|
||||||
|
*
|
||||||
|
* This script creates/updates the default subscription plans in the database.
|
||||||
|
* Plans are idempotent - running multiple times won't create duplicates.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* pnpm db:seed:plans
|
||||||
|
*
|
||||||
|
* Prerequisites:
|
||||||
|
* 1. Create products and prices in Stripe Dashboard
|
||||||
|
* 2. Set STRIPE_* environment variables with the price IDs
|
||||||
|
*
|
||||||
|
* Stripe Products to create:
|
||||||
|
* - Mana Free (price: 0 EUR)
|
||||||
|
* - Mana Pro (prices: 9.99 EUR/month, 99 EUR/year)
|
||||||
|
* - Mana Enterprise (contact sales)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'dotenv/config';
|
||||||
|
import postgres from 'postgres';
|
||||||
|
import { drizzle } from 'drizzle-orm/postgres-js';
|
||||||
|
import { eq } from 'drizzle-orm';
|
||||||
|
import { plans } from '../schema/subscriptions.schema';
|
||||||
|
|
||||||
|
// Environment configuration
|
||||||
|
const DATABASE_URL =
|
||||||
|
process.env.DATABASE_URL || 'postgresql://manacore:manacore@localhost:5432/manacore_auth';
|
||||||
|
|
||||||
|
// Stripe Price IDs from environment (or defaults for development)
|
||||||
|
const STRIPE_CONFIG = {
|
||||||
|
// Free plan (no Stripe price needed)
|
||||||
|
FREE_PRODUCT_ID: process.env.STRIPE_FREE_PRODUCT_ID || '',
|
||||||
|
|
||||||
|
// Pro plan
|
||||||
|
PRO_PRODUCT_ID: process.env.STRIPE_PRO_PRODUCT_ID || '',
|
||||||
|
PRO_PRICE_MONTHLY: process.env.STRIPE_PRO_PRICE_MONTHLY || '', // e.g., price_xxx
|
||||||
|
PRO_PRICE_YEARLY: process.env.STRIPE_PRO_PRICE_YEARLY || '', // e.g., price_xxx
|
||||||
|
|
||||||
|
// Enterprise plan
|
||||||
|
ENTERPRISE_PRODUCT_ID: process.env.STRIPE_ENTERPRISE_PRODUCT_ID || '',
|
||||||
|
ENTERPRISE_PRICE_MONTHLY: process.env.STRIPE_ENTERPRISE_PRICE_MONTHLY || '',
|
||||||
|
ENTERPRISE_PRICE_YEARLY: process.env.STRIPE_ENTERPRISE_PRICE_YEARLY || '',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Plan definitions
|
||||||
|
const PLANS = [
|
||||||
|
{
|
||||||
|
name: 'Free',
|
||||||
|
description: 'Kostenlos starten mit grundlegenden Features',
|
||||||
|
monthlyCredits: 150,
|
||||||
|
priceMonthlyEuroCents: 0,
|
||||||
|
priceYearlyEuroCents: 0,
|
||||||
|
stripePriceIdMonthly: null,
|
||||||
|
stripePriceIdYearly: null,
|
||||||
|
stripeProductId: STRIPE_CONFIG.FREE_PRODUCT_ID || null,
|
||||||
|
features: [
|
||||||
|
'150 Credits pro Monat',
|
||||||
|
'5 tägliche Gratis-Credits',
|
||||||
|
'Zugang zu allen Apps',
|
||||||
|
'Basis-Support',
|
||||||
|
],
|
||||||
|
maxTeamMembers: null,
|
||||||
|
maxOrganizations: null,
|
||||||
|
isDefault: true,
|
||||||
|
isEnterprise: false,
|
||||||
|
sortOrder: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Pro',
|
||||||
|
description: 'Für Power-User mit mehr Credits und Features',
|
||||||
|
monthlyCredits: 2000,
|
||||||
|
priceMonthlyEuroCents: 999, // 9.99 EUR
|
||||||
|
priceYearlyEuroCents: 9900, // 99 EUR (2 months free)
|
||||||
|
stripePriceIdMonthly: STRIPE_CONFIG.PRO_PRICE_MONTHLY || null,
|
||||||
|
stripePriceIdYearly: STRIPE_CONFIG.PRO_PRICE_YEARLY || null,
|
||||||
|
stripeProductId: STRIPE_CONFIG.PRO_PRODUCT_ID || null,
|
||||||
|
features: [
|
||||||
|
'2.000 Credits pro Monat',
|
||||||
|
'20 tägliche Gratis-Credits',
|
||||||
|
'Prioritäts-Support',
|
||||||
|
'Erweiterte AI-Modelle',
|
||||||
|
'API-Zugang',
|
||||||
|
],
|
||||||
|
maxTeamMembers: 5,
|
||||||
|
maxOrganizations: 3,
|
||||||
|
isDefault: false,
|
||||||
|
isEnterprise: false,
|
||||||
|
sortOrder: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Enterprise',
|
||||||
|
description: 'Für Teams und Unternehmen mit individuellen Anforderungen',
|
||||||
|
monthlyCredits: 10000,
|
||||||
|
priceMonthlyEuroCents: 4900, // 49 EUR
|
||||||
|
priceYearlyEuroCents: 49000, // 490 EUR (2 months free)
|
||||||
|
stripePriceIdMonthly: STRIPE_CONFIG.ENTERPRISE_PRICE_MONTHLY || null,
|
||||||
|
stripePriceIdYearly: STRIPE_CONFIG.ENTERPRISE_PRICE_YEARLY || null,
|
||||||
|
stripeProductId: STRIPE_CONFIG.ENTERPRISE_PRODUCT_ID || null,
|
||||||
|
features: [
|
||||||
|
'10.000 Credits pro Monat',
|
||||||
|
'Unbegrenzte tägliche Credits',
|
||||||
|
'Dedizierter Account Manager',
|
||||||
|
'SLA-garantierte Verfügbarkeit',
|
||||||
|
'Custom AI-Modelle',
|
||||||
|
'SSO / SAML Integration',
|
||||||
|
'Admin-Dashboard',
|
||||||
|
],
|
||||||
|
maxTeamMembers: null, // Unlimited
|
||||||
|
maxOrganizations: null, // Unlimited
|
||||||
|
isDefault: false,
|
||||||
|
isEnterprise: true,
|
||||||
|
sortOrder: 2,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
async function seedPlans() {
|
||||||
|
console.log('Seeding subscription plans...');
|
||||||
|
console.log(`Database: ${DATABASE_URL.replace(/:[^@]+@/, ':***@')}`);
|
||||||
|
|
||||||
|
const client = postgres(DATABASE_URL);
|
||||||
|
const db = drizzle(client);
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (const plan of PLANS) {
|
||||||
|
// Check if plan exists
|
||||||
|
const [existing] = await db.select().from(plans).where(eq(plans.name, plan.name)).limit(1);
|
||||||
|
|
||||||
|
if (existing) {
|
||||||
|
// Update existing plan
|
||||||
|
await db
|
||||||
|
.update(plans)
|
||||||
|
.set({
|
||||||
|
...plan,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
})
|
||||||
|
.where(eq(plans.id, existing.id));
|
||||||
|
console.log(`✓ Updated plan: ${plan.name}`);
|
||||||
|
} else {
|
||||||
|
// Insert new plan
|
||||||
|
await db.insert(plans).values({
|
||||||
|
...plan,
|
||||||
|
} as any);
|
||||||
|
console.log(`✓ Created plan: ${plan.name}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// List all plans
|
||||||
|
const allPlans = await db.select().from(plans).orderBy(plans.sortOrder);
|
||||||
|
console.log('\nAll subscription plans:');
|
||||||
|
console.table(
|
||||||
|
allPlans.map((p) => ({
|
||||||
|
name: p.name,
|
||||||
|
credits: p.monthlyCredits,
|
||||||
|
monthly: `€${(p.priceMonthlyEuroCents / 100).toFixed(2)}`,
|
||||||
|
yearly: `€${(p.priceYearlyEuroCents / 100).toFixed(2)}`,
|
||||||
|
stripeMonthly: p.stripePriceIdMonthly || '(not set)',
|
||||||
|
stripeYearly: p.stripePriceIdYearly || '(not set)',
|
||||||
|
default: p.isDefault,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('\n✅ Subscription plans seeded successfully!');
|
||||||
|
|
||||||
|
if (!STRIPE_CONFIG.PRO_PRICE_MONTHLY || !STRIPE_CONFIG.PRO_PRICE_YEARLY) {
|
||||||
|
console.log('\n⚠️ Warning: Stripe Price IDs not configured.');
|
||||||
|
console.log(' Set these environment variables:');
|
||||||
|
console.log(' - STRIPE_PRO_PRODUCT_ID');
|
||||||
|
console.log(' - STRIPE_PRO_PRICE_MONTHLY');
|
||||||
|
console.log(' - STRIPE_PRO_PRICE_YEARLY');
|
||||||
|
console.log(' - STRIPE_ENTERPRISE_PRODUCT_ID');
|
||||||
|
console.log(' - STRIPE_ENTERPRISE_PRICE_MONTHLY');
|
||||||
|
console.log(' - STRIPE_ENTERPRISE_PRICE_YEARLY');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error seeding plans:', error);
|
||||||
|
process.exit(1);
|
||||||
|
} finally {
|
||||||
|
await client.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run if called directly
|
||||||
|
seedPlans();
|
||||||
3
services/mana-core-auth/src/storage/index.ts
Normal file
3
services/mana-core-auth/src/storage/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export { StorageModule } from './storage.module';
|
||||||
|
export { StorageService } from './storage.service';
|
||||||
|
export { StorageController } from './storage.controller';
|
||||||
113
services/mana-core-auth/src/storage/storage.controller.ts
Normal file
113
services/mana-core-auth/src/storage/storage.controller.ts
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
Post,
|
||||||
|
Body,
|
||||||
|
UseGuards,
|
||||||
|
UseInterceptors,
|
||||||
|
UploadedFile,
|
||||||
|
BadRequestException,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { FileInterceptor } from '@nestjs/platform-express';
|
||||||
|
import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth, ApiConsumes } from '@nestjs/swagger';
|
||||||
|
import { StorageService } from './storage.service';
|
||||||
|
import { JwtAuthGuard } from '../common/guards/jwt-auth.guard';
|
||||||
|
import { CurrentUser } from '../common/decorators/current-user.decorator';
|
||||||
|
import type { CurrentUserData } from '../common/decorators/current-user.decorator';
|
||||||
|
|
||||||
|
interface GetUploadUrlDto {
|
||||||
|
filename: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiTags('storage')
|
||||||
|
@Controller('storage')
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@ApiBearerAuth('JWT-auth')
|
||||||
|
export class StorageController {
|
||||||
|
constructor(private readonly storageService: StorageService) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a presigned URL for avatar upload
|
||||||
|
*
|
||||||
|
* Returns a presigned URL that the client can use to upload
|
||||||
|
* the avatar directly to S3/MinIO. This is the recommended approach
|
||||||
|
* for frontend uploads as it's more efficient.
|
||||||
|
*/
|
||||||
|
@Post('avatar/upload-url')
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'Get presigned URL for avatar upload',
|
||||||
|
description:
|
||||||
|
'Returns a presigned URL for direct upload to storage. Use this URL to PUT the file.',
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: 200,
|
||||||
|
description: 'Returns presigned upload URL',
|
||||||
|
schema: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
uploadUrl: { type: 'string', description: 'PUT this URL with the file' },
|
||||||
|
fileUrl: { type: 'string', description: 'Public URL after upload' },
|
||||||
|
key: { type: 'string', description: 'Storage key' },
|
||||||
|
expiresIn: { type: 'number', description: 'URL expires in seconds' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
@ApiResponse({ status: 400, description: 'Invalid file type or storage not configured' })
|
||||||
|
async getAvatarUploadUrl(
|
||||||
|
@CurrentUser() user: CurrentUserData,
|
||||||
|
@Body() dto: GetUploadUrlDto
|
||||||
|
): Promise<{
|
||||||
|
uploadUrl: string;
|
||||||
|
fileUrl: string;
|
||||||
|
key: string;
|
||||||
|
expiresIn: number;
|
||||||
|
}> {
|
||||||
|
if (!dto.filename) {
|
||||||
|
throw new BadRequestException('filename is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.storageService.getAvatarUploadUrl(user.userId, dto.filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload avatar directly (multipart/form-data)
|
||||||
|
*
|
||||||
|
* Alternative to presigned URLs. The file is uploaded to the backend
|
||||||
|
* which then uploads it to S3/MinIO. Simpler but less efficient for
|
||||||
|
* large files.
|
||||||
|
*/
|
||||||
|
@Post('avatar')
|
||||||
|
@UseInterceptors(
|
||||||
|
FileInterceptor('file', {
|
||||||
|
limits: {
|
||||||
|
fileSize: 5 * 1024 * 1024, // 5MB
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
@ApiConsumes('multipart/form-data')
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'Upload avatar directly',
|
||||||
|
description: 'Upload avatar file directly to the server',
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: 201,
|
||||||
|
description: 'Avatar uploaded successfully',
|
||||||
|
schema: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
url: { type: 'string', description: 'Public URL of the uploaded avatar' },
|
||||||
|
key: { type: 'string', description: 'Storage key' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
@ApiResponse({ status: 400, description: 'Invalid file type or size' })
|
||||||
|
async uploadAvatar(
|
||||||
|
@CurrentUser() user: CurrentUserData,
|
||||||
|
@UploadedFile() file: Express.Multer.File
|
||||||
|
): Promise<{ url: string; key: string }> {
|
||||||
|
if (!file) {
|
||||||
|
throw new BadRequestException('No file uploaded');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.storageService.uploadAvatar(user.userId, file.buffer, file.originalname);
|
||||||
|
}
|
||||||
|
}
|
||||||
12
services/mana-core-auth/src/storage/storage.module.ts
Normal file
12
services/mana-core-auth/src/storage/storage.module.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { ConfigModule } from '@nestjs/config';
|
||||||
|
import { StorageService } from './storage.service';
|
||||||
|
import { StorageController } from './storage.controller';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [ConfigModule],
|
||||||
|
controllers: [StorageController],
|
||||||
|
providers: [StorageService],
|
||||||
|
exports: [StorageService],
|
||||||
|
})
|
||||||
|
export class StorageModule {}
|
||||||
176
services/mana-core-auth/src/storage/storage.service.ts
Normal file
176
services/mana-core-auth/src/storage/storage.service.ts
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
import { Injectable, Logger, BadRequestException, OnModuleInit } from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import {
|
||||||
|
createManaCoreStorage,
|
||||||
|
generateUserFileKey,
|
||||||
|
getContentType,
|
||||||
|
validateFileSize,
|
||||||
|
validateFileExtension,
|
||||||
|
IMAGE_EXTENSIONS,
|
||||||
|
} from '@manacore/shared-storage';
|
||||||
|
import type { StorageClient } from '@manacore/shared-storage';
|
||||||
|
|
||||||
|
const MAX_AVATAR_SIZE = 5 * 1024 * 1024; // 5MB
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class StorageService implements OnModuleInit {
|
||||||
|
private readonly logger = new Logger(StorageService.name);
|
||||||
|
private storage: StorageClient | null = null;
|
||||||
|
private readonly publicUrl: string | undefined;
|
||||||
|
|
||||||
|
constructor(private readonly configService: ConfigService) {
|
||||||
|
this.publicUrl = this.configService.get<string>('storage.publicUrl');
|
||||||
|
}
|
||||||
|
|
||||||
|
async onModuleInit() {
|
||||||
|
try {
|
||||||
|
this.storage = createManaCoreStorage(this.publicUrl);
|
||||||
|
this.logger.log('Storage service initialized');
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.warn(
|
||||||
|
'Storage service not configured - avatar uploads will be disabled',
|
||||||
|
error instanceof Error ? error.message : undefined
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if storage is available
|
||||||
|
*/
|
||||||
|
isAvailable(): boolean {
|
||||||
|
return this.storage !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a presigned URL for avatar upload
|
||||||
|
*
|
||||||
|
* @param userId - User ID
|
||||||
|
* @param filename - Original filename
|
||||||
|
* @returns Presigned upload URL and the final file URL
|
||||||
|
*/
|
||||||
|
async getAvatarUploadUrl(
|
||||||
|
userId: string,
|
||||||
|
filename: string
|
||||||
|
): Promise<{
|
||||||
|
uploadUrl: string;
|
||||||
|
fileUrl: string;
|
||||||
|
key: string;
|
||||||
|
expiresIn: number;
|
||||||
|
}> {
|
||||||
|
if (!this.storage) {
|
||||||
|
throw new BadRequestException('Storage service is not configured');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate file extension
|
||||||
|
const ext = filename.split('.').pop()?.toLowerCase();
|
||||||
|
if (!ext || !validateFileExtension(filename, IMAGE_EXTENSIONS)) {
|
||||||
|
throw new BadRequestException(`Invalid file type. Allowed: ${IMAGE_EXTENSIONS.join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate unique key for avatar
|
||||||
|
const key = `avatars/${userId}/${Date.now()}.${ext}`;
|
||||||
|
const contentType = getContentType(filename);
|
||||||
|
|
||||||
|
// Get presigned upload URL (1 hour expiry)
|
||||||
|
const expiresIn = 3600;
|
||||||
|
const uploadUrl = await this.storage.getUploadUrl(key, {
|
||||||
|
expiresIn,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Construct the final public URL
|
||||||
|
const fileUrl = await this.getPublicUrl(key);
|
||||||
|
|
||||||
|
this.logger.debug('Generated avatar upload URL', { userId, key });
|
||||||
|
|
||||||
|
return {
|
||||||
|
uploadUrl,
|
||||||
|
fileUrl,
|
||||||
|
key,
|
||||||
|
expiresIn,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload avatar directly (for server-side uploads)
|
||||||
|
*
|
||||||
|
* @param userId - User ID
|
||||||
|
* @param buffer - File buffer
|
||||||
|
* @param filename - Original filename
|
||||||
|
* @returns Public URL of the uploaded avatar
|
||||||
|
*/
|
||||||
|
async uploadAvatar(
|
||||||
|
userId: string,
|
||||||
|
buffer: Buffer,
|
||||||
|
filename: string
|
||||||
|
): Promise<{ url: string; key: string }> {
|
||||||
|
if (!this.storage) {
|
||||||
|
throw new BadRequestException('Storage service is not configured');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate file extension
|
||||||
|
if (!validateFileExtension(filename, IMAGE_EXTENSIONS)) {
|
||||||
|
throw new BadRequestException(`Invalid file type. Allowed: ${IMAGE_EXTENSIONS.join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate file size
|
||||||
|
if (!validateFileSize(buffer.length, MAX_AVATAR_SIZE)) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
`File too large. Maximum size: ${MAX_AVATAR_SIZE / 1024 / 1024}MB`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate unique key for avatar
|
||||||
|
const ext = filename.split('.').pop()?.toLowerCase() || 'jpg';
|
||||||
|
const key = `avatars/${userId}/${Date.now()}.${ext}`;
|
||||||
|
|
||||||
|
// Upload file
|
||||||
|
const result = await this.storage.upload(key, buffer, {
|
||||||
|
contentType: getContentType(filename),
|
||||||
|
public: true,
|
||||||
|
cacheControl: 'public, max-age=31536000', // 1 year cache
|
||||||
|
});
|
||||||
|
|
||||||
|
const url = result.url || (await this.getPublicUrl(key));
|
||||||
|
|
||||||
|
this.logger.log('Avatar uploaded', { userId, key });
|
||||||
|
|
||||||
|
return { url, key };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete avatar
|
||||||
|
*
|
||||||
|
* @param key - Storage key of the avatar
|
||||||
|
*/
|
||||||
|
async deleteAvatar(key: string): Promise<void> {
|
||||||
|
if (!this.storage) {
|
||||||
|
throw new BadRequestException('Storage service is not configured');
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.storage.delete(key);
|
||||||
|
this.logger.log('Avatar deleted', { key });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get public URL for a key
|
||||||
|
*/
|
||||||
|
private async getPublicUrl(key: string): Promise<string> {
|
||||||
|
if (!this.storage) {
|
||||||
|
throw new BadRequestException('Storage service is not configured');
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a configured public URL, use it
|
||||||
|
if (this.publicUrl) {
|
||||||
|
return `${this.publicUrl}/${key}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the storage has a public URL configured
|
||||||
|
const publicUrl = this.storage.getPublicUrl(key);
|
||||||
|
if (publicUrl) {
|
||||||
|
return publicUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, get a presigned URL for reading
|
||||||
|
return this.storage.getDownloadUrl(key, { expiresIn: 86400 * 365 }); // 1 year
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue