@idevconn/create-icore 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +238 -2
- package/dist/index.cjs +238 -2
- package/dist/index.js +238 -2
- package/package.json +1 -1
- package/templates/.yarn/releases/yarn-4.15.0.cjs +940 -0
- package/templates/.yarnrc.yml +6 -1
- package/templates/apps/api/src/app/app.module.ts +5 -1
- package/templates/apps/api/src/main.ts +12 -6
- package/templates/apps/microservices/auth/project.json +2 -1
- package/templates/apps/microservices/auth/src/app/app.module.ts +47 -23
- package/templates/apps/microservices/jobs/project.json +2 -1
- package/templates/apps/microservices/notes/project.json +2 -1
- package/templates/apps/microservices/notes/src/app/app.module.ts +44 -25
- package/templates/apps/microservices/payment/project.json +2 -1
- package/templates/apps/microservices/payment/src/app/app.module.ts +35 -12
- package/templates/apps/microservices/upload/project.json +2 -1
- package/templates/apps/microservices/upload/src/app/app.module.ts +48 -28
- package/templates/apps/templates/client-antd/.env.example +7 -0
- package/templates/apps/templates/client-antd/vite.config.mts +4 -4
- package/templates/apps/templates/client-mui/.env.example +7 -0
- package/templates/apps/templates/client-mui/vite.config.mts +4 -4
- package/templates/apps/templates/client-shadcn/.env.example +6 -1
- package/templates/apps/templates/client-shadcn/vite.config.mts +4 -4
- package/templates/libs/auth-client/src/index.ts +1 -0
- package/templates/libs/auth-client/src/lib/auth-client.module.ts +1 -1
- package/templates/libs/auth-client/src/lib/auth-client.service.ts +1 -1
- package/templates/libs/auth-client/src/lib/auth-client.tokens.ts +4 -0
- package/templates/libs/jobs-client/src/index.ts +1 -0
- package/templates/libs/jobs-client/src/lib/jobs-client.module.ts +1 -1
- package/templates/libs/jobs-client/src/lib/jobs-client.service.ts +1 -1
- package/templates/libs/jobs-client/src/lib/jobs-client.tokens.ts +4 -0
- package/templates/libs/notes-client/src/index.ts +1 -0
- package/templates/libs/notes-client/src/lib/notes-client.module.ts +1 -1
- package/templates/libs/notes-client/src/lib/notes-client.service.ts +1 -1
- package/templates/libs/notes-client/src/lib/notes-client.tokens.ts +4 -0
- package/templates/libs/payment-client/src/index.ts +1 -0
- package/templates/libs/payment-client/src/lib/payment-client.module.ts +1 -1
- package/templates/libs/payment-client/src/lib/payment-client.service.ts +1 -1
- package/templates/libs/payment-client/src/lib/payment-client.tokens.ts +4 -0
- package/templates/libs/shared/src/env.ts +88 -0
- package/templates/libs/shared/src/index.ts +1 -0
- package/templates/libs/shared/src/transport.ts +37 -0
- package/templates/libs/upload-client/src/index.ts +1 -0
- package/templates/libs/upload-client/src/lib/upload-client.module.ts +1 -1
- package/templates/libs/upload-client/src/lib/upload-client.service.ts +1 -1
- package/templates/libs/upload-client/src/lib/upload-client.tokens.ts +4 -0
- package/templates/libs/vite-plugins/src/index.d.mts +6 -0
- package/templates/libs/vite-plugins/src/index.mjs +50 -0
- package/templates/package.json +1 -0
- package/templates/tools/create-icore/_template-shell/package.json +1 -0
package/templates/.yarnrc.yml
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
1
2
|
import { Module } from '@nestjs/common';
|
|
2
3
|
import { ConfigModule } from '@nestjs/config';
|
|
3
4
|
import { ThrottlerModule, seconds } from '@nestjs/throttler';
|
|
@@ -11,7 +12,10 @@ import { AdminModule } from './admin/admin.module';
|
|
|
11
12
|
|
|
12
13
|
@Module({
|
|
13
14
|
imports: [
|
|
14
|
-
ConfigModule.forRoot({
|
|
15
|
+
ConfigModule.forRoot({
|
|
16
|
+
isGlobal: true,
|
|
17
|
+
envFilePath: [join(process.cwd(), 'apps/api/.env'), join(process.cwd(), '.env')],
|
|
18
|
+
}),
|
|
15
19
|
ThrottlerModule.forRoot([{ name: 'auth-burst', ttl: seconds(60), limit: 10 }]),
|
|
16
20
|
AuthModule,
|
|
17
21
|
AbilitiesModule,
|
|
@@ -3,9 +3,17 @@ import { NestFactory } from '@nestjs/core';
|
|
|
3
3
|
import { NestExpressApplication } from '@nestjs/platform-express';
|
|
4
4
|
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
|
5
5
|
import cookieParser from 'cookie-parser';
|
|
6
|
+
import { formatGatewayBanner } from '@icore/shared';
|
|
6
7
|
import { AppModule } from './app/app.module';
|
|
7
8
|
import pkg from '@icore/package.json';
|
|
8
9
|
|
|
10
|
+
const GATEWAY_SERVICES = [
|
|
11
|
+
{ name: 'auth', prefix: 'AUTH' },
|
|
12
|
+
{ name: 'upload', prefix: 'UPLOAD' },
|
|
13
|
+
{ name: 'notes', prefix: 'NOTES' },
|
|
14
|
+
{ name: 'payment', prefix: 'PAYMENT' },
|
|
15
|
+
];
|
|
16
|
+
|
|
9
17
|
const DEFAULT_PORT = 3001;
|
|
10
18
|
|
|
11
19
|
async function bootstrap() {
|
|
@@ -28,12 +36,10 @@ async function bootstrap() {
|
|
|
28
36
|
|
|
29
37
|
bootstrap()
|
|
30
38
|
.then(() => {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
logger.log(
|
|
36
|
-
`Swagger UI: ${process.env.API_ORIGIN ?? 'http://localhost'}:${process.env.API_PORT ?? DEFAULT_PORT}/api/docs`,
|
|
39
|
+
const origin = process.env.API_ORIGIN ?? 'http://localhost';
|
|
40
|
+
const port = Number(process.env.API_PORT ?? DEFAULT_PORT);
|
|
41
|
+
new Logger('API-Bootstrap').log(
|
|
42
|
+
formatGatewayBanner({ port, origin, services: GATEWAY_SERVICES }),
|
|
37
43
|
);
|
|
38
44
|
})
|
|
39
45
|
.catch((err) => {
|
|
@@ -5,15 +5,26 @@ import { createClient } from '@supabase/supabase-js';
|
|
|
5
5
|
import * as admin from 'firebase-admin';
|
|
6
6
|
import { SupabaseAuthStrategy } from '@icore/auth-supabase';
|
|
7
7
|
import { FirebaseAuthStrategy, HttpIdentityToolkitClient } from '@icore/auth-firebase';
|
|
8
|
-
import { FakeAuthStrategy } from '@icore/shared';
|
|
8
|
+
import { FakeAuthStrategy, missingEnv, formatEnvBanner } from '@icore/shared';
|
|
9
9
|
import type { AuthStrategy } from '@icore/shared';
|
|
10
10
|
import { Logger } from '@nestjs/common';
|
|
11
11
|
import { AuthController } from './auth.controller';
|
|
12
12
|
|
|
13
|
+
const ENV_PATH = 'apps/microservices/auth/.env';
|
|
14
|
+
|
|
15
|
+
// Env vars each provider needs (besides AUTH_PROVIDER itself).
|
|
16
|
+
const REQUIRED_ENV: Record<string, string[]> = {
|
|
17
|
+
supabase: ['SUPABASE_URL', 'SUPABASE_SERVICE_ROLE_KEY'],
|
|
18
|
+
firebase: [
|
|
19
|
+
'FB_ADMIN_PROJECT_ID',
|
|
20
|
+
'FB_ADMIN_CLIENT_EMAIL',
|
|
21
|
+
'FB_ADMIN_PRIVATE_KEY',
|
|
22
|
+
'FIREBASE_WEB_API_KEY',
|
|
23
|
+
],
|
|
24
|
+
};
|
|
25
|
+
|
|
13
26
|
function requireEnv(cfg: ConfigService, key: string): string {
|
|
14
|
-
|
|
15
|
-
if (!val) throw new Error(`${key} is not set — check apps/microservices/auth/.env`);
|
|
16
|
-
return val;
|
|
27
|
+
return cfg.getOrThrow<string>(key);
|
|
17
28
|
}
|
|
18
29
|
|
|
19
30
|
function makeFirebaseStrategy(cfg: ConfigService): AuthStrategy {
|
|
@@ -49,28 +60,41 @@ function makeFirebaseStrategy(cfg: ConfigService): AuthStrategy {
|
|
|
49
60
|
{
|
|
50
61
|
provide: 'AuthStrategy',
|
|
51
62
|
useFactory: (cfg: ConfigService): AuthStrategy => {
|
|
63
|
+
const logger = new Logger('AuthStrategy');
|
|
64
|
+
const provider = cfg.get<string>('AUTH_PROVIDER')?.trim();
|
|
65
|
+
const keys = provider ? REQUIRED_ENV[provider] : undefined;
|
|
66
|
+
const missing = keys ? missingEnv((k) => cfg.get<string>(k), keys) : [];
|
|
67
|
+
|
|
68
|
+
// Prod: fail fast — never silently run a fake auth strategy.
|
|
69
|
+
// Dev: warn with a boxed banner + fall back to the in-memory fake.
|
|
70
|
+
const fallback = (reason?: string): AuthStrategy => {
|
|
71
|
+
const banner = formatEnvBanner({
|
|
72
|
+
service: 'auth MS',
|
|
73
|
+
provider,
|
|
74
|
+
missing,
|
|
75
|
+
envPath: ENV_PATH,
|
|
76
|
+
reason,
|
|
77
|
+
});
|
|
78
|
+
if (process.env.NODE_ENV === 'production') throw new Error(banner);
|
|
79
|
+
logger.warn(banner);
|
|
80
|
+
return new FakeAuthStrategy();
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
if (!keys || missing.length > 0) return fallback();
|
|
84
|
+
|
|
52
85
|
try {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
);
|
|
61
|
-
return new SupabaseAuthStrategy({ client });
|
|
62
|
-
}
|
|
63
|
-
case 'firebase':
|
|
64
|
-
return makeFirebaseStrategy(cfg);
|
|
65
|
-
default:
|
|
66
|
-
throw new Error(`Unsupported AUTH_PROVIDER: ${provider}`);
|
|
86
|
+
if (provider === 'supabase') {
|
|
87
|
+
const client = createClient(
|
|
88
|
+
requireEnv(cfg, 'SUPABASE_URL'),
|
|
89
|
+
requireEnv(cfg, 'SUPABASE_SERVICE_ROLE_KEY'),
|
|
90
|
+
{ auth: { autoRefreshToken: false, persistSession: false } },
|
|
91
|
+
);
|
|
92
|
+
return new SupabaseAuthStrategy({ client });
|
|
67
93
|
}
|
|
94
|
+
return makeFirebaseStrategy(cfg);
|
|
68
95
|
} catch (err) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
`Requests will fail until apps/microservices/auth/.env is set.`,
|
|
72
|
-
);
|
|
73
|
-
return new FakeAuthStrategy();
|
|
96
|
+
// Vars present but invalid (e.g. placeholder URL the SDK rejects).
|
|
97
|
+
return fallback(err instanceof Error ? err.message : String(err));
|
|
74
98
|
}
|
|
75
99
|
},
|
|
76
100
|
inject: [ConfigService],
|
|
@@ -5,15 +5,22 @@ import { createClient } from '@supabase/supabase-js';
|
|
|
5
5
|
import * as admin from 'firebase-admin';
|
|
6
6
|
import { SupabaseDBStrategy } from '@icore/db-supabase';
|
|
7
7
|
import { FirestoreDBStrategy } from '@icore/db-firestore';
|
|
8
|
-
import { FakeDBStrategy } from '@icore/shared';
|
|
8
|
+
import { FakeDBStrategy, missingEnv, formatEnvBanner } from '@icore/shared';
|
|
9
9
|
import type { DBStrategy } from '@icore/shared';
|
|
10
10
|
import { Logger } from '@nestjs/common';
|
|
11
11
|
import { NotesController } from './notes.controller';
|
|
12
12
|
|
|
13
|
+
const ENV_PATH = 'apps/microservices/notes/.env';
|
|
14
|
+
|
|
15
|
+
// DB_PROVIDER accepts supabase | firestore | firebase (latter two are Firestore).
|
|
16
|
+
const REQUIRED_ENV: Record<string, string[]> = {
|
|
17
|
+
supabase: ['SUPABASE_URL', 'SUPABASE_SERVICE_ROLE_KEY'],
|
|
18
|
+
firestore: ['FB_ADMIN_PROJECT_ID', 'FB_ADMIN_CLIENT_EMAIL', 'FB_ADMIN_PRIVATE_KEY'],
|
|
19
|
+
firebase: ['FB_ADMIN_PROJECT_ID', 'FB_ADMIN_CLIENT_EMAIL', 'FB_ADMIN_PRIVATE_KEY'],
|
|
20
|
+
};
|
|
21
|
+
|
|
13
22
|
function requireEnv(cfg: ConfigService, key: string): string {
|
|
14
|
-
|
|
15
|
-
if (!val) throw new Error(`${key} is not set — check apps/microservices/notes/.env`);
|
|
16
|
-
return val;
|
|
23
|
+
return cfg.getOrThrow<string>(key);
|
|
17
24
|
}
|
|
18
25
|
|
|
19
26
|
@Module({
|
|
@@ -31,8 +38,27 @@ function requireEnv(cfg: ConfigService, key: string): string {
|
|
|
31
38
|
{
|
|
32
39
|
provide: 'DBStrategy',
|
|
33
40
|
useFactory: (cfg: ConfigService): DBStrategy => {
|
|
41
|
+
const logger = new Logger('DBStrategy');
|
|
42
|
+
const provider = cfg.get<string>('DB_PROVIDER')?.trim();
|
|
43
|
+
const keys = provider ? REQUIRED_ENV[provider] : undefined;
|
|
44
|
+
const missing = keys ? missingEnv((k) => cfg.get<string>(k), keys) : [];
|
|
45
|
+
|
|
46
|
+
const fallback = (reason?: string): DBStrategy => {
|
|
47
|
+
const banner = formatEnvBanner({
|
|
48
|
+
service: 'notes MS',
|
|
49
|
+
provider,
|
|
50
|
+
missing,
|
|
51
|
+
envPath: ENV_PATH,
|
|
52
|
+
reason,
|
|
53
|
+
});
|
|
54
|
+
if (process.env.NODE_ENV === 'production') throw new Error(banner);
|
|
55
|
+
logger.warn(banner);
|
|
56
|
+
return new FakeDBStrategy();
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
if (!keys || missing.length > 0) return fallback();
|
|
60
|
+
|
|
34
61
|
try {
|
|
35
|
-
const provider = requireEnv(cfg, 'DB_PROVIDER');
|
|
36
62
|
if (provider === 'supabase') {
|
|
37
63
|
const client = createClient(
|
|
38
64
|
requireEnv(cfg, 'SUPABASE_URL'),
|
|
@@ -41,29 +67,22 @@ function requireEnv(cfg: ConfigService, key: string): string {
|
|
|
41
67
|
);
|
|
42
68
|
return new SupabaseDBStrategy({ client });
|
|
43
69
|
}
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
admin.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}),
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
return new FirestoreDBStrategy({
|
|
55
|
-
db: admin.firestore() as unknown as ConstructorParameters<
|
|
56
|
-
typeof FirestoreDBStrategy
|
|
57
|
-
>[0]['db'],
|
|
70
|
+
if (admin.apps.length === 0) {
|
|
71
|
+
admin.initializeApp({
|
|
72
|
+
credential: admin.credential.cert({
|
|
73
|
+
projectId: requireEnv(cfg, 'FB_ADMIN_PROJECT_ID'),
|
|
74
|
+
clientEmail: requireEnv(cfg, 'FB_ADMIN_CLIENT_EMAIL'),
|
|
75
|
+
privateKey: requireEnv(cfg, 'FB_ADMIN_PRIVATE_KEY').replace(/\\n/g, '\n'),
|
|
76
|
+
}),
|
|
58
77
|
});
|
|
59
78
|
}
|
|
60
|
-
|
|
79
|
+
return new FirestoreDBStrategy({
|
|
80
|
+
db: admin.firestore() as unknown as ConstructorParameters<
|
|
81
|
+
typeof FirestoreDBStrategy
|
|
82
|
+
>[0]['db'],
|
|
83
|
+
});
|
|
61
84
|
} catch (err) {
|
|
62
|
-
|
|
63
|
-
`Not configured: ${err instanceof Error ? err.message : String(err)}. ` +
|
|
64
|
-
`Requests will fail until apps/microservices/notes/.env is set.`,
|
|
65
|
-
);
|
|
66
|
-
return new FakeDBStrategy();
|
|
85
|
+
return fallback(err instanceof Error ? err.message : String(err));
|
|
67
86
|
}
|
|
68
87
|
},
|
|
69
88
|
inject: [ConfigService],
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
|
-
import { Module } from '@nestjs/common';
|
|
2
|
+
import { Module, Logger } from '@nestjs/common';
|
|
3
3
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
4
4
|
import { PaymentRegistry, PaypalStrategy, createPayment } from '@idevconn/payment';
|
|
5
|
+
import { missingEnv, formatEnvBanner } from '@icore/shared';
|
|
5
6
|
import { PaymentController } from './payment.controller';
|
|
6
7
|
|
|
8
|
+
const ENV_PATH = 'apps/microservices/payment/.env';
|
|
9
|
+
|
|
10
|
+
const REQUIRED_ENV: Record<string, string[]> = {
|
|
11
|
+
paypal: ['PAYPAL_CLIENT_ID', 'PAYPAL_CLIENT_SECRET'],
|
|
12
|
+
};
|
|
13
|
+
|
|
7
14
|
@Module({
|
|
8
15
|
imports: [
|
|
9
16
|
ConfigModule.forRoot({
|
|
@@ -19,19 +26,35 @@ import { PaymentController } from './payment.controller';
|
|
|
19
26
|
{
|
|
20
27
|
provide: PaymentRegistry,
|
|
21
28
|
useFactory: (cfg: ConfigService) => {
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
const logger = new Logger('PaymentRegistry');
|
|
30
|
+
const provider = (cfg.get<string>('PAYMENT_PROVIDER') ?? 'paypal').trim();
|
|
31
|
+
const keys = REQUIRED_ENV[provider];
|
|
32
|
+
if (!keys) throw new Error(`Unsupported PAYMENT_PROVIDER: ${provider}`);
|
|
33
|
+
|
|
34
|
+
const missing = missingEnv((k) => cfg.get<string>(k), keys);
|
|
35
|
+
if (missing.length > 0) {
|
|
36
|
+
const banner = formatEnvBanner({
|
|
37
|
+
service: 'payment MS',
|
|
38
|
+
provider,
|
|
39
|
+
missing,
|
|
40
|
+
envPath: ENV_PATH,
|
|
41
|
+
headline: `⚠ payment MS — ${provider} credentials missing (payments will fail)`,
|
|
32
42
|
});
|
|
43
|
+
// Prod: fail fast. Dev: warn — PayPal is lazy, so the MS still boots
|
|
44
|
+
// and only payment calls fail until creds are set.
|
|
45
|
+
if (process.env.NODE_ENV === 'production') throw new Error(banner);
|
|
46
|
+
logger.warn(banner);
|
|
33
47
|
}
|
|
34
|
-
|
|
48
|
+
|
|
49
|
+
return createPayment({
|
|
50
|
+
strategies: {
|
|
51
|
+
paypal: new PaypalStrategy({
|
|
52
|
+
clientId: cfg.get<string>('PAYPAL_CLIENT_ID') ?? '',
|
|
53
|
+
secret: cfg.get<string>('PAYPAL_CLIENT_SECRET') ?? '',
|
|
54
|
+
environment: cfg.get<'sandbox' | 'live'>('PAYPAL_ENVIRONMENT') ?? 'sandbox',
|
|
55
|
+
}),
|
|
56
|
+
},
|
|
57
|
+
});
|
|
35
58
|
},
|
|
36
59
|
inject: [ConfigService],
|
|
37
60
|
},
|
|
@@ -7,15 +7,26 @@ import { v2 as cloudinary } from 'cloudinary';
|
|
|
7
7
|
import { SupabaseStorageStrategy } from '@icore/storage-supabase';
|
|
8
8
|
import { FirebaseStorageStrategy } from '@icore/storage-firebase';
|
|
9
9
|
import { CloudinaryStorageStrategy, type CloudinaryApiLike } from '@icore/storage-cloudinary';
|
|
10
|
-
import { FakeStorageStrategy } from '@icore/shared';
|
|
10
|
+
import { FakeStorageStrategy, missingEnv, formatEnvBanner } from '@icore/shared';
|
|
11
11
|
import type { StorageStrategy } from '@icore/shared';
|
|
12
12
|
import { Logger } from '@nestjs/common';
|
|
13
13
|
import { StorageController } from './storage.controller';
|
|
14
14
|
|
|
15
|
+
const ENV_PATH = 'apps/microservices/upload/.env';
|
|
16
|
+
|
|
17
|
+
const REQUIRED_ENV: Record<string, string[]> = {
|
|
18
|
+
supabase: ['SUPABASE_URL', 'SUPABASE_SERVICE_ROLE_KEY', 'SUPABASE_STORAGE_BUCKET'],
|
|
19
|
+
firebase: [
|
|
20
|
+
'FIREBASE_STORAGE_BUCKET',
|
|
21
|
+
'FB_ADMIN_PROJECT_ID',
|
|
22
|
+
'FB_ADMIN_CLIENT_EMAIL',
|
|
23
|
+
'FB_ADMIN_PRIVATE_KEY',
|
|
24
|
+
],
|
|
25
|
+
cloudinary: ['CLOUDINARY_CLOUD_NAME', 'CLOUDINARY_API_KEY', 'CLOUDINARY_API_SECRET'],
|
|
26
|
+
};
|
|
27
|
+
|
|
15
28
|
function requireEnv(cfg: ConfigService, key: string): string {
|
|
16
|
-
|
|
17
|
-
if (!val) throw new Error(`${key} is not set — check apps/microservices/upload/.env`);
|
|
18
|
-
return val;
|
|
29
|
+
return cfg.getOrThrow<string>(key);
|
|
19
30
|
}
|
|
20
31
|
|
|
21
32
|
function makeFirebaseStorage(cfg: ConfigService): StorageStrategy {
|
|
@@ -97,33 +108,42 @@ function makeCloudinaryStorage(cfg: ConfigService): StorageStrategy {
|
|
|
97
108
|
{
|
|
98
109
|
provide: 'StorageStrategy',
|
|
99
110
|
useFactory: (cfg: ConfigService): StorageStrategy => {
|
|
111
|
+
const logger = new Logger('StorageStrategy');
|
|
112
|
+
const provider = cfg.get<string>('STORAGE_PROVIDER')?.trim();
|
|
113
|
+
const keys = provider ? REQUIRED_ENV[provider] : undefined;
|
|
114
|
+
const missing = keys ? missingEnv((k) => cfg.get<string>(k), keys) : [];
|
|
115
|
+
|
|
116
|
+
const fallback = (reason?: string): StorageStrategy => {
|
|
117
|
+
const banner = formatEnvBanner({
|
|
118
|
+
service: 'upload MS',
|
|
119
|
+
provider,
|
|
120
|
+
missing,
|
|
121
|
+
envPath: ENV_PATH,
|
|
122
|
+
reason,
|
|
123
|
+
});
|
|
124
|
+
if (process.env.NODE_ENV === 'production') throw new Error(banner);
|
|
125
|
+
logger.warn(banner);
|
|
126
|
+
return new FakeStorageStrategy();
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
if (!keys || missing.length > 0) return fallback();
|
|
130
|
+
|
|
100
131
|
try {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
bucket: requireEnv(cfg, 'SUPABASE_STORAGE_BUCKET'),
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
case 'firebase':
|
|
115
|
-
return makeFirebaseStorage(cfg);
|
|
116
|
-
case 'cloudinary':
|
|
117
|
-
return makeCloudinaryStorage(cfg);
|
|
118
|
-
default:
|
|
119
|
-
throw new Error(`Unsupported STORAGE_PROVIDER: ${provider}`);
|
|
132
|
+
if (provider === 'supabase') {
|
|
133
|
+
const client = createClient(
|
|
134
|
+
requireEnv(cfg, 'SUPABASE_URL'),
|
|
135
|
+
requireEnv(cfg, 'SUPABASE_SERVICE_ROLE_KEY'),
|
|
136
|
+
{ auth: { autoRefreshToken: false, persistSession: false } },
|
|
137
|
+
);
|
|
138
|
+
return new SupabaseStorageStrategy({
|
|
139
|
+
client,
|
|
140
|
+
bucket: requireEnv(cfg, 'SUPABASE_STORAGE_BUCKET'),
|
|
141
|
+
});
|
|
120
142
|
}
|
|
143
|
+
if (provider === 'firebase') return makeFirebaseStorage(cfg);
|
|
144
|
+
return makeCloudinaryStorage(cfg);
|
|
121
145
|
} catch (err) {
|
|
122
|
-
|
|
123
|
-
`Not configured: ${err instanceof Error ? err.message : String(err)}. ` +
|
|
124
|
-
`Requests will fail until apps/microservices/upload/.env is set.`,
|
|
125
|
-
);
|
|
126
|
-
return new FakeStorageStrategy();
|
|
146
|
+
return fallback(err instanceof Error ? err.message : String(err));
|
|
127
147
|
}
|
|
128
148
|
},
|
|
129
149
|
inject: [ConfigService],
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# ─── Client environment variables ────────────────────────────────────────
|
|
2
|
+
# API gateway base URL.
|
|
3
|
+
# /api — default; dev-server proxies it to http://localhost:3001 (see vite.config.mts)
|
|
4
|
+
# http://localhost:3001/api — set explicitly if you want to bypass the proxy
|
|
5
|
+
# https://your-domain.com/api — production override when building a standalone SPA
|
|
6
|
+
VITE_API_URL=/api
|
|
7
|
+
|
|
@@ -6,8 +6,10 @@ import { tanstackRouter } from '@tanstack/router-plugin/vite';
|
|
|
6
6
|
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
|
7
7
|
import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
|
|
8
8
|
import {
|
|
9
|
+
apiInfoPlugin,
|
|
9
10
|
commonDefines,
|
|
10
11
|
commonManualChunks,
|
|
12
|
+
commonServer,
|
|
11
13
|
commonTestConfig,
|
|
12
14
|
injectAppVersionPlugin,
|
|
13
15
|
noServerModulesPlugin,
|
|
@@ -27,10 +29,7 @@ function depVersion(name: string): string {
|
|
|
27
29
|
export default defineConfig(() => ({
|
|
28
30
|
root: import.meta.dirname,
|
|
29
31
|
cacheDir: '../../../node_modules/.vite/apps/templates/client-antd',
|
|
30
|
-
server:
|
|
31
|
-
port: 4201,
|
|
32
|
-
host: 'localhost',
|
|
33
|
-
},
|
|
32
|
+
server: commonServer(4201),
|
|
34
33
|
preview: {
|
|
35
34
|
port: 4201,
|
|
36
35
|
host: 'localhost',
|
|
@@ -49,6 +48,7 @@ export default defineConfig(() => ({
|
|
|
49
48
|
nxViteTsPaths(),
|
|
50
49
|
nxCopyAssetsPlugin(['*.md']),
|
|
51
50
|
noServerModulesPlugin(),
|
|
51
|
+
apiInfoPlugin(),
|
|
52
52
|
injectAppVersionPlugin(rootPackageJson),
|
|
53
53
|
],
|
|
54
54
|
// Uncomment this if you are using workers.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# ─── Client environment variables ────────────────────────────────────────
|
|
2
|
+
# API gateway base URL.
|
|
3
|
+
# /api — default; dev-server proxies it to http://localhost:3001 (see vite.config.mts)
|
|
4
|
+
# http://localhost:3001/api — set explicitly if you want to bypass the proxy
|
|
5
|
+
# https://your-domain.com/api — production override when building a standalone SPA
|
|
6
|
+
VITE_API_URL=/api
|
|
7
|
+
|
|
@@ -6,8 +6,10 @@ import { tanstackRouter } from '@tanstack/router-plugin/vite';
|
|
|
6
6
|
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
|
7
7
|
import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
|
|
8
8
|
import {
|
|
9
|
+
apiInfoPlugin,
|
|
9
10
|
commonDefines,
|
|
10
11
|
commonManualChunks,
|
|
12
|
+
commonServer,
|
|
11
13
|
commonTestConfig,
|
|
12
14
|
injectAppVersionPlugin,
|
|
13
15
|
noServerModulesPlugin,
|
|
@@ -27,10 +29,7 @@ function depVersion(name: string): string {
|
|
|
27
29
|
export default defineConfig(() => ({
|
|
28
30
|
root: import.meta.dirname,
|
|
29
31
|
cacheDir: '../../../node_modules/.vite/client-mui',
|
|
30
|
-
server:
|
|
31
|
-
port: 4202,
|
|
32
|
-
host: 'localhost',
|
|
33
|
-
},
|
|
32
|
+
server: commonServer(4202),
|
|
34
33
|
preview: {
|
|
35
34
|
port: 4202,
|
|
36
35
|
host: 'localhost',
|
|
@@ -49,6 +48,7 @@ export default defineConfig(() => ({
|
|
|
49
48
|
nxViteTsPaths(),
|
|
50
49
|
nxCopyAssetsPlugin(['*.md']),
|
|
51
50
|
noServerModulesPlugin(),
|
|
51
|
+
apiInfoPlugin(),
|
|
52
52
|
injectAppVersionPlugin(rootPackageJson),
|
|
53
53
|
],
|
|
54
54
|
// Uncomment this if you are using workers.
|
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ─── Client environment variables ────────────────────────────────────────
|
|
2
|
+
# API gateway base URL.
|
|
3
|
+
# /api — default; dev-server proxies it to http://localhost:3001 (see vite.config.mts)
|
|
4
|
+
# http://localhost:3001/api — set explicitly if you want to bypass the proxy
|
|
5
|
+
# https://your-domain.com/api — production override when building a standalone SPA
|
|
2
6
|
VITE_API_URL=/api
|
|
7
|
+
|
|
@@ -7,8 +7,10 @@ import { tanstackRouter } from '@tanstack/router-plugin/vite';
|
|
|
7
7
|
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
|
8
8
|
import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
|
|
9
9
|
import {
|
|
10
|
+
apiInfoPlugin,
|
|
10
11
|
commonDefines,
|
|
11
12
|
commonManualChunks,
|
|
13
|
+
commonServer,
|
|
12
14
|
commonTestConfig,
|
|
13
15
|
injectAppVersionPlugin,
|
|
14
16
|
noServerModulesPlugin,
|
|
@@ -28,10 +30,7 @@ function depVersion(name: string): string {
|
|
|
28
30
|
export default defineConfig(() => ({
|
|
29
31
|
root: import.meta.dirname,
|
|
30
32
|
cacheDir: '../../../node_modules/.vite/apps/templates/client-shadcn',
|
|
31
|
-
server:
|
|
32
|
-
port: 4200,
|
|
33
|
-
host: 'localhost',
|
|
34
|
-
},
|
|
33
|
+
server: commonServer(4200),
|
|
35
34
|
preview: {
|
|
36
35
|
port: 4200,
|
|
37
36
|
host: 'localhost',
|
|
@@ -51,6 +50,7 @@ export default defineConfig(() => ({
|
|
|
51
50
|
nxViteTsPaths(),
|
|
52
51
|
nxCopyAssetsPlugin(['*.md']),
|
|
53
52
|
noServerModulesPlugin(),
|
|
53
|
+
apiInfoPlugin(),
|
|
54
54
|
injectAppVersionPlugin(rootPackageJson),
|
|
55
55
|
],
|
|
56
56
|
// Uncomment this if you are using workers.
|
|
@@ -3,7 +3,7 @@ import { ClientsModule } from '@nestjs/microservices';
|
|
|
3
3
|
import { buildTransport } from '@icore/shared';
|
|
4
4
|
import { AuthClientService } from './auth-client.service';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
import { AUTH_CLIENT } from './auth-client.tokens';
|
|
7
7
|
|
|
8
8
|
@Module({})
|
|
9
9
|
export class AuthClientModule {
|
|
@@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
|
|
2
2
|
import { ClientProxy } from '@nestjs/microservices';
|
|
3
3
|
import { firstValueFrom } from 'rxjs';
|
|
4
4
|
import type { AuthSession, OAuthProvider, OAuthStartResult, VerifiedToken } from '@icore/shared';
|
|
5
|
-
import { AUTH_CLIENT } from './auth-client.
|
|
5
|
+
import { AUTH_CLIENT } from './auth-client.tokens';
|
|
6
6
|
|
|
7
7
|
@Injectable()
|
|
8
8
|
export class AuthClientService {
|
|
@@ -2,7 +2,7 @@ import { DynamicModule, Module } from '@nestjs/common';
|
|
|
2
2
|
import { ConfigService } from '@nestjs/config';
|
|
3
3
|
import { JobsClientService } from './jobs-client.service';
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
import { JOBS_REDIS_URL } from './jobs-client.tokens';
|
|
6
6
|
|
|
7
7
|
@Module({})
|
|
8
8
|
export class JobsClientModule {
|