webspresso 0.0.13 → 0.0.14
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/README.md +4 -7
- package/bin/commands/add-tailwind.js +151 -0
- package/bin/commands/api.js +70 -0
- package/bin/commands/db-make.js +76 -0
- package/bin/commands/db-migrate.js +43 -0
- package/bin/commands/db-rollback.js +48 -0
- package/bin/commands/db-status.js +53 -0
- package/bin/commands/dev.js +73 -0
- package/bin/commands/new.js +634 -0
- package/bin/commands/page.js +134 -0
- package/bin/commands/seed.js +154 -0
- package/bin/commands/start.js +30 -0
- package/bin/utils/db.js +54 -0
- package/bin/utils/migration.js +36 -0
- package/bin/utils/project.js +97 -0
- package/bin/utils/seed.js +112 -0
- package/bin/webspresso.js +24 -1696
- package/core/orm/index.js +14 -1
- package/core/orm/migrations/scaffold.js +5 -0
- package/core/orm/model.js +8 -0
- package/core/orm/schema-helpers.js +39 -1
- package/core/orm/seeder.js +56 -3
- package/core/orm/types.js +28 -1
- package/index.js +2 -1
- package/package.json +1 -1
- package/plugins/admin-panel/admin-user-model.js +42 -0
- package/plugins/admin-panel/api.js +436 -0
- package/plugins/admin-panel/app.js +68 -0
- package/plugins/admin-panel/auth.js +157 -0
- package/plugins/admin-panel/components.js +359 -0
- package/plugins/admin-panel/field-renderers/array.js +57 -0
- package/plugins/admin-panel/field-renderers/basic.js +205 -0
- package/plugins/admin-panel/field-renderers/file-upload.js +124 -0
- package/plugins/admin-panel/field-renderers/index.js +93 -0
- package/plugins/admin-panel/field-renderers/json.js +52 -0
- package/plugins/admin-panel/field-renderers/relations.js +96 -0
- package/plugins/admin-panel/field-renderers/rich-text.js +83 -0
- package/plugins/admin-panel/index.js +187 -0
- package/plugins/admin-panel/migration-template.js +39 -0
- package/plugins/admin-panel/styles.js +9 -0
- package/plugins/index.js +2 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page Command
|
|
3
|
+
* Add a new page to the current project
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const inquirer = require('inquirer');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
function registerCommand(program) {
|
|
11
|
+
program
|
|
12
|
+
.command('page')
|
|
13
|
+
.description('Add a new page to the current project')
|
|
14
|
+
.action(async () => {
|
|
15
|
+
if (!fs.existsSync('pages')) {
|
|
16
|
+
console.error('❌ Not a Webspresso project! Run this command in your project directory.');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const answers = await inquirer.prompt([
|
|
21
|
+
{
|
|
22
|
+
type: 'input',
|
|
23
|
+
name: 'route',
|
|
24
|
+
message: 'Route path (e.g., /about or /blog/post):',
|
|
25
|
+
validate: (input) => {
|
|
26
|
+
if (!input.startsWith('/')) {
|
|
27
|
+
return 'Route must start with /';
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
type: 'confirm',
|
|
34
|
+
name: 'hasConfig',
|
|
35
|
+
message: 'Add route config file (.js)?',
|
|
36
|
+
default: false
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
type: 'confirm',
|
|
40
|
+
name: 'hasLocales',
|
|
41
|
+
message: 'Add locale files?',
|
|
42
|
+
default: false
|
|
43
|
+
}
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
const route = answers.route.replace(/^\//, '');
|
|
47
|
+
const routePath = path.join('pages', route);
|
|
48
|
+
const dirPath = path.dirname(routePath);
|
|
49
|
+
const fileName = path.basename(routePath);
|
|
50
|
+
|
|
51
|
+
// Create directory
|
|
52
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
53
|
+
|
|
54
|
+
// Create .njk file
|
|
55
|
+
const templateName = fileName === 'index' ? 'index' : fileName;
|
|
56
|
+
const njkFile = path.join(dirPath, `${templateName}.njk`);
|
|
57
|
+
|
|
58
|
+
const njkContent = `{% extends "layout.njk" %}
|
|
59
|
+
|
|
60
|
+
{% block content %}
|
|
61
|
+
<div>
|
|
62
|
+
<h1>{{ t('title') or '${route}' }}</h1>
|
|
63
|
+
<p>{{ t('description') or 'Page content' }}</p>
|
|
64
|
+
</div>
|
|
65
|
+
{% endblock %}
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
fs.writeFileSync(njkFile, njkContent);
|
|
69
|
+
console.log(`✅ Created ${njkFile}`);
|
|
70
|
+
|
|
71
|
+
// Create config file if requested
|
|
72
|
+
if (answers.hasConfig) {
|
|
73
|
+
const jsFile = path.join(dirPath, `${templateName}.js`);
|
|
74
|
+
const jsContent = `module.exports = {
|
|
75
|
+
async load(req, ctx) {
|
|
76
|
+
return {
|
|
77
|
+
// Add your data here
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
meta(req, ctx) {
|
|
82
|
+
return {
|
|
83
|
+
title: ctx.t('meta.title') || '${route}',
|
|
84
|
+
description: ctx.t('meta.description') || ''
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
`;
|
|
89
|
+
|
|
90
|
+
fs.writeFileSync(jsFile, jsContent);
|
|
91
|
+
console.log(`✅ Created ${jsFile}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Create locales if requested
|
|
95
|
+
if (answers.hasLocales) {
|
|
96
|
+
const localesDir = path.join(dirPath, 'locales');
|
|
97
|
+
fs.mkdirSync(localesDir, { recursive: true });
|
|
98
|
+
|
|
99
|
+
const enContent = {
|
|
100
|
+
title: route,
|
|
101
|
+
description: 'Page description',
|
|
102
|
+
meta: {
|
|
103
|
+
title: `${route} - Webspresso`,
|
|
104
|
+
description: 'Page description'
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
fs.writeFileSync(
|
|
109
|
+
path.join(localesDir, 'en.json'),
|
|
110
|
+
JSON.stringify(enContent, null, 2) + '\n'
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const trContent = {
|
|
114
|
+
title: route,
|
|
115
|
+
description: 'Sayfa açıklaması',
|
|
116
|
+
meta: {
|
|
117
|
+
title: `${route} - Webspresso`,
|
|
118
|
+
description: 'Sayfa açıklaması'
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
fs.writeFileSync(
|
|
123
|
+
path.join(localesDir, 'tr.json'),
|
|
124
|
+
JSON.stringify(trContent, null, 2) + '\n'
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
console.log(`✅ Created locale files in ${localesDir}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
console.log(`\n✅ Page created at ${route}\n`);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
module.exports = { registerCommand };
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seed Command
|
|
3
|
+
* Run database seeders to populate database with fake data
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const inquirer = require('inquirer');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const { execSync } = require('child_process');
|
|
10
|
+
const { loadDbConfig } = require('../utils/db');
|
|
11
|
+
const { getSeedFileTemplate } = require('../utils/seed');
|
|
12
|
+
|
|
13
|
+
function registerCommand(program) {
|
|
14
|
+
program
|
|
15
|
+
.command('seed')
|
|
16
|
+
.description('Run database seeders to populate database with fake data')
|
|
17
|
+
.option('-c, --config <path>', 'Path to database config file')
|
|
18
|
+
.option('-e, --env <environment>', 'Environment (development, production)', 'development')
|
|
19
|
+
.option('--setup', 'Setup seed files if they don\'t exist')
|
|
20
|
+
.action(async (options) => {
|
|
21
|
+
// Check if it's a Webspresso project
|
|
22
|
+
if (!fs.existsSync(path.join(process.cwd(), 'server.js')) &&
|
|
23
|
+
!fs.existsSync(path.join(process.cwd(), 'pages'))) {
|
|
24
|
+
console.error('❌ Not a Webspresso project! Run this command in your project directory.');
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Check for faker in the project's node_modules
|
|
29
|
+
// Use require.resolve with paths to ensure we load from the project directory
|
|
30
|
+
let faker;
|
|
31
|
+
try {
|
|
32
|
+
// Try to resolve from project's node_modules first
|
|
33
|
+
const resolvedPath = require.resolve('@faker-js/faker', {
|
|
34
|
+
paths: [process.cwd(), path.join(process.cwd(), 'node_modules')]
|
|
35
|
+
});
|
|
36
|
+
faker = require(resolvedPath);
|
|
37
|
+
} catch {
|
|
38
|
+
console.error('❌ @faker-js/faker not installed.');
|
|
39
|
+
|
|
40
|
+
// Check if it's in package.json
|
|
41
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
42
|
+
let shouldInstall = false;
|
|
43
|
+
|
|
44
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
45
|
+
try {
|
|
46
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
47
|
+
const hasFaker = packageJson.dependencies?.['@faker-js/faker'] ||
|
|
48
|
+
packageJson.devDependencies?.['@faker-js/faker'];
|
|
49
|
+
|
|
50
|
+
if (hasFaker) {
|
|
51
|
+
console.log(' @faker-js/faker is in package.json but not installed.');
|
|
52
|
+
console.log(' Run: npm install');
|
|
53
|
+
process.exit(1);
|
|
54
|
+
} else {
|
|
55
|
+
// Ask if user wants to install it
|
|
56
|
+
const { install } = await inquirer.prompt([
|
|
57
|
+
{
|
|
58
|
+
type: 'confirm',
|
|
59
|
+
name: 'install',
|
|
60
|
+
message: 'Install @faker-js/faker now?',
|
|
61
|
+
default: true
|
|
62
|
+
}
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
if (install) {
|
|
66
|
+
shouldInstall = true;
|
|
67
|
+
} else {
|
|
68
|
+
console.log(' Install it manually with: npm install @faker-js/faker');
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
} catch (err) {
|
|
73
|
+
console.log(' Install it with: npm install @faker-js/faker');
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
console.log(' Install it with: npm install @faker-js/faker');
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Install faker if user confirmed
|
|
82
|
+
if (shouldInstall) {
|
|
83
|
+
console.log('\n📦 Installing @faker-js/faker...\n');
|
|
84
|
+
try {
|
|
85
|
+
execSync('npm install @faker-js/faker', {
|
|
86
|
+
stdio: 'inherit',
|
|
87
|
+
cwd: process.cwd()
|
|
88
|
+
});
|
|
89
|
+
console.log('\n✅ @faker-js/faker installed successfully!\n');
|
|
90
|
+
|
|
91
|
+
// Try to require again from project's node_modules
|
|
92
|
+
const resolvedPath = require.resolve('@faker-js/faker', {
|
|
93
|
+
paths: [process.cwd(), path.join(process.cwd(), 'node_modules')]
|
|
94
|
+
});
|
|
95
|
+
// Clear cache to ensure fresh load
|
|
96
|
+
delete require.cache[resolvedPath];
|
|
97
|
+
faker = require(resolvedPath);
|
|
98
|
+
} catch (err) {
|
|
99
|
+
console.error('\n❌ Failed to install @faker-js/faker:', err.message);
|
|
100
|
+
console.log(' Install it manually with: npm install @faker-js/faker');
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Load database config
|
|
107
|
+
const { config, path: configPath } = loadDbConfig(options.config);
|
|
108
|
+
console.log(`\n📦 Using config: ${configPath}`);
|
|
109
|
+
console.log(` Environment: ${options.env}\n`);
|
|
110
|
+
|
|
111
|
+
// Check if models directory exists
|
|
112
|
+
const modelsDir = path.join(process.cwd(), 'models');
|
|
113
|
+
if (!fs.existsSync(modelsDir)) {
|
|
114
|
+
console.error('❌ models/ directory not found.');
|
|
115
|
+
console.log(' Create models first, then run: webspresso seed');
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Check if seeds directory exists
|
|
120
|
+
const seedsDir = path.join(process.cwd(), 'seeds');
|
|
121
|
+
const seedIndexPath = path.join(seedsDir, 'index.js');
|
|
122
|
+
|
|
123
|
+
if (!fs.existsSync(seedsDir) || !fs.existsSync(seedIndexPath)) {
|
|
124
|
+
if (options.setup) {
|
|
125
|
+
console.log('📝 Setting up seed files...\n');
|
|
126
|
+
|
|
127
|
+
// Create seeds directory
|
|
128
|
+
if (!fs.existsSync(seedsDir)) {
|
|
129
|
+
fs.mkdirSync(seedsDir, { recursive: true });
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Create seed index file
|
|
133
|
+
const seedIndex = getSeedFileTemplate();
|
|
134
|
+
|
|
135
|
+
fs.writeFileSync(seedIndexPath, seedIndex);
|
|
136
|
+
console.log('✅ Seed files created!\n');
|
|
137
|
+
} else {
|
|
138
|
+
console.error('❌ seeds/index.js not found.');
|
|
139
|
+
console.log(' Run with --setup flag to create seed files: webspresso seed --setup');
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Run the seed script
|
|
145
|
+
try {
|
|
146
|
+
require(seedIndexPath);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error('❌ Failed to run seed:', error.message);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
module.exports = { registerCommand };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Start Command
|
|
3
|
+
* Start production server
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
function registerCommand(program) {
|
|
10
|
+
program
|
|
11
|
+
.command('start')
|
|
12
|
+
.description('Start production server')
|
|
13
|
+
.option('-p, --port <port>', 'Port number', '3000')
|
|
14
|
+
.action((options) => {
|
|
15
|
+
if (!fs.existsSync('server.js')) {
|
|
16
|
+
console.error('❌ server.js not found! Make sure you are in a Webspresso project.');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
process.env.PORT = options.port;
|
|
21
|
+
process.env.NODE_ENV = 'production';
|
|
22
|
+
|
|
23
|
+
console.log(`\n🚀 Starting production server on port ${options.port}...\n`);
|
|
24
|
+
|
|
25
|
+
const serverPath = path.resolve(process.cwd(), 'server.js');
|
|
26
|
+
require(serverPath);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = { registerCommand };
|
package/bin/utils/db.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database Utilities
|
|
3
|
+
* Functions for loading database configuration and creating instances
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Load database configuration
|
|
11
|
+
* @param {string} [configPath] - Custom config path
|
|
12
|
+
* @returns {Object} Database config
|
|
13
|
+
*/
|
|
14
|
+
function loadDbConfig(configPath) {
|
|
15
|
+
const defaultPaths = ['webspresso.db.js', 'knexfile.js'];
|
|
16
|
+
const paths = configPath ? [configPath, ...defaultPaths] : defaultPaths;
|
|
17
|
+
|
|
18
|
+
for (const p of paths) {
|
|
19
|
+
const fullPath = path.resolve(process.cwd(), p);
|
|
20
|
+
if (fs.existsSync(fullPath)) {
|
|
21
|
+
return { config: require(fullPath), path: fullPath };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.error('❌ Database config not found. Create webspresso.db.js or knexfile.js');
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Create database instance from config
|
|
31
|
+
* @param {Object} config - Database config
|
|
32
|
+
* @param {string} [env] - Environment name
|
|
33
|
+
* @returns {Promise<Object>} Database instance
|
|
34
|
+
*/
|
|
35
|
+
async function createDbInstance(config, env) {
|
|
36
|
+
const environment = env || process.env.NODE_ENV || 'development';
|
|
37
|
+
const dbConfig = config[environment] || config;
|
|
38
|
+
|
|
39
|
+
// Dynamic import knex
|
|
40
|
+
let knex;
|
|
41
|
+
try {
|
|
42
|
+
knex = require('knex');
|
|
43
|
+
} catch {
|
|
44
|
+
console.error('❌ Knex not installed. Run: npm install knex');
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return knex(dbConfig);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = {
|
|
52
|
+
loadDbConfig,
|
|
53
|
+
createDbInstance
|
|
54
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration Utilities
|
|
3
|
+
* Functions for generating migration file content
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Get default migration content
|
|
8
|
+
* @param {string} name - Migration name
|
|
9
|
+
* @returns {string}
|
|
10
|
+
*/
|
|
11
|
+
function getDefaultMigrationContent(name) {
|
|
12
|
+
// Parse table name from migration name (e.g., create_users_table -> users)
|
|
13
|
+
const match = name.match(/^create_(\w+)_table$/);
|
|
14
|
+
const tableName = match ? match[1] : 'table_name';
|
|
15
|
+
|
|
16
|
+
return `/**
|
|
17
|
+
* Migration: ${name}
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
exports.up = function(knex) {
|
|
21
|
+
return knex.schema.createTable('${tableName}', (table) => {
|
|
22
|
+
table.bigIncrements('id').primary();
|
|
23
|
+
// Add your columns here
|
|
24
|
+
table.timestamps(true, true);
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
exports.down = function(knex) {
|
|
29
|
+
return knex.schema.dropTableIfExists('${tableName}');
|
|
30
|
+
};
|
|
31
|
+
`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = {
|
|
35
|
+
getDefaultMigrationContent
|
|
36
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project Setup Utilities
|
|
3
|
+
* Functions for installing dependencies and starting development server
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { execSync, spawn } = require('child_process');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Run installation and build process
|
|
10
|
+
* @param {string} projectPath - Project directory path
|
|
11
|
+
* @param {boolean} useTailwind - Whether Tailwind CSS is enabled
|
|
12
|
+
* @returns {Promise<void>}
|
|
13
|
+
*/
|
|
14
|
+
async function runInstallation(projectPath, useTailwind) {
|
|
15
|
+
console.log('\n📦 Installing dependencies...\n');
|
|
16
|
+
try {
|
|
17
|
+
execSync('npm install', {
|
|
18
|
+
stdio: 'inherit',
|
|
19
|
+
cwd: projectPath
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
if (useTailwind) {
|
|
23
|
+
console.log('\n🎨 Building Tailwind CSS...\n');
|
|
24
|
+
execSync('npm run build:css', {
|
|
25
|
+
stdio: 'inherit',
|
|
26
|
+
cwd: projectPath
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
console.log('\n✅ Installation complete!\n');
|
|
31
|
+
} catch (err) {
|
|
32
|
+
console.error('❌ Installation failed:', err.message);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Start development server
|
|
39
|
+
* @param {string} projectPath - Project directory path
|
|
40
|
+
* @param {boolean} useTailwind - Whether Tailwind CSS is enabled
|
|
41
|
+
* @returns {void}
|
|
42
|
+
*/
|
|
43
|
+
function startDevServer(projectPath, useTailwind) {
|
|
44
|
+
console.log('\n🚀 Starting development server...\n');
|
|
45
|
+
console.log('Press Ctrl+C to stop\n');
|
|
46
|
+
|
|
47
|
+
if (useTailwind) {
|
|
48
|
+
// Start CSS watch and server together
|
|
49
|
+
const cssWatch = spawn('npm', ['run', 'watch:css'], {
|
|
50
|
+
stdio: 'inherit',
|
|
51
|
+
shell: true,
|
|
52
|
+
cwd: projectPath
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const server = spawn('node', ['--watch', 'server.js'], {
|
|
56
|
+
stdio: 'inherit',
|
|
57
|
+
shell: true,
|
|
58
|
+
cwd: projectPath,
|
|
59
|
+
env: { ...process.env, PORT: process.env.PORT || '3000', NODE_ENV: 'development' }
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Handle exit
|
|
63
|
+
const cleanup = () => {
|
|
64
|
+
cssWatch.kill();
|
|
65
|
+
server.kill();
|
|
66
|
+
process.exit(0);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
process.on('SIGINT', cleanup);
|
|
70
|
+
process.on('SIGTERM', cleanup);
|
|
71
|
+
|
|
72
|
+
cssWatch.on('exit', cleanup);
|
|
73
|
+
server.on('exit', cleanup);
|
|
74
|
+
} else {
|
|
75
|
+
// Just start server
|
|
76
|
+
const server = spawn('node', ['--watch', 'server.js'], {
|
|
77
|
+
stdio: 'inherit',
|
|
78
|
+
shell: true,
|
|
79
|
+
cwd: projectPath,
|
|
80
|
+
env: { ...process.env, PORT: process.env.PORT || '3000', NODE_ENV: 'development' }
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
server.on('exit', (code) => {
|
|
84
|
+
process.exit(code || 0);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
process.on('SIGINT', () => {
|
|
88
|
+
server.kill();
|
|
89
|
+
process.exit(0);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
module.exports = {
|
|
95
|
+
runInstallation,
|
|
96
|
+
startDevServer
|
|
97
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seed Utilities
|
|
3
|
+
* Functions for generating seed file templates
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Get seed file template content
|
|
8
|
+
* @returns {string} Seed file template
|
|
9
|
+
*/
|
|
10
|
+
function getSeedFileTemplate() {
|
|
11
|
+
return `require('dotenv').config();
|
|
12
|
+
const { faker } = require('@faker-js/faker');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const { createDatabase, getAllModels } = require('webspresso/orm');
|
|
16
|
+
const dbConfig = require('../webspresso.db.js');
|
|
17
|
+
|
|
18
|
+
const db = createDatabase(dbConfig);
|
|
19
|
+
const seeder = db.seeder(faker);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Load all models from models/ directory
|
|
23
|
+
*/
|
|
24
|
+
function loadModels() {
|
|
25
|
+
const modelsDir = path.join(__dirname, '..', 'models');
|
|
26
|
+
|
|
27
|
+
if (!fs.existsSync(modelsDir)) {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const modelFiles = fs.readdirSync(modelsDir)
|
|
32
|
+
.filter(file => file.endsWith('.js') && !file.startsWith('_'));
|
|
33
|
+
|
|
34
|
+
const loadedModels = [];
|
|
35
|
+
for (const file of modelFiles) {
|
|
36
|
+
try {
|
|
37
|
+
require(path.join(modelsDir, file));
|
|
38
|
+
loadedModels.push(file);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.warn(\`⚠️ Failed to load model from \${file}:\`, error.message);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return loadedModels;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Seed database with fake data
|
|
49
|
+
* This script automatically detects models in the models/ directory
|
|
50
|
+
* and generates seed data based on their schemas.
|
|
51
|
+
*/
|
|
52
|
+
async function runSeeds() {
|
|
53
|
+
try {
|
|
54
|
+
console.log('🌱 Starting seed process...');
|
|
55
|
+
|
|
56
|
+
// Load all models
|
|
57
|
+
const loadedFiles = loadModels();
|
|
58
|
+
|
|
59
|
+
if (loadedFiles.length === 0) {
|
|
60
|
+
console.log('⚠️ No model files found in models/ directory.');
|
|
61
|
+
console.log(' Create models first, then run: webspresso seed');
|
|
62
|
+
await db.knex.destroy();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Get all registered models
|
|
67
|
+
const models = getAllModels();
|
|
68
|
+
|
|
69
|
+
if (models.size === 0) {
|
|
70
|
+
console.log('⚠️ No models registered. Make sure your model files export models using defineModel().');
|
|
71
|
+
await db.knex.destroy();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
console.log(\`📦 Found \${models.size} model(s):\`);
|
|
76
|
+
for (const [name] of models) {
|
|
77
|
+
console.log(\` - \${name}\`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Seed each model
|
|
81
|
+
const results = {};
|
|
82
|
+
for (const [modelName, model] of models) {
|
|
83
|
+
console.log(\`\\n🌱 Seeding \${modelName}...\`);
|
|
84
|
+
|
|
85
|
+
// Default count: 10 records per model
|
|
86
|
+
const count = 10;
|
|
87
|
+
const records = await seeder.seed(modelName, count);
|
|
88
|
+
results[modelName] = records.length;
|
|
89
|
+
|
|
90
|
+
console.log(\`✅ Created \${records.length} \${modelName} record(s)\`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log(\`\\n✨ Seed completed! Created:\`);
|
|
94
|
+
for (const [modelName, count] of Object.entries(results)) {
|
|
95
|
+
console.log(\` - \${count} \${modelName} record(s)\`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
await db.knex.destroy();
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.error('❌ Seed failed:', error);
|
|
101
|
+
await db.knex.destroy().catch(() => {});
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
runSeeds();
|
|
107
|
+
`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
module.exports = {
|
|
111
|
+
getSeedFileTemplate
|
|
112
|
+
};
|