@diagramers/cli 1.0.23 → 1.0.24
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/services/template-updater.d.ts +2 -0
- package/dist/services/template-updater.d.ts.map +1 -1
- package/dist/services/template-updater.js +66 -14
- package/dist/services/template-updater.js.map +1 -1
- package/package.json +5 -1
- package/templates/api/certs/auth-app-cert.json +13 -0
- package/templates/api/main.ts +10 -0
- package/templates/api/package.json +70 -0
- package/templates/api/src/assets/css/email-template.css +8 -0
- package/templates/api/src/assets/images/logo_large.png +0 -0
- package/templates/api/src/assets/keys/certificate.pem +22 -0
- package/templates/api/src/assets/keys/private-key.pem +28 -0
- package/templates/api/src/config/config-interface.ts +191 -0
- package/templates/api/src/config/development.ts +145 -0
- package/templates/api/src/config/index.ts +59 -0
- package/templates/api/src/config/production.ts +145 -0
- package/templates/api/src/config/staging.ts +144 -0
- package/templates/api/src/config/uat.ts +144 -0
- package/templates/api/src/controllers/account-controller.ts +162 -0
- package/templates/api/src/entities/audit.ts +12 -0
- package/templates/api/src/entities/base-entity.ts +10 -0
- package/templates/api/src/entities/user.ts +71 -0
- package/templates/api/src/helpers/FrameworkHelper.ts +157 -0
- package/templates/api/src/helpers/auth.ts +971 -0
- package/templates/api/src/helpers/cronHelper.ts +170 -0
- package/templates/api/src/helpers/dbcontext.ts +83 -0
- package/templates/api/src/helpers/encryptionHelper.ts +76 -0
- package/templates/api/src/helpers/enums.ts +258 -0
- package/templates/api/src/helpers/handle-response.ts +49 -0
- package/templates/api/src/helpers/httpHelper.ts +75 -0
- package/templates/api/src/helpers/mailer.ts +152 -0
- package/templates/api/src/helpers/result.ts +47 -0
- package/templates/api/src/helpers/string-helper.ts +27 -0
- package/templates/api/src/routes/account-routes.ts +37 -0
- package/templates/api/src/routes/auth-routes.ts +286 -0
- package/templates/api/src/routes/index.ts +92 -0
- package/templates/api/src/schemas/audit.ts +36 -0
- package/templates/api/src/schemas/otp.ts +52 -0
- package/templates/api/src/schemas/session.ts +57 -0
- package/templates/api/src/schemas/user.ts +125 -0
- package/templates/api/src/server/index.ts +86 -0
- package/templates/api/src/server/socket-server-provider.ts +209 -0
- package/templates/api/src/services/account-service.ts +243 -0
- package/templates/api/src/services/audit-service.ts +56 -0
- package/templates/api/tsconfig.json +16 -0
- package/templates/api/webpack.config.js +66 -0
- package/scripts/publish.sh +0 -58
- package/scripts/setup.sh +0 -38
- package/scripts/version.sh +0 -80
- package/src/commands/api.ts +0 -76
- package/src/commands/extend.ts +0 -35
- package/src/commands/init.ts +0 -32
- package/src/commands/update.ts +0 -25
- package/src/config/template-config.ts +0 -111
- package/src/index.ts +0 -41
- package/src/services/api-generator.ts +0 -378
- package/src/services/project-extender.ts +0 -330
- package/src/services/project-initializer.ts +0 -335
- package/src/services/project-updater.ts +0 -117
- package/src/services/relation-generator.ts +0 -203
- package/src/services/table-generator.ts +0 -114
- package/src/services/template-processor.ts +0 -166
- package/src/services/template-updater.ts +0 -184
- package/tsconfig.json +0 -19
@@ -1,117 +0,0 @@
|
|
1
|
-
import * as fs from 'fs-extra';
|
2
|
-
import * as path from 'path';
|
3
|
-
import * as glob from 'glob';
|
4
|
-
import chalk from 'chalk';
|
5
|
-
|
6
|
-
export interface UpdateOptions {
|
7
|
-
force?: boolean;
|
8
|
-
backup?: boolean;
|
9
|
-
}
|
10
|
-
|
11
|
-
export class ProjectUpdater {
|
12
|
-
private currentProjectPath: string;
|
13
|
-
|
14
|
-
constructor() {
|
15
|
-
this.currentProjectPath = process.cwd();
|
16
|
-
}
|
17
|
-
|
18
|
-
async update(options: UpdateOptions): Promise<void> {
|
19
|
-
// Check if we're in a valid project directory
|
20
|
-
if (!await this.isValidProject()) {
|
21
|
-
throw new Error('Not a valid project directory. Please run this command from your project root.');
|
22
|
-
}
|
23
|
-
|
24
|
-
// Create backup if requested
|
25
|
-
if (options.backup) {
|
26
|
-
await this.createBackup();
|
27
|
-
}
|
28
|
-
|
29
|
-
// Get list of files that can be updated
|
30
|
-
const updateableFiles = await this.getUpdateableFiles();
|
31
|
-
|
32
|
-
// Check for conflicts
|
33
|
-
const conflicts = await this.checkConflicts(updateableFiles);
|
34
|
-
|
35
|
-
if (conflicts.length > 0 && !options.force) {
|
36
|
-
console.log(chalk.yellow('⚠️ Conflicts detected in the following files:'));
|
37
|
-
conflicts.forEach(file => console.log(chalk.yellow(` - ${file}`)));
|
38
|
-
console.log(chalk.yellow('Use --force to overwrite these files'));
|
39
|
-
throw new Error('Update aborted due to conflicts');
|
40
|
-
}
|
41
|
-
|
42
|
-
// Perform the update
|
43
|
-
await this.performUpdate(updateableFiles);
|
44
|
-
|
45
|
-
console.log(chalk.green('✅ Project updated successfully!'));
|
46
|
-
}
|
47
|
-
|
48
|
-
private async isValidProject(): Promise<boolean> {
|
49
|
-
const packageJsonPath = path.join(this.currentProjectPath, 'package.json');
|
50
|
-
const mainTsPath = path.join(this.currentProjectPath, 'main.ts');
|
51
|
-
|
52
|
-
return await fs.pathExists(packageJsonPath) && await fs.pathExists(mainTsPath);
|
53
|
-
}
|
54
|
-
|
55
|
-
private async createBackup(): Promise<void> {
|
56
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
57
|
-
const backupPath = path.join(this.currentProjectPath, `backup-${timestamp}`);
|
58
|
-
|
59
|
-
await fs.copy(this.currentProjectPath, backupPath, {
|
60
|
-
filter: (src) => {
|
61
|
-
const relativePath = path.relative(this.currentProjectPath, src);
|
62
|
-
return !relativePath.startsWith('node_modules') &&
|
63
|
-
!relativePath.startsWith('.git') &&
|
64
|
-
!relativePath.startsWith('backup-');
|
65
|
-
}
|
66
|
-
});
|
67
|
-
|
68
|
-
console.log(chalk.blue(`📦 Backup created at: ${backupPath}`));
|
69
|
-
}
|
70
|
-
|
71
|
-
private async getUpdateableFiles(): Promise<string[]> {
|
72
|
-
// For now, we'll update specific files that are commonly modified in templates
|
73
|
-
const updateableFiles = [
|
74
|
-
'src/helpers/dbcontext.ts',
|
75
|
-
'src/helpers/result.ts',
|
76
|
-
'src/helpers/enums.ts',
|
77
|
-
'src/config/index.ts',
|
78
|
-
'src/config/development.ts',
|
79
|
-
'src/config/staging.ts',
|
80
|
-
'src/config/production.ts',
|
81
|
-
'src/server/index.ts',
|
82
|
-
'webpack.config.js',
|
83
|
-
'tsconfig.json'
|
84
|
-
];
|
85
|
-
|
86
|
-
return updateableFiles.filter(file => fs.existsSync(path.join(this.currentProjectPath, file)));
|
87
|
-
}
|
88
|
-
|
89
|
-
private async checkConflicts(files: string[]): Promise<string[]> {
|
90
|
-
const conflicts: string[] = [];
|
91
|
-
|
92
|
-
for (const file of files) {
|
93
|
-
const projectPath = path.join(this.currentProjectPath, file);
|
94
|
-
|
95
|
-
if (await fs.pathExists(projectPath)) {
|
96
|
-
// For now, we'll consider all files as potentially conflicting
|
97
|
-
// In a real implementation, you'd compare with the template version
|
98
|
-
conflicts.push(file);
|
99
|
-
}
|
100
|
-
}
|
101
|
-
|
102
|
-
return conflicts;
|
103
|
-
}
|
104
|
-
|
105
|
-
private async performUpdate(files: string[]): Promise<void> {
|
106
|
-
console.log(chalk.blue('📝 Updating project files...'));
|
107
|
-
|
108
|
-
// For now, we'll just report what would be updated
|
109
|
-
// In a real implementation, you'd copy from the latest template
|
110
|
-
for (const file of files) {
|
111
|
-
console.log(chalk.green(`✅ Would update: ${file}`));
|
112
|
-
}
|
113
|
-
|
114
|
-
console.log(chalk.yellow('⚠️ Update mechanism is being improved.'));
|
115
|
-
console.log(chalk.yellow(' For now, manual updates are recommended.'));
|
116
|
-
}
|
117
|
-
}
|
@@ -1,203 +0,0 @@
|
|
1
|
-
import * as fs from 'fs';
|
2
|
-
import * as path from 'path';
|
3
|
-
|
4
|
-
export type RelationType = 'one-to-one' | 'one-to-many' | 'many-to-many';
|
5
|
-
|
6
|
-
export async function generateRelation(table1: string, table2: string, relationType: RelationType = 'one-to-one'): Promise<void> {
|
7
|
-
// Validate table names
|
8
|
-
if (!table1 || !table2 || !/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(table1) || !/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(table2)) {
|
9
|
-
throw new Error('Invalid table names. Use only letters, numbers, hyphens, and underscores. Must start with a letter.');
|
10
|
-
}
|
11
|
-
|
12
|
-
const table1Capitalized = table1.charAt(0).toUpperCase() + table1.slice(1);
|
13
|
-
const table2Capitalized = table2.charAt(0).toUpperCase() + table2.slice(1);
|
14
|
-
const currentDir = process.cwd();
|
15
|
-
|
16
|
-
// Check for required API project structure
|
17
|
-
const requiredDirs = ['src/entities', 'src/schemas'];
|
18
|
-
const missingDirs = requiredDirs.filter(dir => !fs.existsSync(path.join(currentDir, dir)));
|
19
|
-
if (missingDirs.length > 0) {
|
20
|
-
throw new Error(`This command should be run from a diagramers API project. Missing directories: ${missingDirs.join(', ')}`);
|
21
|
-
}
|
22
|
-
|
23
|
-
// Check if both tables exist
|
24
|
-
const table1EntityPath = path.join(currentDir, 'src/entities', `${table1}.ts`);
|
25
|
-
const table2EntityPath = path.join(currentDir, 'src/entities', `${table2}.ts`);
|
26
|
-
const table1SchemaPath = path.join(currentDir, 'src/schemas', `${table1}.ts`);
|
27
|
-
const table2SchemaPath = path.join(currentDir, 'src/schemas', `${table2}.ts`);
|
28
|
-
|
29
|
-
if (!fs.existsSync(table1EntityPath) || !fs.existsSync(table2EntityPath)) {
|
30
|
-
throw new Error(`Both tables must exist before creating relationships. Missing: ${!fs.existsSync(table1EntityPath) ? table1 : ''} ${!fs.existsSync(table2EntityPath) ? table2 : ''}`);
|
31
|
-
}
|
32
|
-
|
33
|
-
console.log(`🔗 Creating ${relationType} relationship between ${table1} and ${table2}...`);
|
34
|
-
|
35
|
-
// Update entities
|
36
|
-
console.log('📝 Updating entities...');
|
37
|
-
updateEntity(table1EntityPath, table1Capitalized, table2Capitalized, relationType, 'forward');
|
38
|
-
updateEntity(table2EntityPath, table2Capitalized, table1Capitalized, relationType, 'reverse');
|
39
|
-
|
40
|
-
// Update schemas
|
41
|
-
console.log('📋 Updating schemas...');
|
42
|
-
updateSchema(table1SchemaPath, table1Capitalized, table2Capitalized, relationType, 'forward');
|
43
|
-
updateSchema(table2SchemaPath, table2Capitalized, table1Capitalized, relationType, 'reverse');
|
44
|
-
|
45
|
-
console.log('✅ Relationship created successfully!');
|
46
|
-
console.log(`🔗 ${table1} ↔ ${table2} (${relationType})`);
|
47
|
-
}
|
48
|
-
|
49
|
-
function updateEntity(entityPath: string, tableCapitalized: string, relatedTableCapitalized: string, relationType: RelationType, direction: 'forward' | 'reverse'): void {
|
50
|
-
let entityContent = fs.readFileSync(entityPath, 'utf8');
|
51
|
-
|
52
|
-
// Add import for the related interface
|
53
|
-
const importStatement = `import { I${relatedTableCapitalized} } from './${direction === 'forward' ? relatedTableCapitalized.toLowerCase() : relatedTableCapitalized.toLowerCase()}';`;
|
54
|
-
if (!entityContent.includes(importStatement)) {
|
55
|
-
// Find the last import statement and add after it
|
56
|
-
const importRegex = /import.*from.*['"];?\s*$/gm;
|
57
|
-
const matches = [...entityContent.matchAll(importRegex)];
|
58
|
-
if (matches.length > 0) {
|
59
|
-
const lastImport = matches[matches.length - 1];
|
60
|
-
const insertIndex = lastImport.index! + lastImport[0].length;
|
61
|
-
entityContent = entityContent.slice(0, insertIndex) + '\n' + importStatement + entityContent.slice(insertIndex);
|
62
|
-
}
|
63
|
-
}
|
64
|
-
|
65
|
-
// Add relationship fields based on type
|
66
|
-
const interfaceRegex = /export interface I\w+ extends mongoose\.Document \{([^}]*)\}/s;
|
67
|
-
const match = entityContent.match(interfaceRegex);
|
68
|
-
|
69
|
-
if (match) {
|
70
|
-
const interfaceContent = match[1];
|
71
|
-
const newFields = generateEntityFields(tableCapitalized, relatedTableCapitalized, relationType, direction);
|
72
|
-
|
73
|
-
// Add new fields before the closing brace
|
74
|
-
const updatedInterfaceContent = interfaceContent.trim() + '\n ' + newFields;
|
75
|
-
entityContent = entityContent.replace(interfaceRegex, `export interface I${tableCapitalized} extends mongoose.Document {$1${updatedInterfaceContent}\n}`);
|
76
|
-
}
|
77
|
-
|
78
|
-
fs.writeFileSync(entityPath, entityContent);
|
79
|
-
}
|
80
|
-
|
81
|
-
function updateSchema(schemaPath: string, tableCapitalized: string, relatedTableCapitalized: string, relationType: RelationType, direction: 'forward' | 'reverse'): void {
|
82
|
-
let schemaContent = fs.readFileSync(schemaPath, 'utf8');
|
83
|
-
|
84
|
-
// Add import for the related schema
|
85
|
-
const importStatement = `import { ${relatedTableCapitalized}Entity } from './${direction === 'forward' ? relatedTableCapitalized.toLowerCase() : relatedTableCapitalized.toLowerCase()}';`;
|
86
|
-
if (!schemaContent.includes(importStatement)) {
|
87
|
-
// Find the last import statement and add after it
|
88
|
-
const importRegex = /import.*from.*['"];?\s*$/gm;
|
89
|
-
const matches = [...schemaContent.matchAll(importRegex)];
|
90
|
-
if (matches.length > 0) {
|
91
|
-
const lastImport = matches[matches.length - 1];
|
92
|
-
const insertIndex = lastImport.index! + lastImport[0].length;
|
93
|
-
schemaContent = schemaContent.slice(0, insertIndex) + '\n' + importStatement + schemaContent.slice(insertIndex);
|
94
|
-
}
|
95
|
-
}
|
96
|
-
|
97
|
-
// Add schema fields based on relationship type
|
98
|
-
const schemaRegex = /export const \w+Schema = new mongoose\.Schema\(([^)]*),/s;
|
99
|
-
const match = schemaContent.match(schemaRegex);
|
100
|
-
|
101
|
-
if (match) {
|
102
|
-
const schemaFields = match[1];
|
103
|
-
const newFields = generateSchemaFields(tableCapitalized, relatedTableCapitalized, relationType, direction);
|
104
|
-
|
105
|
-
// Add new fields before the closing brace
|
106
|
-
const updatedSchemaFields = schemaFields.trim() + '\n ' + newFields;
|
107
|
-
schemaContent = schemaContent.replace(schemaRegex, `export const ${tableCapitalized.toLowerCase()}Schema = new mongoose.Schema(${updatedSchemaFields},`);
|
108
|
-
}
|
109
|
-
|
110
|
-
// Add virtual fields and population methods
|
111
|
-
const modelRegex = /export const \w+Entity = mongoose\.model<.*>\(.*\);?\s*$/;
|
112
|
-
const virtualFields = generateVirtualFields(tableCapitalized, relatedTableCapitalized, relationType, direction);
|
113
|
-
|
114
|
-
if (virtualFields) {
|
115
|
-
schemaContent = schemaContent.replace(modelRegex, `${virtualFields}\n\n$&`);
|
116
|
-
}
|
117
|
-
|
118
|
-
fs.writeFileSync(schemaPath, schemaContent);
|
119
|
-
}
|
120
|
-
|
121
|
-
function generateEntityFields(tableCapitalized: string, relatedTableCapitalized: string, relationType: RelationType, direction: 'forward' | 'reverse'): string {
|
122
|
-
switch (relationType) {
|
123
|
-
case 'one-to-one':
|
124
|
-
if (direction === 'forward') {
|
125
|
-
return `_${relatedTableCapitalized.toLowerCase()}Id: ObjectId,\n ${relatedTableCapitalized.toLowerCase()}?: I${relatedTableCapitalized}`;
|
126
|
-
} else {
|
127
|
-
return `_${tableCapitalized.toLowerCase()}Id: ObjectId,\n ${tableCapitalized.toLowerCase()}?: I${tableCapitalized}`;
|
128
|
-
}
|
129
|
-
|
130
|
-
case 'one-to-many':
|
131
|
-
if (direction === 'forward') {
|
132
|
-
return `_${relatedTableCapitalized.toLowerCase()}Id: ObjectId,\n ${relatedTableCapitalized.toLowerCase()}?: I${relatedTableCapitalized}`;
|
133
|
-
} else {
|
134
|
-
return `${tableCapitalized.toLowerCase()}s?: I${tableCapitalized}[]`;
|
135
|
-
}
|
136
|
-
|
137
|
-
case 'many-to-many':
|
138
|
-
if (direction === 'forward') {
|
139
|
-
return `_${relatedTableCapitalized.toLowerCase()}Ids: ObjectId[],\n ${relatedTableCapitalized.toLowerCase()}s?: I${relatedTableCapitalized}[]`;
|
140
|
-
} else {
|
141
|
-
return `_${tableCapitalized.toLowerCase()}Ids: ObjectId[],\n ${tableCapitalized.toLowerCase()}s?: I${tableCapitalized}[]`;
|
142
|
-
}
|
143
|
-
|
144
|
-
default:
|
145
|
-
return '';
|
146
|
-
}
|
147
|
-
}
|
148
|
-
|
149
|
-
function generateSchemaFields(tableCapitalized: string, relatedTableCapitalized: string, relationType: RelationType, direction: 'forward' | 'reverse'): string {
|
150
|
-
switch (relationType) {
|
151
|
-
case 'one-to-one':
|
152
|
-
if (direction === 'forward') {
|
153
|
-
return `_${relatedTableCapitalized.toLowerCase()}Id: {\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${relatedTableCapitalized.toLowerCase()}',\n required: false\n }`;
|
154
|
-
} else {
|
155
|
-
return `_${tableCapitalized.toLowerCase()}Id: {\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${tableCapitalized.toLowerCase()}',\n required: false\n }`;
|
156
|
-
}
|
157
|
-
|
158
|
-
case 'one-to-many':
|
159
|
-
if (direction === 'forward') {
|
160
|
-
return `_${relatedTableCapitalized.toLowerCase()}Id: {\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${relatedTableCapitalized.toLowerCase()}',\n required: false\n }`;
|
161
|
-
} else {
|
162
|
-
return ''; // No field needed for reverse one-to-many
|
163
|
-
}
|
164
|
-
|
165
|
-
case 'many-to-many':
|
166
|
-
if (direction === 'forward') {
|
167
|
-
return `_${relatedTableCapitalized.toLowerCase()}Ids: [{\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${relatedTableCapitalized.toLowerCase()}'\n }]`;
|
168
|
-
} else {
|
169
|
-
return `_${tableCapitalized.toLowerCase()}Ids: [{\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${tableCapitalized.toLowerCase()}'\n }]`;
|
170
|
-
}
|
171
|
-
|
172
|
-
default:
|
173
|
-
return '';
|
174
|
-
}
|
175
|
-
}
|
176
|
-
|
177
|
-
function generateVirtualFields(tableCapitalized: string, relatedTableCapitalized: string, relationType: RelationType, direction: 'forward' | 'reverse'): string {
|
178
|
-
switch (relationType) {
|
179
|
-
case 'one-to-one':
|
180
|
-
if (direction === 'forward') {
|
181
|
-
return `// Virtual populate for ${relatedTableCapitalized.toLowerCase()}\n${tableCapitalized.toLowerCase()}Schema.virtual('${relatedTableCapitalized.toLowerCase()}', {\n ref: '${relatedTableCapitalized.toLowerCase()}',\n localField: '_${relatedTableCapitalized.toLowerCase()}Id',\n foreignField: '_id',\n justOne: true\n});`;
|
182
|
-
} else {
|
183
|
-
return `// Virtual populate for ${tableCapitalized.toLowerCase()}\n${tableCapitalized.toLowerCase()}Schema.virtual('${tableCapitalized.toLowerCase()}', {\n ref: '${tableCapitalized.toLowerCase()}',\n localField: '_${tableCapitalized.toLowerCase()}Id',\n foreignField: '_id',\n justOne: true\n});`;
|
184
|
-
}
|
185
|
-
|
186
|
-
case 'one-to-many':
|
187
|
-
if (direction === 'forward') {
|
188
|
-
return `// Virtual populate for ${relatedTableCapitalized.toLowerCase()}\n${tableCapitalized.toLowerCase()}Schema.virtual('${relatedTableCapitalized.toLowerCase()}', {\n ref: '${relatedTableCapitalized.toLowerCase()}',\n localField: '_${relatedTableCapitalized.toLowerCase()}Id',\n foreignField: '_id',\n justOne: true\n});`;
|
189
|
-
} else {
|
190
|
-
return `// Virtual populate for ${tableCapitalized.toLowerCase()}s\n${tableCapitalized.toLowerCase()}Schema.virtual('${tableCapitalized.toLowerCase()}s', {\n ref: '${tableCapitalized.toLowerCase()}',\n localField: '_id',\n foreignField: '_${tableCapitalized.toLowerCase()}Id'\n});`;
|
191
|
-
}
|
192
|
-
|
193
|
-
case 'many-to-many':
|
194
|
-
if (direction === 'forward') {
|
195
|
-
return `// Virtual populate for ${relatedTableCapitalized.toLowerCase()}s\n${tableCapitalized.toLowerCase()}Schema.virtual('${relatedTableCapitalized.toLowerCase()}s', {\n ref: '${relatedTableCapitalized.toLowerCase()}',\n localField: '_${relatedTableCapitalized.toLowerCase()}Ids',\n foreignField: '_id'\n});`;
|
196
|
-
} else {
|
197
|
-
return `// Virtual populate for ${tableCapitalized.toLowerCase()}s\n${tableCapitalized.toLowerCase()}Schema.virtual('${tableCapitalized.toLowerCase()}s', {\n ref: '${tableCapitalized.toLowerCase()}',\n localField: '_${tableCapitalized.toLowerCase()}Ids',\n foreignField: '_id'\n});`;
|
198
|
-
}
|
199
|
-
|
200
|
-
default:
|
201
|
-
return '';
|
202
|
-
}
|
203
|
-
}
|
@@ -1,114 +0,0 @@
|
|
1
|
-
import * as fs from 'fs';
|
2
|
-
import * as path from 'path';
|
3
|
-
|
4
|
-
export async function generateTable(tableName: string): Promise<void> {
|
5
|
-
// Validate table name
|
6
|
-
if (!tableName || !/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(tableName)) {
|
7
|
-
throw new Error('Invalid table name. Use only letters, numbers, hyphens, and underscores. Must start with a letter.');
|
8
|
-
}
|
9
|
-
|
10
|
-
const tableNameCapitalized = tableName.charAt(0).toUpperCase() + tableName.slice(1);
|
11
|
-
const currentDir = process.cwd();
|
12
|
-
|
13
|
-
// Check for required API project structure
|
14
|
-
const requiredDirs = [
|
15
|
-
'src/entities',
|
16
|
-
'src/schemas'
|
17
|
-
];
|
18
|
-
const missingDirs = requiredDirs.filter(dir => !fs.existsSync(path.join(currentDir, dir)));
|
19
|
-
if (missingDirs.length > 0) {
|
20
|
-
throw new Error(`This command should be run from a diagramers API project. Missing directories: ${missingDirs.join(', ')}`);
|
21
|
-
}
|
22
|
-
|
23
|
-
console.log('📝 Creating entity...');
|
24
|
-
const entityContent = generateEntityContent(tableName, tableNameCapitalized);
|
25
|
-
fs.writeFileSync(path.join(currentDir, 'src/entities', `${tableName}.ts`), entityContent);
|
26
|
-
|
27
|
-
console.log('📋 Creating schema...');
|
28
|
-
const schemaContent = generateSchemaContent(tableName, tableNameCapitalized);
|
29
|
-
fs.writeFileSync(path.join(currentDir, 'src/schemas', `${tableName}.ts`), schemaContent);
|
30
|
-
|
31
|
-
// Update dbcontext to include the new entity
|
32
|
-
updateDbContext(currentDir, tableName, tableNameCapitalized);
|
33
|
-
|
34
|
-
console.log('✅ Table generation completed!');
|
35
|
-
console.log(`📊 Entity: src/entities/${tableName}.ts`);
|
36
|
-
console.log(`📋 Schema: src/schemas/${tableName}.ts`);
|
37
|
-
console.log('🔄 dbcontext.ts updated');
|
38
|
-
}
|
39
|
-
|
40
|
-
function generateEntityContent(tableName: string, tableNameCapitalized: string): string {
|
41
|
-
return `import * as mongoose from 'mongoose';
|
42
|
-
import { ObjectId } from "bson";
|
43
|
-
|
44
|
-
export interface I${tableNameCapitalized} extends mongoose.Document {
|
45
|
-
_id: ObjectId,
|
46
|
-
name: string,
|
47
|
-
description?: string,
|
48
|
-
status: number,
|
49
|
-
createdAt: Date,
|
50
|
-
updatedAt: Date
|
51
|
-
}`;
|
52
|
-
}
|
53
|
-
|
54
|
-
function generateSchemaContent(tableName: string, tableNameCapitalized: string): string {
|
55
|
-
return `import * as mongoose from 'mongoose';
|
56
|
-
import { I${tableNameCapitalized} } from '../entities/${tableName}';
|
57
|
-
|
58
|
-
export const ${tableName}Schema = new mongoose.Schema(
|
59
|
-
{
|
60
|
-
name: {
|
61
|
-
type: mongoose.SchemaTypes.String,
|
62
|
-
required: true,
|
63
|
-
},
|
64
|
-
description: {
|
65
|
-
type: mongoose.SchemaTypes.String,
|
66
|
-
required: false,
|
67
|
-
},
|
68
|
-
status: {
|
69
|
-
type: mongoose.SchemaTypes.Number,
|
70
|
-
default: 1,
|
71
|
-
}
|
72
|
-
},
|
73
|
-
{ timestamps: true, suppressReservedKeysWarning: true },
|
74
|
-
);
|
75
|
-
|
76
|
-
export const ${tableNameCapitalized}Entity = mongoose.model<I${tableNameCapitalized}>('${tableName}', ${tableName}Schema);`;
|
77
|
-
}
|
78
|
-
|
79
|
-
function updateDbContext(currentDir: string, tableName: string, tableNameCapitalized: string): void {
|
80
|
-
const dbcontextPath = path.join(currentDir, 'src/helpers/dbcontext.ts');
|
81
|
-
if (!fs.existsSync(dbcontextPath)) {
|
82
|
-
console.log('⚠️ dbcontext.ts not found, skipping dbcontext update');
|
83
|
-
return;
|
84
|
-
}
|
85
|
-
|
86
|
-
let dbcontextContent = fs.readFileSync(dbcontextPath, 'utf8');
|
87
|
-
|
88
|
-
// Add import
|
89
|
-
const importStatement = `import { ${tableNameCapitalized}Entity } from '../schemas/${tableName}';`;
|
90
|
-
if (!dbcontextContent.includes(importStatement)) {
|
91
|
-
// Find the last import statement and add after it
|
92
|
-
const importRegex = /import.*from.*['"];?\s*$/gm;
|
93
|
-
const matches = [...dbcontextContent.matchAll(importRegex)];
|
94
|
-
if (matches.length > 0) {
|
95
|
-
const lastImport = matches[matches.length - 1];
|
96
|
-
const insertIndex = lastImport.index! + lastImport[0].length;
|
97
|
-
dbcontextContent = dbcontextContent.slice(0, insertIndex) + '\n' + importStatement + dbcontextContent.slice(insertIndex);
|
98
|
-
}
|
99
|
-
}
|
100
|
-
|
101
|
-
// Add to dbcontext object
|
102
|
-
const dbcontextObjectRegex = /export\s+default\s*\{([^}]*)\}/s;
|
103
|
-
const match = dbcontextContent.match(dbcontextObjectRegex);
|
104
|
-
if (match) {
|
105
|
-
const objectContent = match[1];
|
106
|
-
if (!objectContent.includes(`${tableNameCapitalized}Entity`)) {
|
107
|
-
const newObjectContent = objectContent.trim() + `,\n ${tableNameCapitalized}Entity`;
|
108
|
-
dbcontextContent = dbcontextContent.replace(dbcontextObjectRegex, `export default {$1${newObjectContent}\n}`);
|
109
|
-
}
|
110
|
-
}
|
111
|
-
|
112
|
-
fs.writeFileSync(dbcontextPath, dbcontextContent);
|
113
|
-
console.log('📊 Updated dbcontext.ts');
|
114
|
-
}
|
@@ -1,166 +0,0 @@
|
|
1
|
-
import * as fs from 'fs';
|
2
|
-
import * as path from 'path';
|
3
|
-
|
4
|
-
export async function processTemplate(projectName: string): Promise<void> {
|
5
|
-
if (!projectName) {
|
6
|
-
throw new Error('Project name is required');
|
7
|
-
}
|
8
|
-
|
9
|
-
const currentDir = process.cwd();
|
10
|
-
|
11
|
-
// Check for required API project structure
|
12
|
-
const requiredDirs = [
|
13
|
-
'src/entities',
|
14
|
-
'src/schemas',
|
15
|
-
'src/services',
|
16
|
-
'src/controllers',
|
17
|
-
'src/routes'
|
18
|
-
];
|
19
|
-
const missingDirs = requiredDirs.filter(dir => !fs.existsSync(path.join(currentDir, dir)));
|
20
|
-
if (missingDirs.length > 0) {
|
21
|
-
throw new Error(`This command should be run from a diagramers API project. Missing directories: ${missingDirs.join(', ')}`);
|
22
|
-
}
|
23
|
-
|
24
|
-
// Convert project name to database-friendly format
|
25
|
-
const dbName = projectName
|
26
|
-
.toLowerCase()
|
27
|
-
.replace(/[^a-zA-Z0-9]/g, '_')
|
28
|
-
.replace(/_+/g, '_')
|
29
|
-
.replace(/^_|_$/g, '');
|
30
|
-
|
31
|
-
console.log(`🗄️ Database name will be: ${dbName}`);
|
32
|
-
|
33
|
-
// Process configuration files
|
34
|
-
console.log('📝 Updating configuration files...');
|
35
|
-
|
36
|
-
// Update development config
|
37
|
-
const devConfigPath = path.join(currentDir, 'src/config/development.ts');
|
38
|
-
if (fs.existsSync(devConfigPath)) {
|
39
|
-
let devConfig = fs.readFileSync(devConfigPath, 'utf8');
|
40
|
-
devConfig = devConfig.replace(
|
41
|
-
/mongodb:\/\/127\.0\.0\.1:27017\/[^,\s]+/g,
|
42
|
-
`mongodb://127.0.0.1:27017/${dbName}-development`
|
43
|
-
);
|
44
|
-
fs.writeFileSync(devConfigPath, devConfig);
|
45
|
-
console.log(' ✅ Updated development.ts');
|
46
|
-
}
|
47
|
-
|
48
|
-
// Update staging config
|
49
|
-
const stagingConfigPath = path.join(currentDir, 'src/config/staging.ts');
|
50
|
-
if (fs.existsSync(stagingConfigPath)) {
|
51
|
-
let stagingConfig = fs.readFileSync(stagingConfigPath, 'utf8');
|
52
|
-
stagingConfig = stagingConfig.replace(
|
53
|
-
/sendifier-staging/g,
|
54
|
-
`${dbName}-staging`
|
55
|
-
);
|
56
|
-
fs.writeFileSync(stagingConfigPath, stagingConfig);
|
57
|
-
console.log(' ✅ Updated staging.ts');
|
58
|
-
}
|
59
|
-
|
60
|
-
// Update production config
|
61
|
-
const prodConfigPath = path.join(currentDir, 'src/config/production.ts');
|
62
|
-
if (fs.existsSync(prodConfigPath)) {
|
63
|
-
let prodConfig = fs.readFileSync(prodConfigPath, 'utf8');
|
64
|
-
prodConfig = prodConfig.replace(
|
65
|
-
/sendifier-production/g,
|
66
|
-
`${dbName}-production`
|
67
|
-
);
|
68
|
-
fs.writeFileSync(prodConfigPath, prodConfig);
|
69
|
-
console.log(' ✅ Updated production.ts');
|
70
|
-
}
|
71
|
-
|
72
|
-
// Update package.json
|
73
|
-
const packageJsonPath = path.join(currentDir, 'package.json');
|
74
|
-
if (fs.existsSync(packageJsonPath)) {
|
75
|
-
let packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8');
|
76
|
-
packageJsonContent = packageJsonContent.replace(
|
77
|
-
/"name":\s*"@diagramers\/api"/g,
|
78
|
-
`"name": "${projectName}"`
|
79
|
-
);
|
80
|
-
packageJsonContent = packageJsonContent.replace(
|
81
|
-
/"description":\s*"Diagramers API - A comprehensive Node\.js API template with TypeScript, Firebase Functions, and Socket\.io"/g,
|
82
|
-
`"description": "${projectName} - API project"`
|
83
|
-
);
|
84
|
-
fs.writeFileSync(packageJsonPath, packageJsonContent);
|
85
|
-
console.log(' ✅ Updated package.json');
|
86
|
-
}
|
87
|
-
|
88
|
-
// Update README.md
|
89
|
-
const readmePath = path.join(currentDir, 'README.md');
|
90
|
-
if (fs.existsSync(readmePath)) {
|
91
|
-
let readmeContent = fs.readFileSync(readmePath, 'utf8');
|
92
|
-
readmeContent = readmeContent.replace(
|
93
|
-
/# @diagramers\/api/g,
|
94
|
-
`# ${projectName}`
|
95
|
-
);
|
96
|
-
readmeContent = readmeContent.replace(
|
97
|
-
/A comprehensive Node\.js API template with TypeScript, Firebase Functions, and Socket\.io\./g,
|
98
|
-
`${projectName} - API project.`
|
99
|
-
);
|
100
|
-
fs.writeFileSync(readmePath, readmeContent);
|
101
|
-
console.log(' ✅ Updated README.md');
|
102
|
-
}
|
103
|
-
|
104
|
-
// Create .env file template
|
105
|
-
console.log('📄 Creating .env template...');
|
106
|
-
const envTemplate = generateEnvTemplate(dbName);
|
107
|
-
fs.writeFileSync(path.join(currentDir, '.env.example'), envTemplate);
|
108
|
-
|
109
|
-
console.log('✅ Template processing completed!');
|
110
|
-
console.log('');
|
111
|
-
console.log('📋 Summary:');
|
112
|
-
console.log(` Project Name: ${projectName}`);
|
113
|
-
console.log(` Database Name: ${dbName}`);
|
114
|
-
console.log(` Development DB: ${dbName}-development`);
|
115
|
-
console.log(` Staging DB: ${dbName}-staging`);
|
116
|
-
console.log(` Production DB: ${dbName}-production`);
|
117
|
-
console.log('');
|
118
|
-
console.log('🚀 Next steps:');
|
119
|
-
console.log(' 1. Copy .env.example to .env and configure your environment variables');
|
120
|
-
console.log(' 2. Install dependencies: npm install');
|
121
|
-
console.log(' 3. Build the project: npm run build:dev');
|
122
|
-
console.log(' 4. Start the server: npm run serve');
|
123
|
-
}
|
124
|
-
|
125
|
-
function generateEnvTemplate(dbName: string): string {
|
126
|
-
return `# Environment Configuration
|
127
|
-
NODE_ENV=development
|
128
|
-
|
129
|
-
# Server Configuration
|
130
|
-
PORT=4000
|
131
|
-
HOST=localhost
|
132
|
-
|
133
|
-
# Database Configuration
|
134
|
-
MONGODB_URI=mongodb://127.0.0.1:27017/${dbName}-development
|
135
|
-
|
136
|
-
# Firebase Configuration
|
137
|
-
FIREBASE_API_KEY=your-api-key
|
138
|
-
FIREBASE_AUTH_DOMAIN=your-project.firebaseapp.com
|
139
|
-
FIREBASE_PROJECT_ID=your-project-id
|
140
|
-
FIREBASE_STORAGE_BUCKET=your-project.appspot.com
|
141
|
-
FIREBASE_MESSAGING_SENDER_ID=your-sender-id
|
142
|
-
FIREBASE_APP_ID=your-app-id
|
143
|
-
|
144
|
-
# JWT Configuration
|
145
|
-
JWT_SECRET=your-jwt-secret-key
|
146
|
-
|
147
|
-
# Twilio Configuration (for SMS)
|
148
|
-
TWILIO_ACCOUNT_SID=your-account-sid
|
149
|
-
TWILIO_AUTH_TOKEN=your-auth-token
|
150
|
-
|
151
|
-
# Email Configuration
|
152
|
-
SMTP_HOST=smtp.gmail.com
|
153
|
-
SMTP_PORT=587
|
154
|
-
SMTP_USER=your-email@gmail.com
|
155
|
-
SMTP_PASS=your-app-password
|
156
|
-
|
157
|
-
# Internal API Configuration
|
158
|
-
INTERNAL_REQUEST_HEADER_VALUE=your-secret-header-value
|
159
|
-
|
160
|
-
# Optional Configuration
|
161
|
-
BASE_SITE_URL=http://localhost:3000
|
162
|
-
LOCAL_API_TUNNEL_URL=https://your-ngrok-url.ngrok-free.app
|
163
|
-
LOCAL_UI_TUNNEL_URL=https://your-ui-ngrok-url.ngrok-free.app
|
164
|
-
BACKUP_DATABASES_FOLDER_PATH=/path/to/backup/folder
|
165
|
-
EXTERNAL_HTTP_REQUEST_TIMEOUT=5000`;
|
166
|
-
}
|