@bloomneo/appkit 1.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +902 -0
- package/bin/appkit.js +71 -0
- package/bin/commands/generate.js +1050 -0
- package/bin/templates/backend/README.md.template +39 -0
- package/bin/templates/backend/api.http.template +0 -0
- package/bin/templates/backend/docs/APPKIT_CLI.md +507 -0
- package/bin/templates/backend/docs/APPKIT_COMMENTS_GUIDELINES.md +61 -0
- package/bin/templates/backend/docs/APPKIT_LLM_GUIDE.md +2539 -0
- package/bin/templates/backend/package.json.template +34 -0
- package/bin/templates/backend/src/api/features/welcome/welcome.http.template +29 -0
- package/bin/templates/backend/src/api/features/welcome/welcome.route.ts.template +36 -0
- package/bin/templates/backend/src/api/features/welcome/welcome.service.ts.template +88 -0
- package/bin/templates/backend/src/api/features/welcome/welcome.types.ts.template +18 -0
- package/bin/templates/backend/src/api/lib/api-router.ts.template +84 -0
- package/bin/templates/backend/src/api/server.ts.template +188 -0
- package/bin/templates/backend/tsconfig.api.json.template +24 -0
- package/bin/templates/backend/tsconfig.json.template +40 -0
- package/bin/templates/feature/feature.http.template +63 -0
- package/bin/templates/feature/feature.route.ts.template +36 -0
- package/bin/templates/feature/feature.service.ts.template +81 -0
- package/bin/templates/feature/feature.types.ts.template +23 -0
- package/bin/templates/feature-db/feature.http.template +63 -0
- package/bin/templates/feature-db/feature.model.ts.template +74 -0
- package/bin/templates/feature-db/feature.route.ts.template +58 -0
- package/bin/templates/feature-db/feature.service.ts.template +231 -0
- package/bin/templates/feature-db/feature.types.ts.template +25 -0
- package/bin/templates/feature-db/schema-addition.prisma.template +9 -0
- package/bin/templates/feature-db/seeding/README.md.template +57 -0
- package/bin/templates/feature-db/seeding/feature.seed.js.template +67 -0
- package/bin/templates/feature-user/schema-addition.prisma.template +19 -0
- package/bin/templates/feature-user/user.http.template +157 -0
- package/bin/templates/feature-user/user.model.ts.template +244 -0
- package/bin/templates/feature-user/user.route.ts.template +379 -0
- package/bin/templates/feature-user/user.seed.js.template +182 -0
- package/bin/templates/feature-user/user.service.ts.template +426 -0
- package/bin/templates/feature-user/user.types.ts.template +127 -0
- package/dist/auth/auth.d.ts +182 -0
- package/dist/auth/auth.d.ts.map +1 -0
- package/dist/auth/auth.js +477 -0
- package/dist/auth/auth.js.map +1 -0
- package/dist/auth/defaults.d.ts +104 -0
- package/dist/auth/defaults.d.ts.map +1 -0
- package/dist/auth/defaults.js +374 -0
- package/dist/auth/defaults.js.map +1 -0
- package/dist/auth/index.d.ts +70 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +94 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/cache/cache.d.ts +118 -0
- package/dist/cache/cache.d.ts.map +1 -0
- package/dist/cache/cache.js +249 -0
- package/dist/cache/cache.js.map +1 -0
- package/dist/cache/defaults.d.ts +63 -0
- package/dist/cache/defaults.d.ts.map +1 -0
- package/dist/cache/defaults.js +193 -0
- package/dist/cache/defaults.js.map +1 -0
- package/dist/cache/index.d.ts +101 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +203 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/strategies/memory.d.ts +138 -0
- package/dist/cache/strategies/memory.d.ts.map +1 -0
- package/dist/cache/strategies/memory.js +348 -0
- package/dist/cache/strategies/memory.js.map +1 -0
- package/dist/cache/strategies/redis.d.ts +105 -0
- package/dist/cache/strategies/redis.d.ts.map +1 -0
- package/dist/cache/strategies/redis.js +318 -0
- package/dist/cache/strategies/redis.js.map +1 -0
- package/dist/config/config.d.ts +62 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +107 -0
- package/dist/config/config.js.map +1 -0
- package/dist/config/defaults.d.ts +44 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +217 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/index.d.ts +105 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +163 -0
- package/dist/config/index.js.map +1 -0
- package/dist/database/adapters/mongoose.d.ts +106 -0
- package/dist/database/adapters/mongoose.d.ts.map +1 -0
- package/dist/database/adapters/mongoose.js +480 -0
- package/dist/database/adapters/mongoose.js.map +1 -0
- package/dist/database/adapters/prisma.d.ts +106 -0
- package/dist/database/adapters/prisma.d.ts.map +1 -0
- package/dist/database/adapters/prisma.js +494 -0
- package/dist/database/adapters/prisma.js.map +1 -0
- package/dist/database/defaults.d.ts +87 -0
- package/dist/database/defaults.d.ts.map +1 -0
- package/dist/database/defaults.js +271 -0
- package/dist/database/defaults.js.map +1 -0
- package/dist/database/index.d.ts +137 -0
- package/dist/database/index.d.ts.map +1 -0
- package/dist/database/index.js +490 -0
- package/dist/database/index.js.map +1 -0
- package/dist/email/defaults.d.ts +100 -0
- package/dist/email/defaults.d.ts.map +1 -0
- package/dist/email/defaults.js +400 -0
- package/dist/email/defaults.js.map +1 -0
- package/dist/email/email.d.ts +139 -0
- package/dist/email/email.d.ts.map +1 -0
- package/dist/email/email.js +316 -0
- package/dist/email/email.js.map +1 -0
- package/dist/email/index.d.ts +176 -0
- package/dist/email/index.d.ts.map +1 -0
- package/dist/email/index.js +251 -0
- package/dist/email/index.js.map +1 -0
- package/dist/email/strategies/console.d.ts +90 -0
- package/dist/email/strategies/console.d.ts.map +1 -0
- package/dist/email/strategies/console.js +268 -0
- package/dist/email/strategies/console.js.map +1 -0
- package/dist/email/strategies/resend.d.ts +84 -0
- package/dist/email/strategies/resend.d.ts.map +1 -0
- package/dist/email/strategies/resend.js +266 -0
- package/dist/email/strategies/resend.js.map +1 -0
- package/dist/email/strategies/smtp.d.ts +77 -0
- package/dist/email/strategies/smtp.d.ts.map +1 -0
- package/dist/email/strategies/smtp.js +286 -0
- package/dist/email/strategies/smtp.js.map +1 -0
- package/dist/error/defaults.d.ts +40 -0
- package/dist/error/defaults.d.ts.map +1 -0
- package/dist/error/defaults.js +75 -0
- package/dist/error/defaults.js.map +1 -0
- package/dist/error/error.d.ts +140 -0
- package/dist/error/error.d.ts.map +1 -0
- package/dist/error/error.js +200 -0
- package/dist/error/error.js.map +1 -0
- package/dist/error/index.d.ts +145 -0
- package/dist/error/index.d.ts.map +1 -0
- package/dist/error/index.js +145 -0
- package/dist/error/index.js.map +1 -0
- package/dist/event/defaults.d.ts +111 -0
- package/dist/event/defaults.d.ts.map +1 -0
- package/dist/event/defaults.js +378 -0
- package/dist/event/defaults.js.map +1 -0
- package/dist/event/event.d.ts +171 -0
- package/dist/event/event.d.ts.map +1 -0
- package/dist/event/event.js +391 -0
- package/dist/event/event.js.map +1 -0
- package/dist/event/index.d.ts +173 -0
- package/dist/event/index.d.ts.map +1 -0
- package/dist/event/index.js +302 -0
- package/dist/event/index.js.map +1 -0
- package/dist/event/strategies/memory.d.ts +122 -0
- package/dist/event/strategies/memory.d.ts.map +1 -0
- package/dist/event/strategies/memory.js +331 -0
- package/dist/event/strategies/memory.js.map +1 -0
- package/dist/event/strategies/redis.d.ts +115 -0
- package/dist/event/strategies/redis.d.ts.map +1 -0
- package/dist/event/strategies/redis.js +434 -0
- package/dist/event/strategies/redis.js.map +1 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +72 -0
- package/dist/index.js.map +1 -0
- package/dist/logger/defaults.d.ts +67 -0
- package/dist/logger/defaults.d.ts.map +1 -0
- package/dist/logger/defaults.js +213 -0
- package/dist/logger/defaults.js.map +1 -0
- package/dist/logger/index.d.ts +84 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/logger/index.js +101 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/logger/logger.d.ts +165 -0
- package/dist/logger/logger.d.ts.map +1 -0
- package/dist/logger/logger.js +843 -0
- package/dist/logger/logger.js.map +1 -0
- package/dist/logger/transports/console.d.ts +102 -0
- package/dist/logger/transports/console.d.ts.map +1 -0
- package/dist/logger/transports/console.js +276 -0
- package/dist/logger/transports/console.js.map +1 -0
- package/dist/logger/transports/database.d.ts +153 -0
- package/dist/logger/transports/database.d.ts.map +1 -0
- package/dist/logger/transports/database.js +539 -0
- package/dist/logger/transports/database.js.map +1 -0
- package/dist/logger/transports/file.d.ts +146 -0
- package/dist/logger/transports/file.d.ts.map +1 -0
- package/dist/logger/transports/file.js +464 -0
- package/dist/logger/transports/file.js.map +1 -0
- package/dist/logger/transports/http.d.ts +128 -0
- package/dist/logger/transports/http.d.ts.map +1 -0
- package/dist/logger/transports/http.js +401 -0
- package/dist/logger/transports/http.js.map +1 -0
- package/dist/logger/transports/webhook.d.ts +152 -0
- package/dist/logger/transports/webhook.d.ts.map +1 -0
- package/dist/logger/transports/webhook.js +485 -0
- package/dist/logger/transports/webhook.js.map +1 -0
- package/dist/queue/defaults.d.ts +66 -0
- package/dist/queue/defaults.d.ts.map +1 -0
- package/dist/queue/defaults.js +205 -0
- package/dist/queue/defaults.js.map +1 -0
- package/dist/queue/index.d.ts +124 -0
- package/dist/queue/index.d.ts.map +1 -0
- package/dist/queue/index.js +116 -0
- package/dist/queue/index.js.map +1 -0
- package/dist/queue/queue.d.ts +156 -0
- package/dist/queue/queue.d.ts.map +1 -0
- package/dist/queue/queue.js +387 -0
- package/dist/queue/queue.js.map +1 -0
- package/dist/queue/transports/database.d.ts +165 -0
- package/dist/queue/transports/database.d.ts.map +1 -0
- package/dist/queue/transports/database.js +595 -0
- package/dist/queue/transports/database.js.map +1 -0
- package/dist/queue/transports/memory.d.ts +143 -0
- package/dist/queue/transports/memory.d.ts.map +1 -0
- package/dist/queue/transports/memory.js +415 -0
- package/dist/queue/transports/memory.js.map +1 -0
- package/dist/queue/transports/redis.d.ts +203 -0
- package/dist/queue/transports/redis.d.ts.map +1 -0
- package/dist/queue/transports/redis.js +744 -0
- package/dist/queue/transports/redis.js.map +1 -0
- package/dist/security/defaults.d.ts +64 -0
- package/dist/security/defaults.d.ts.map +1 -0
- package/dist/security/defaults.js +159 -0
- package/dist/security/defaults.js.map +1 -0
- package/dist/security/index.d.ts +110 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +160 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/security.d.ts +138 -0
- package/dist/security/security.d.ts.map +1 -0
- package/dist/security/security.js +419 -0
- package/dist/security/security.js.map +1 -0
- package/dist/storage/defaults.d.ts +79 -0
- package/dist/storage/defaults.d.ts.map +1 -0
- package/dist/storage/defaults.js +358 -0
- package/dist/storage/defaults.js.map +1 -0
- package/dist/storage/index.d.ts +153 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +242 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/storage.d.ts +151 -0
- package/dist/storage/storage.d.ts.map +1 -0
- package/dist/storage/storage.js +439 -0
- package/dist/storage/storage.js.map +1 -0
- package/dist/storage/strategies/local.d.ts +117 -0
- package/dist/storage/strategies/local.d.ts.map +1 -0
- package/dist/storage/strategies/local.js +368 -0
- package/dist/storage/strategies/local.js.map +1 -0
- package/dist/storage/strategies/r2.d.ts +130 -0
- package/dist/storage/strategies/r2.d.ts.map +1 -0
- package/dist/storage/strategies/r2.js +470 -0
- package/dist/storage/strategies/r2.js.map +1 -0
- package/dist/storage/strategies/s3.d.ts +121 -0
- package/dist/storage/strategies/s3.d.ts.map +1 -0
- package/dist/storage/strategies/s3.js +461 -0
- package/dist/storage/strategies/s3.js.map +1 -0
- package/dist/util/defaults.d.ts +77 -0
- package/dist/util/defaults.d.ts.map +1 -0
- package/dist/util/defaults.js +193 -0
- package/dist/util/defaults.js.map +1 -0
- package/dist/util/index.d.ts +97 -0
- package/dist/util/index.d.ts.map +1 -0
- package/dist/util/index.js +165 -0
- package/dist/util/index.js.map +1 -0
- package/dist/util/util.d.ts +145 -0
- package/dist/util/util.d.ts.map +1 -0
- package/dist/util/util.js +481 -0
- package/dist/util/util.js.map +1 -0
- package/package.json +234 -0
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ultra-simple database API with org/tenant support and smart connection management
|
|
3
|
+
* @module @bloomneo/appkit/database
|
|
4
|
+
* @file src/database/index.ts
|
|
5
|
+
*
|
|
6
|
+
* @llm-rule WHEN: ALWAYS add tenant_id text field to ALL tables (nullable for future compatibility)
|
|
7
|
+
* @llm-rule NOTE: tenant_id = null (single tenant) or "team-1" (multi-tenant)
|
|
8
|
+
* @llm-rule VARIABLE: const db = await databaseClass.get() - user's data (single or tenant-filtered)
|
|
9
|
+
* @llm-rule VARIABLE: const dbTenants = await databaseClass.getTenants() - all tenants (admin access)
|
|
10
|
+
* @llm-rule VARIABLE: const {orgName}Db = await databaseClass.org('{orgName}').get() - org-specific data
|
|
11
|
+
* @llm-rule VARIABLE: const {orgName}DbTenants = await databaseClass.org('{orgName}').getTenants() - all tenants in org
|
|
12
|
+
*/
|
|
13
|
+
import fs from 'fs';
|
|
14
|
+
import { PrismaAdapter } from './adapters/prisma.js';
|
|
15
|
+
import { MongooseAdapter } from './adapters/mongoose.js';
|
|
16
|
+
// Global instances cache for performance
|
|
17
|
+
const connections = new Map();
|
|
18
|
+
let envWatcher = null;
|
|
19
|
+
/**
|
|
20
|
+
* Environment file watcher for hot reload
|
|
21
|
+
*/
|
|
22
|
+
function setupEnvWatcher() {
|
|
23
|
+
if (envWatcher)
|
|
24
|
+
return;
|
|
25
|
+
try {
|
|
26
|
+
envWatcher = fs.watch('.env', (eventType) => {
|
|
27
|
+
if (eventType === 'change') {
|
|
28
|
+
// Clear require cache and reload
|
|
29
|
+
delete require.cache[require.resolve('dotenv')];
|
|
30
|
+
require('dotenv').config();
|
|
31
|
+
// Clear connection cache to use new URLs
|
|
32
|
+
connections.clear();
|
|
33
|
+
if (process.env.NODE_ENV === 'development') {
|
|
34
|
+
console.log('🔄 [AppKit] .env file reloaded, connections reset');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
// .env file doesn't exist or can't be watched - continue without watching
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Detect organization from request context
|
|
45
|
+
*/
|
|
46
|
+
function detectOrg(req) {
|
|
47
|
+
if (!req)
|
|
48
|
+
return null;
|
|
49
|
+
return (req.headers?.['x-org-id'] ||
|
|
50
|
+
req.user?.org_id ||
|
|
51
|
+
req.params?.orgId ||
|
|
52
|
+
req.query?.org ||
|
|
53
|
+
extractFromSubdomain(req, 'org') ||
|
|
54
|
+
null);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Detect tenant from request context
|
|
58
|
+
*/
|
|
59
|
+
function detectTenant(req) {
|
|
60
|
+
if (!req)
|
|
61
|
+
return null;
|
|
62
|
+
if (!process.env.VOILA_DB_TENANT)
|
|
63
|
+
return null;
|
|
64
|
+
return (req.headers?.['x-tenant-id'] ||
|
|
65
|
+
req.user?.tenant_id ||
|
|
66
|
+
req.params?.tenantId ||
|
|
67
|
+
req.query?.tenant ||
|
|
68
|
+
extractFromSubdomain(req, 'tenant') ||
|
|
69
|
+
null);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Extract org/tenant from subdomain
|
|
73
|
+
*/
|
|
74
|
+
function extractFromSubdomain(req, type) {
|
|
75
|
+
try {
|
|
76
|
+
const host = req.headers?.host || req.hostname;
|
|
77
|
+
if (!host)
|
|
78
|
+
return null;
|
|
79
|
+
const parts = host.split('.');
|
|
80
|
+
if (parts.length >= 3) {
|
|
81
|
+
const subdomain = parts[0];
|
|
82
|
+
// Skip common subdomains
|
|
83
|
+
if (!['www', 'api', 'admin', 'app'].includes(subdomain)) {
|
|
84
|
+
return subdomain;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Auto-detect database adapter from URL
|
|
95
|
+
*/
|
|
96
|
+
function detectAdapter(url) {
|
|
97
|
+
if (url.includes('postgresql') || url.includes('postgres')) {
|
|
98
|
+
return 'prisma';
|
|
99
|
+
}
|
|
100
|
+
if (url.includes('mongodb')) {
|
|
101
|
+
return 'mongoose';
|
|
102
|
+
}
|
|
103
|
+
return 'prisma'; // Default fallback
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Get database URL for organization
|
|
107
|
+
*/
|
|
108
|
+
function getOrgUrl(orgId) {
|
|
109
|
+
if (!orgId)
|
|
110
|
+
return process.env.DATABASE_URL || '';
|
|
111
|
+
// Check for specific org URL
|
|
112
|
+
const orgUrl = process.env[`ORG_${orgId.toUpperCase()}`];
|
|
113
|
+
if (orgUrl)
|
|
114
|
+
return orgUrl;
|
|
115
|
+
// Check for pattern in base URL
|
|
116
|
+
const baseUrl = process.env.DATABASE_URL;
|
|
117
|
+
if (baseUrl?.includes('{org}')) {
|
|
118
|
+
return baseUrl.replace('{org}', orgId);
|
|
119
|
+
}
|
|
120
|
+
return baseUrl || '';
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Create database client with caching
|
|
124
|
+
*/
|
|
125
|
+
async function createClient(url, tenantId = null, orgId = null) {
|
|
126
|
+
const cacheKey = `${url}_${tenantId || 'null'}_${orgId || 'null'}`;
|
|
127
|
+
if (connections.has(cacheKey)) {
|
|
128
|
+
return connections.get(cacheKey);
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
// Detect and create adapter
|
|
132
|
+
const adapterType = detectAdapter(url);
|
|
133
|
+
const adapter = adapterType === 'mongoose' ? new MongooseAdapter({ url }) : new PrismaAdapter({ url });
|
|
134
|
+
// Create client
|
|
135
|
+
let client = await adapter.createClient({ url });
|
|
136
|
+
// Apply tenant middleware if needed
|
|
137
|
+
if (tenantId && adapter.applyTenantMiddleware) {
|
|
138
|
+
client = await adapter.applyTenantMiddleware(client, tenantId, {
|
|
139
|
+
fieldName: 'tenant_id',
|
|
140
|
+
orgId
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
// Add metadata
|
|
144
|
+
client._appKit = true;
|
|
145
|
+
client._orgId = orgId || undefined;
|
|
146
|
+
client._tenantId = tenantId || undefined;
|
|
147
|
+
client._url = url;
|
|
148
|
+
// Cache connection
|
|
149
|
+
connections.set(cacheKey, client);
|
|
150
|
+
return client;
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
throw new Error(`Failed to create database connection: ${error.message}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Organization database builder
|
|
158
|
+
*/
|
|
159
|
+
class OrgDatabase {
|
|
160
|
+
orgId;
|
|
161
|
+
constructor(orgId) {
|
|
162
|
+
this.orgId = orgId;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Get organization database (tenant-filtered if tenant mode enabled)
|
|
166
|
+
*/
|
|
167
|
+
async get(req = null) {
|
|
168
|
+
const tenantId = detectTenant(req);
|
|
169
|
+
const url = getOrgUrl(this.orgId);
|
|
170
|
+
if (!url) {
|
|
171
|
+
throw new Error(`No database URL found for organization '${this.orgId}'`);
|
|
172
|
+
}
|
|
173
|
+
return await createClient(url, tenantId, this.orgId);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Get all tenants in organization (admin access)
|
|
177
|
+
*/
|
|
178
|
+
async getTenants(req = null) {
|
|
179
|
+
const url = getOrgUrl(this.orgId);
|
|
180
|
+
if (!url) {
|
|
181
|
+
throw new Error(`No database URL found for organization '${this.orgId}'`);
|
|
182
|
+
}
|
|
183
|
+
// No tenant filtering - admin sees all data
|
|
184
|
+
return await createClient(url, null, this.orgId);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Main database API - ultra-simple like auth module
|
|
189
|
+
*/
|
|
190
|
+
export const databaseClass = {
|
|
191
|
+
/**
|
|
192
|
+
* Get database client - main function that handles all contexts
|
|
193
|
+
* @param {Object} [req] - Request object for context detection
|
|
194
|
+
* @returns {Promise<DatabaseClientUnion>} Database client
|
|
195
|
+
*/
|
|
196
|
+
async get(req = null) {
|
|
197
|
+
// Setup env watching on first use
|
|
198
|
+
setupEnvWatcher();
|
|
199
|
+
// Detect context
|
|
200
|
+
const orgId = detectOrg(req);
|
|
201
|
+
const tenantId = detectTenant(req);
|
|
202
|
+
// Get appropriate URL
|
|
203
|
+
const url = getOrgUrl(orgId || undefined) || process.env.DATABASE_URL;
|
|
204
|
+
if (!url) {
|
|
205
|
+
throw new Error('Database URL required. Set DATABASE_URL environment variable');
|
|
206
|
+
}
|
|
207
|
+
return await createClient(url, tenantId, orgId);
|
|
208
|
+
},
|
|
209
|
+
/**
|
|
210
|
+
* Get all tenants data (admin access - no tenant filtering)
|
|
211
|
+
* @param {Object} [req] - Request object for org context
|
|
212
|
+
* @returns {Promise<DatabaseClientUnion>} Database client with no tenant filtering
|
|
213
|
+
*/
|
|
214
|
+
async getTenants(req = null) {
|
|
215
|
+
setupEnvWatcher();
|
|
216
|
+
const orgId = detectOrg(req);
|
|
217
|
+
const url = getOrgUrl(orgId || undefined) || process.env.DATABASE_URL;
|
|
218
|
+
if (!url) {
|
|
219
|
+
throw new Error('Database URL required. Set DATABASE_URL environment variable');
|
|
220
|
+
}
|
|
221
|
+
// No tenant filtering - admin sees all data
|
|
222
|
+
return await createClient(url, null, orgId);
|
|
223
|
+
},
|
|
224
|
+
/**
|
|
225
|
+
* Get organization-specific database
|
|
226
|
+
* @param {string} orgId - Organization ID
|
|
227
|
+
* @returns {OrgDatabase} Organization database instance
|
|
228
|
+
*/
|
|
229
|
+
org(orgId) {
|
|
230
|
+
if (!orgId || typeof orgId !== 'string') {
|
|
231
|
+
throw new Error('Organization ID is required and must be a string');
|
|
232
|
+
}
|
|
233
|
+
return new OrgDatabase(orgId);
|
|
234
|
+
},
|
|
235
|
+
/**
|
|
236
|
+
* Health check for database connections
|
|
237
|
+
* @returns {Promise<Object>} Health status
|
|
238
|
+
*/
|
|
239
|
+
async health() {
|
|
240
|
+
try {
|
|
241
|
+
const db = await this.get();
|
|
242
|
+
// Simple connectivity test
|
|
243
|
+
if (db.$queryRaw) {
|
|
244
|
+
// Prisma client
|
|
245
|
+
await db.$queryRaw `SELECT 1`;
|
|
246
|
+
}
|
|
247
|
+
else if (db.db) {
|
|
248
|
+
// Mongoose connection
|
|
249
|
+
await db.db.admin().ping();
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
healthy: true,
|
|
253
|
+
connections: connections.size,
|
|
254
|
+
timestamp: new Date().toISOString(),
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
return {
|
|
259
|
+
healthy: false,
|
|
260
|
+
error: error.message,
|
|
261
|
+
connections: connections.size,
|
|
262
|
+
timestamp: new Date().toISOString(),
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
/**
|
|
267
|
+
* List tenants in current context
|
|
268
|
+
* @param {Object} [req] - Request object for org context
|
|
269
|
+
* @returns {Promise<string[]>} Array of tenant IDs
|
|
270
|
+
*/
|
|
271
|
+
async list(req = null) {
|
|
272
|
+
try {
|
|
273
|
+
const db = await this.getTenants(req);
|
|
274
|
+
return await this._getDistinctTenantIds(db);
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
throw new Error(`Failed to list tenants: ${error.message}`);
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
/**
|
|
281
|
+
* Check if tenant exists
|
|
282
|
+
* @param {string} tenantId - Tenant ID
|
|
283
|
+
* @param {Object} [req] - Request object for org context
|
|
284
|
+
* @returns {Promise<boolean>} Whether tenant exists
|
|
285
|
+
*/
|
|
286
|
+
async exists(tenantId, req = null) {
|
|
287
|
+
if (!tenantId)
|
|
288
|
+
return false;
|
|
289
|
+
try {
|
|
290
|
+
const db = await this.getTenants(req);
|
|
291
|
+
return await this._tenantHasData(db, tenantId);
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
/**
|
|
298
|
+
* Create tenant (registers tenant for future use)
|
|
299
|
+
* @param {string} tenantId - Tenant ID
|
|
300
|
+
* @param {Object} [req] - Request object for org context
|
|
301
|
+
* @returns {Promise<void>}
|
|
302
|
+
*/
|
|
303
|
+
async create(tenantId, req = null) {
|
|
304
|
+
if (!tenantId || typeof tenantId !== 'string') {
|
|
305
|
+
throw new Error('Tenant ID is required and must be a string');
|
|
306
|
+
}
|
|
307
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(tenantId)) {
|
|
308
|
+
throw new Error('Invalid tenant ID format. Use alphanumeric characters, underscores, and hyphens only');
|
|
309
|
+
}
|
|
310
|
+
// For row-level strategy, tenant creation is implicit
|
|
311
|
+
// The tenant exists when first record with tenant_id is created
|
|
312
|
+
// This method can be used to validate the tenant ID format
|
|
313
|
+
},
|
|
314
|
+
/**
|
|
315
|
+
* Delete all tenant data (requires confirmation)
|
|
316
|
+
* @param {string} tenantId - Tenant ID
|
|
317
|
+
* @param {Object} options - Options object
|
|
318
|
+
* @param {boolean} options.confirm - Confirmation flag (required)
|
|
319
|
+
* @param {Object} [req] - Request object for org context
|
|
320
|
+
* @returns {Promise<void>}
|
|
321
|
+
*/
|
|
322
|
+
async delete(tenantId, options, req = null) {
|
|
323
|
+
if (!tenantId) {
|
|
324
|
+
throw new Error('Tenant ID is required');
|
|
325
|
+
}
|
|
326
|
+
if (!options?.confirm) {
|
|
327
|
+
throw new Error('Tenant deletion requires explicit confirmation. Pass { confirm: true }');
|
|
328
|
+
}
|
|
329
|
+
const db = await this.getTenants(req);
|
|
330
|
+
await this._deleteAllTenantData(db, tenantId);
|
|
331
|
+
// Clear cached connections for this tenant
|
|
332
|
+
this._clearTenantCache(tenantId);
|
|
333
|
+
},
|
|
334
|
+
/**
|
|
335
|
+
* Disconnect all connections and cleanup
|
|
336
|
+
* @returns {Promise<void>}
|
|
337
|
+
*/
|
|
338
|
+
async disconnect() {
|
|
339
|
+
const disconnectPromises = [];
|
|
340
|
+
for (const [key, connection] of connections) {
|
|
341
|
+
disconnectPromises.push(this._closeConnection(connection).catch((error) => console.warn(`Error disconnecting ${key}:`, error.message)));
|
|
342
|
+
}
|
|
343
|
+
await Promise.all(disconnectPromises);
|
|
344
|
+
connections.clear();
|
|
345
|
+
if (envWatcher) {
|
|
346
|
+
envWatcher.close();
|
|
347
|
+
envWatcher = null;
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
// Private helper methods
|
|
351
|
+
/**
|
|
352
|
+
* Get distinct tenant IDs from database
|
|
353
|
+
* @private
|
|
354
|
+
*/
|
|
355
|
+
async _getDistinctTenantIds(client) {
|
|
356
|
+
const tenantIds = new Set();
|
|
357
|
+
try {
|
|
358
|
+
if (client.$queryRaw) {
|
|
359
|
+
// Prisma client - find models with tenant_id field
|
|
360
|
+
const models = Object.keys(client).filter((key) => !key.startsWith('$') &&
|
|
361
|
+
!key.startsWith('_') &&
|
|
362
|
+
typeof client[key] === 'object' &&
|
|
363
|
+
typeof client[key].findMany === 'function');
|
|
364
|
+
for (const modelName of models) {
|
|
365
|
+
try {
|
|
366
|
+
const records = await client[modelName].findMany({
|
|
367
|
+
select: { tenant_id: true },
|
|
368
|
+
distinct: ['tenant_id'],
|
|
369
|
+
where: { tenant_id: { not: null } },
|
|
370
|
+
});
|
|
371
|
+
records.forEach((record) => {
|
|
372
|
+
if (record.tenant_id)
|
|
373
|
+
tenantIds.add(record.tenant_id);
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
catch {
|
|
377
|
+
// Model might not have tenant_id field
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return Array.from(tenantIds).sort();
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
throw new Error(`Failed to get tenant IDs: ${error.message}`);
|
|
386
|
+
}
|
|
387
|
+
},
|
|
388
|
+
/**
|
|
389
|
+
* Check if tenant has data
|
|
390
|
+
* @private
|
|
391
|
+
*/
|
|
392
|
+
async _tenantHasData(client, tenantId) {
|
|
393
|
+
try {
|
|
394
|
+
if (client.$queryRaw) {
|
|
395
|
+
// Prisma client
|
|
396
|
+
const models = Object.keys(client).filter((key) => !key.startsWith('$') &&
|
|
397
|
+
!key.startsWith('_') &&
|
|
398
|
+
typeof client[key] === 'object' &&
|
|
399
|
+
typeof client[key].findFirst === 'function');
|
|
400
|
+
for (const modelName of models) {
|
|
401
|
+
try {
|
|
402
|
+
const record = await client[modelName].findFirst({
|
|
403
|
+
where: { tenant_id: tenantId },
|
|
404
|
+
});
|
|
405
|
+
if (record)
|
|
406
|
+
return true;
|
|
407
|
+
}
|
|
408
|
+
catch {
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
catch {
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
},
|
|
419
|
+
/**
|
|
420
|
+
* Delete all tenant data
|
|
421
|
+
* @private
|
|
422
|
+
*/
|
|
423
|
+
async _deleteAllTenantData(client, tenantId) {
|
|
424
|
+
try {
|
|
425
|
+
if (client.$transaction) {
|
|
426
|
+
// Prisma client - use transaction for safety
|
|
427
|
+
const models = Object.keys(client).filter((key) => !key.startsWith('$') &&
|
|
428
|
+
!key.startsWith('_') &&
|
|
429
|
+
typeof client[key] === 'object' &&
|
|
430
|
+
typeof client[key].deleteMany === 'function');
|
|
431
|
+
const deleteOperations = [];
|
|
432
|
+
for (const modelName of models) {
|
|
433
|
+
try {
|
|
434
|
+
deleteOperations.push(client[modelName].deleteMany({
|
|
435
|
+
where: { tenant_id: tenantId },
|
|
436
|
+
}));
|
|
437
|
+
}
|
|
438
|
+
catch {
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
if (deleteOperations.length > 0) {
|
|
443
|
+
await client.$transaction(deleteOperations);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
catch (error) {
|
|
448
|
+
throw new Error(`Failed to delete tenant data: ${error.message}`);
|
|
449
|
+
}
|
|
450
|
+
},
|
|
451
|
+
/**
|
|
452
|
+
* Clear tenant-specific cached connections
|
|
453
|
+
* @private
|
|
454
|
+
*/
|
|
455
|
+
_clearTenantCache(tenantId) {
|
|
456
|
+
const keysToDelete = [];
|
|
457
|
+
for (const [key] of connections) {
|
|
458
|
+
if (key.includes(`_${tenantId}_`)) {
|
|
459
|
+
keysToDelete.push(key);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
keysToDelete.forEach((key) => {
|
|
463
|
+
const connection = connections.get(key);
|
|
464
|
+
if (connection) {
|
|
465
|
+
this._closeConnection(connection);
|
|
466
|
+
}
|
|
467
|
+
connections.delete(key);
|
|
468
|
+
});
|
|
469
|
+
},
|
|
470
|
+
/**
|
|
471
|
+
* Close database connection
|
|
472
|
+
* @private
|
|
473
|
+
*/
|
|
474
|
+
async _closeConnection(connection) {
|
|
475
|
+
try {
|
|
476
|
+
if (connection.$disconnect) {
|
|
477
|
+
await connection.$disconnect();
|
|
478
|
+
}
|
|
479
|
+
else if (connection.close) {
|
|
480
|
+
await connection.close();
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
catch {
|
|
484
|
+
// Ignore disconnect errors
|
|
485
|
+
}
|
|
486
|
+
},
|
|
487
|
+
};
|
|
488
|
+
// Default export for convenience
|
|
489
|
+
export default databaseClass;
|
|
490
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/database/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart defaults and environment validation for email with auto-strategy detection
|
|
3
|
+
* @module @bloomneo/appkit/email
|
|
4
|
+
* @file src/email/defaults.ts
|
|
5
|
+
*
|
|
6
|
+
* @llm-rule WHEN: App startup - need to configure email strategy and connection settings
|
|
7
|
+
* @llm-rule AVOID: Calling multiple times - expensive environment parsing, use lazy loading in get()
|
|
8
|
+
* @llm-rule NOTE: Called once at startup, cached globally for performance
|
|
9
|
+
* @llm-rule NOTE: Auto-detects Resend → SMTP → Console based on environment variables
|
|
10
|
+
*/
|
|
11
|
+
export interface ResendConfig {
|
|
12
|
+
apiKey: string;
|
|
13
|
+
baseURL: string;
|
|
14
|
+
timeout: number;
|
|
15
|
+
}
|
|
16
|
+
export interface SmtpConfig {
|
|
17
|
+
host: string;
|
|
18
|
+
port: number;
|
|
19
|
+
secure: boolean;
|
|
20
|
+
auth: {
|
|
21
|
+
user: string;
|
|
22
|
+
pass: string;
|
|
23
|
+
};
|
|
24
|
+
timeout: number;
|
|
25
|
+
pool: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface ConsoleConfig {
|
|
28
|
+
colorize: boolean;
|
|
29
|
+
showPreview: boolean;
|
|
30
|
+
format: 'simple' | 'detailed';
|
|
31
|
+
}
|
|
32
|
+
export interface EmailConfig {
|
|
33
|
+
strategy: 'resend' | 'smtp' | 'console';
|
|
34
|
+
from: {
|
|
35
|
+
name: string;
|
|
36
|
+
email: string;
|
|
37
|
+
};
|
|
38
|
+
resend?: ResendConfig;
|
|
39
|
+
smtp?: SmtpConfig;
|
|
40
|
+
console?: ConsoleConfig;
|
|
41
|
+
environment: {
|
|
42
|
+
isDevelopment: boolean;
|
|
43
|
+
isProduction: boolean;
|
|
44
|
+
isTest: boolean;
|
|
45
|
+
nodeEnv: string;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Gets smart defaults using environment variables with auto-strategy detection
|
|
50
|
+
* @llm-rule WHEN: App startup to get production-ready email configuration
|
|
51
|
+
* @llm-rule AVOID: Calling repeatedly - expensive validation, cache the result
|
|
52
|
+
* @llm-rule NOTE: Auto-detects strategy: RESEND_API_KEY → Resend, SMTP_HOST → SMTP, default → Console
|
|
53
|
+
*/
|
|
54
|
+
export declare function getSmartDefaults(): EmailConfig;
|
|
55
|
+
/**
|
|
56
|
+
* Gets email configuration summary for debugging and health checks
|
|
57
|
+
* @llm-rule WHEN: Debugging email configuration or building health check endpoints
|
|
58
|
+
* @llm-rule AVOID: Exposing sensitive API keys or passwords - this only shows safe info
|
|
59
|
+
*/
|
|
60
|
+
export declare function getConfigSummary(): {
|
|
61
|
+
strategy: string;
|
|
62
|
+
fromName: string;
|
|
63
|
+
fromEmail: string;
|
|
64
|
+
resendConfigured: boolean;
|
|
65
|
+
smtpConfigured: boolean;
|
|
66
|
+
environment: string;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Validates that required email configuration is present for production
|
|
70
|
+
* @llm-rule WHEN: App startup validation for production deployments
|
|
71
|
+
* @llm-rule AVOID: Skipping validation - missing email config causes runtime issues
|
|
72
|
+
*/
|
|
73
|
+
export declare function validateProductionRequirements(): void;
|
|
74
|
+
/**
|
|
75
|
+
* Validates startup configuration and throws detailed errors
|
|
76
|
+
* @llm-rule WHEN: App startup to ensure email configuration is valid before starting
|
|
77
|
+
* @llm-rule AVOID: Skipping validation - catches config issues early
|
|
78
|
+
* @llm-rule NOTE: Comprehensive validation for production readiness
|
|
79
|
+
*/
|
|
80
|
+
export declare function validateStartupConfiguration(): {
|
|
81
|
+
strategy: string;
|
|
82
|
+
warnings: string[];
|
|
83
|
+
errors: string[];
|
|
84
|
+
ready: boolean;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Performs comprehensive email system health check
|
|
88
|
+
* @llm-rule WHEN: Health check endpoints or monitoring systems
|
|
89
|
+
* @llm-rule AVOID: Running in critical path - this is for monitoring only
|
|
90
|
+
* @llm-rule NOTE: Returns detailed health status without exposing sensitive data
|
|
91
|
+
*/
|
|
92
|
+
export declare function performHealthCheck(): {
|
|
93
|
+
status: 'healthy' | 'warning' | 'error';
|
|
94
|
+
strategy: string;
|
|
95
|
+
configured: boolean;
|
|
96
|
+
issues: string[];
|
|
97
|
+
ready: boolean;
|
|
98
|
+
timestamp: string;
|
|
99
|
+
};
|
|
100
|
+
//# sourceMappingURL=defaults.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/email/defaults.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,QAAQ,GAAG,UAAU,CAAC;CAC/B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;IACxC,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,WAAW,EAAE;QACX,aAAa,EAAE,OAAO,CAAC;QACvB,YAAY,EAAE,OAAO,CAAC;QACtB,MAAM,EAAE,OAAO,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,WAAW,CAwD9C;AAuOD;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,OAAO,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB,CAWA;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,IAAI,IAAI,CA0BrD;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,IAAI;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;CAChB,CAwEA;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI;IACpC,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB,CAsCA"}
|