gingee-cli 1.0.5 → 1.5.1
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 +8 -1
- package/commands/init.js +245 -134
- package/commands/optionalFeatures.js +115 -0
- package/index.js +1 -1
- package/package.json +3 -3
- package/templates/glade.gin +0 -0
- package/templates/project/gingee.json +157 -34
- package/templates/project/package.json +1 -1
package/README.md
CHANGED
|
@@ -73,7 +73,14 @@ gingee-cli init my-awesome-project
|
|
|
73
73
|
**Wizard Prompts:**
|
|
74
74
|
- `Administrator Username for glade:` Sets the initial username for the bundled Glade admin panel. Defaults to `admin`.
|
|
75
75
|
- `Administrator Password for glade:` Securely prompts for the admin password. This is hashed and stored in Glade's configuration.
|
|
76
|
-
-
|
|
76
|
+
- **Optional feature packages:** Dependency profile for Gingee’s heavy/native optionals (image/`sharp`, non-SQLite SQL drivers, PDF, charts/canvas, SendGrid, Gemini):
|
|
77
|
+
- **Minimal** — core + SQLite only (fastest install; no `sharp` / image).
|
|
78
|
+
- **Recommended** (default) — image (`sharp`), PostgreSQL, PDF, charts, SendGrid, Gemini.
|
|
79
|
+
- **Full** — all optional packages (including Oracle and other SQL drivers).
|
|
80
|
+
- **Custom** — checkbox picker per feature.
|
|
81
|
+
- `Install npm dependencies automatically?` If yes (default), runs `npm install --omit=optional` for core, then `npm install <selected packages>` for optionals. If no, prints the commands to run later.
|
|
82
|
+
|
|
83
|
+
SQLite, email `console`, and AI `mock` work without optionals. Image processing needs `sharp`. Extra packages surface as `FEATURE_NOT_INSTALLED` only when used.
|
|
77
84
|
|
|
78
85
|
---
|
|
79
86
|
|
package/commands/init.js
CHANGED
|
@@ -1,134 +1,245 @@
|
|
|
1
|
-
const fs = require('fs-extra');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const { execSync } = require('child_process');
|
|
4
|
-
const argon2 = require('argon2');
|
|
5
|
-
const { _unzipBuffer } = require('./installerUtils');
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
|
+
const argon2 = require('argon2');
|
|
5
|
+
const { _unzipBuffer } = require('./installerUtils');
|
|
6
|
+
const {
|
|
7
|
+
packagesForFeatures,
|
|
8
|
+
resolveFeatureKeys,
|
|
9
|
+
checkboxChoices
|
|
10
|
+
} = require('./optionalFeatures');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {string} projectPath
|
|
14
|
+
* @param {string[]} npmPackages
|
|
15
|
+
* @param {object} spinner
|
|
16
|
+
* @param {object} chalk
|
|
17
|
+
*/
|
|
18
|
+
function installProjectDependencies(projectPath, npmPackages, spinner, chalk) {
|
|
19
|
+
// Always omit transitive optionals from gingee first (slim, resilient installs).
|
|
20
|
+
spinner.start('Installing core dependencies (npm install --omit=optional)...');
|
|
21
|
+
execSync('npm install --omit=optional', { cwd: projectPath, stdio: 'ignore' });
|
|
22
|
+
spinner.succeed('Core dependencies installed.');
|
|
23
|
+
|
|
24
|
+
if (npmPackages.length === 0) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
spinner.start(
|
|
29
|
+
`Installing selected optional packages (${npmPackages.length}): ${npmPackages.join(', ')}...`
|
|
30
|
+
);
|
|
31
|
+
// Install as direct deps of the project so require() from gingee can resolve them.
|
|
32
|
+
// Package names come from a fixed allowlist in optionalFeatures.js (not free-form user input).
|
|
33
|
+
execSync(`npm install ${npmPackages.join(' ')}`, {
|
|
34
|
+
cwd: projectPath,
|
|
35
|
+
stdio: 'ignore'
|
|
36
|
+
});
|
|
37
|
+
spinner.succeed('Optional feature packages installed.');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function init(projectName) {
|
|
41
|
+
const { default: ora } = await import('ora');
|
|
42
|
+
const { default: chalk } = await import('chalk');
|
|
43
|
+
const { default: inquirer } = await import('inquirer');
|
|
44
|
+
|
|
45
|
+
const spinner = ora();
|
|
46
|
+
try {
|
|
47
|
+
console.log(chalk.blueBright("🚀 Welcome to Gingee! Let's create your new project."));
|
|
48
|
+
const projectPath = path.resolve(process.cwd(), projectName);
|
|
49
|
+
|
|
50
|
+
let currentPath = process.cwd();
|
|
51
|
+
while (currentPath !== path.parse(currentPath).root) {
|
|
52
|
+
if (fs.existsSync(path.join(currentPath, 'gingee.json'))) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Command cannot be run inside an existing Gingee project.\nDetected project root at: ${currentPath}`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
currentPath = path.dirname(currentPath);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (fs.existsSync(projectPath)) {
|
|
61
|
+
throw new Error(`Directory '${projectName}' already exists. Please choose another name.`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const answers = await inquirer.prompt([
|
|
65
|
+
{
|
|
66
|
+
type: 'input',
|
|
67
|
+
name: 'adminUser',
|
|
68
|
+
message: 'Enter a username for the Glade admin panel:',
|
|
69
|
+
default: 'admin'
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
type: 'password',
|
|
73
|
+
name: 'adminPass',
|
|
74
|
+
message: 'Enter a password for the Glade admin:',
|
|
75
|
+
mask: '*',
|
|
76
|
+
validate: (input) => {
|
|
77
|
+
if (!input || input.length < 8) {
|
|
78
|
+
return 'Password must be at least 8 characters long.';
|
|
79
|
+
}
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
type: 'password',
|
|
85
|
+
name: 'confirmPassword',
|
|
86
|
+
message: 'Confirm the new password:',
|
|
87
|
+
mask: '*'
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
type: 'list',
|
|
91
|
+
name: 'depProfile',
|
|
92
|
+
message:
|
|
93
|
+
'Optional feature packages (image/sharp, SQL drivers other than SQLite, PDF, charts, SendGrid, Gemini):',
|
|
94
|
+
default: 'recommended',
|
|
95
|
+
choices: [
|
|
96
|
+
{
|
|
97
|
+
name: 'Minimal — core + SQLite only (fastest; no sharp / image; omit=optional style)',
|
|
98
|
+
value: 'minimal'
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'Recommended — image (sharp), PostgreSQL, PDF, charts, SendGrid, Gemini (good default)',
|
|
102
|
+
value: 'recommended'
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: 'Full — all optional packages (image, all SQL drivers, media, providers)',
|
|
106
|
+
value: 'full'
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: 'Custom — choose packages…',
|
|
110
|
+
value: 'custom'
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
type: 'checkbox',
|
|
116
|
+
name: 'customFeatures',
|
|
117
|
+
message: 'Select optional features to install:',
|
|
118
|
+
choices: checkboxChoices(),
|
|
119
|
+
when: (a) => a.depProfile === 'custom',
|
|
120
|
+
pageSize: 12
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
type: 'confirm',
|
|
124
|
+
name: 'installDeps',
|
|
125
|
+
message: 'Install npm dependencies automatically?',
|
|
126
|
+
default: true
|
|
127
|
+
}
|
|
128
|
+
]);
|
|
129
|
+
|
|
130
|
+
if (answers.adminPass !== answers.confirmPassword) {
|
|
131
|
+
throw new Error('Passwords do not match. Please try again.');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (!answers.adminPass) {
|
|
135
|
+
throw new Error('Admin password cannot be empty.');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const featureKeys = resolveFeatureKeys(answers.depProfile, answers.customFeatures);
|
|
139
|
+
const optionalPackages = packagesForFeatures(featureKeys);
|
|
140
|
+
|
|
141
|
+
spinner.start('Scaffolding project files...');
|
|
142
|
+
fs.mkdirSync(projectPath);
|
|
143
|
+
const templatePath = path.join(__dirname, '..', 'templates');
|
|
144
|
+
const projectTemplatePath = path.join(templatePath, 'project');
|
|
145
|
+
fs.copySync(projectTemplatePath, projectPath);
|
|
146
|
+
|
|
147
|
+
const pkgJsonPath = path.join(projectPath, 'package.json');
|
|
148
|
+
const pkgJson = fs.readJsonSync(pkgJsonPath);
|
|
149
|
+
pkgJson.name = projectName.toLowerCase().replace(/\s+/g, '-');
|
|
150
|
+
// Record intended optionals as optionalDependencies on the app so reinstalls stay consistent.
|
|
151
|
+
if (optionalPackages.length > 0) {
|
|
152
|
+
pkgJson.optionalDependencies = pkgJson.optionalDependencies || {};
|
|
153
|
+
for (const pkg of optionalPackages) {
|
|
154
|
+
pkgJson.optionalDependencies[pkg] = '*';
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
fs.writeJsonSync(pkgJsonPath, pkgJson, { spaces: 2 });
|
|
158
|
+
fs.mkdirSync(path.join(projectPath, 'settings', 'ssl'), { recursive: true });
|
|
159
|
+
fs.mkdirSync(path.join(projectPath, 'backups'), { recursive: true });
|
|
160
|
+
fs.mkdirSync(path.join(projectPath, 'logs'), { recursive: true });
|
|
161
|
+
fs.mkdirSync(path.join(projectPath, 'temp'), { recursive: true });
|
|
162
|
+
|
|
163
|
+
spinner.succeed('Project files scaffolded.');
|
|
164
|
+
spinner.start('Installing `glade` admin panel...');
|
|
165
|
+
|
|
166
|
+
const gladeGinPath = path.join(templatePath, 'glade.gin');
|
|
167
|
+
const gladePackageBuffer = fs.readFileSync(gladeGinPath);
|
|
168
|
+
const gladeDestPath = path.join(projectPath, 'web', 'glade');
|
|
169
|
+
|
|
170
|
+
await _unzipBuffer(gladePackageBuffer, gladeDestPath);
|
|
171
|
+
spinner.succeed('`glade` admin panel installed.');
|
|
172
|
+
spinner.start('Configuring admin credentials...');
|
|
173
|
+
|
|
174
|
+
const passwordHash = await argon2.hash(answers.adminPass);
|
|
175
|
+
const gladeAppConfigPath = path.join(gladeDestPath, 'box', 'app.json');
|
|
176
|
+
const gladeAppConfig = fs.readJsonSync(gladeAppConfigPath);
|
|
177
|
+
gladeAppConfig.env.ADMIN_USERNAME = answers.adminUser;
|
|
178
|
+
gladeAppConfig.env.ADMIN_PASSWORD_HASH = passwordHash;
|
|
179
|
+
fs.writeJsonSync(gladeAppConfigPath, gladeAppConfig, { spaces: 2 });
|
|
180
|
+
spinner.succeed('Admin credentials configured securely.');
|
|
181
|
+
|
|
182
|
+
spinner.start('Granting default permissions to Glade...');
|
|
183
|
+
const permissionsFilePath = path.join(projectPath, 'settings', 'permissions.json');
|
|
184
|
+
const permissionsConfig = {
|
|
185
|
+
glade: {
|
|
186
|
+
granted: ['platform', 'fs']
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
fs.writeJsonSync(permissionsFilePath, permissionsConfig, { spaces: 2 });
|
|
190
|
+
spinner.succeed('Default permissions for Glade configured.');
|
|
191
|
+
|
|
192
|
+
if (answers.installDeps) {
|
|
193
|
+
installProjectDependencies(projectPath, optionalPackages, spinner, chalk);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
console.log(
|
|
197
|
+
chalk.bgGreen('\n✅ Success!'),
|
|
198
|
+
chalk.blueBright(`Your Gingee project "${projectName}" is ready.`)
|
|
199
|
+
);
|
|
200
|
+
console.log(`\nDependency profile: ${chalk.cyan(answers.depProfile)}`);
|
|
201
|
+
if (optionalPackages.length) {
|
|
202
|
+
console.log(
|
|
203
|
+
chalk.blueBright('Optional packages:'),
|
|
204
|
+
optionalPackages.join(', ')
|
|
205
|
+
);
|
|
206
|
+
} else {
|
|
207
|
+
console.log(
|
|
208
|
+
chalk.blueBright(
|
|
209
|
+
'Optional packages: none (SQLite, console email, and mock AI work without extras; image needs sharp).'
|
|
210
|
+
)
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
console.log(`\nTo get started, run the following commands:\n`);
|
|
215
|
+
console.log(chalk.blueBright(` cd ${projectName}`));
|
|
216
|
+
if (!answers.installDeps) {
|
|
217
|
+
console.log(chalk.blueBright(' npm install --omit=optional'));
|
|
218
|
+
if (optionalPackages.length) {
|
|
219
|
+
console.log(chalk.blueBright(` npm install ${optionalPackages.join(' ')}`));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
console.log(chalk.blueBright(' npm run start'));
|
|
223
|
+
|
|
224
|
+
console.log(
|
|
225
|
+
`\nAdd more features later with e.g. ${chalk.cyan('npm install sharp pg pdfmake')}`
|
|
226
|
+
);
|
|
227
|
+
console.log('(Missing optionals fail at use-time with FEATURE_NOT_INSTALLED.)\n');
|
|
228
|
+
|
|
229
|
+
console.log(`\nFor production, you have two options:`);
|
|
230
|
+
console.log(chalk.cyan(' 1. Native Service: sudo gingee-cli service install'));
|
|
231
|
+
console.log(chalk.cyan(' 2. PM2: pm2 start'));
|
|
232
|
+
console.log(` (Customize your PM2 deployment in ecosystem.config.js)`);
|
|
233
|
+
} catch (err) {
|
|
234
|
+
spinner.fail(chalk.bgRed('ERROR!: '));
|
|
235
|
+
if (err.errors) {
|
|
236
|
+
const messages = err.errors.map((e) => e.message).join('\n');
|
|
237
|
+
console.error(chalk.bgRed(`Error: `), chalk.blueBright(`${messages}`));
|
|
238
|
+
} else {
|
|
239
|
+
console.error(chalk.bgRed(`Error: `), chalk.blueBright(`${err.message}`));
|
|
240
|
+
}
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
module.exports = { init, installProjectDependencies };
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional feature packages for Gingee (keep in sync with gingee package.json
|
|
3
|
+
* optionalDependencies and docs/server-config.md → Optional npm feature packages).
|
|
4
|
+
* Used by `gingee-cli init` so new projects can install a slim core, then add only what they need.
|
|
5
|
+
*
|
|
6
|
+
* @type {Record<string, { name: string, packages: string[], description: string }>}
|
|
7
|
+
*/
|
|
8
|
+
const FEATURE_OPTIONS = {
|
|
9
|
+
image: {
|
|
10
|
+
name: 'Image processing (sharp)',
|
|
11
|
+
packages: ['sharp'],
|
|
12
|
+
description: "require('image') — resize, filters, format conversion"
|
|
13
|
+
},
|
|
14
|
+
postgres: {
|
|
15
|
+
name: 'PostgreSQL driver (pg)',
|
|
16
|
+
packages: ['pg'],
|
|
17
|
+
description: 'app.json db type "postgres"'
|
|
18
|
+
},
|
|
19
|
+
mysql: {
|
|
20
|
+
name: 'MySQL / MariaDB driver (mysql2)',
|
|
21
|
+
packages: ['mysql2'],
|
|
22
|
+
description: 'app.json db type "mysql"'
|
|
23
|
+
},
|
|
24
|
+
mssql: {
|
|
25
|
+
name: 'Microsoft SQL Server driver (mssql)',
|
|
26
|
+
packages: ['mssql'],
|
|
27
|
+
description: 'app.json db type "mssql"'
|
|
28
|
+
},
|
|
29
|
+
oracle: {
|
|
30
|
+
name: 'Oracle driver (oracledb)',
|
|
31
|
+
packages: ['oracledb'],
|
|
32
|
+
description: 'app.json db type "oracle" (native client often required)'
|
|
33
|
+
},
|
|
34
|
+
charts: {
|
|
35
|
+
name: 'Charts & canvas (chartjs-node-canvas, canvas)',
|
|
36
|
+
packages: ['chartjs-node-canvas', 'canvas'],
|
|
37
|
+
description: 'chart / dashboard / barcode canvas rendering'
|
|
38
|
+
},
|
|
39
|
+
pdf: {
|
|
40
|
+
name: 'PDF generation (pdfmake)',
|
|
41
|
+
packages: ['pdfmake'],
|
|
42
|
+
description: 'pdf module'
|
|
43
|
+
},
|
|
44
|
+
sendgrid: {
|
|
45
|
+
name: 'SendGrid email (@sendgrid/mail)',
|
|
46
|
+
packages: ['@sendgrid/mail'],
|
|
47
|
+
description: 'email type "sendgrid" (console works without this)'
|
|
48
|
+
},
|
|
49
|
+
gemini: {
|
|
50
|
+
name: 'Google Gemini AI (@google/generative-ai)',
|
|
51
|
+
packages: ['@google/generative-ai'],
|
|
52
|
+
description: 'ai type "gemini" (mock works without this)'
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** Recommended developer set (Joy without Oracle / all SQL engines). */
|
|
57
|
+
const RECOMMENDED_FEATURES = ['image', 'postgres', 'charts', 'pdf', 'sendgrid', 'gemini'];
|
|
58
|
+
|
|
59
|
+
/** All optional feature keys. */
|
|
60
|
+
const ALL_FEATURES = Object.keys(FEATURE_OPTIONS);
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @param {string[]} featureKeys
|
|
64
|
+
* @returns {string[]} unique npm package names
|
|
65
|
+
*/
|
|
66
|
+
function packagesForFeatures(featureKeys) {
|
|
67
|
+
const set = new Set();
|
|
68
|
+
for (const key of featureKeys || []) {
|
|
69
|
+
const feat = FEATURE_OPTIONS[key];
|
|
70
|
+
if (!feat) continue;
|
|
71
|
+
for (const pkg of feat.packages) set.add(pkg);
|
|
72
|
+
}
|
|
73
|
+
return [...set];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @param {'minimal'|'recommended'|'full'|'custom'} profile
|
|
78
|
+
* @param {string[]} [customKeys]
|
|
79
|
+
* @returns {string[]}
|
|
80
|
+
*/
|
|
81
|
+
function resolveFeatureKeys(profile, customKeys = []) {
|
|
82
|
+
switch (profile) {
|
|
83
|
+
case 'minimal':
|
|
84
|
+
return [];
|
|
85
|
+
case 'recommended':
|
|
86
|
+
return [...RECOMMENDED_FEATURES];
|
|
87
|
+
case 'full':
|
|
88
|
+
return [...ALL_FEATURES];
|
|
89
|
+
case 'custom':
|
|
90
|
+
return Array.isArray(customKeys) ? customKeys.filter((k) => FEATURE_OPTIONS[k]) : [];
|
|
91
|
+
default:
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Inquirer choices for the custom checkbox.
|
|
98
|
+
* @returns {Array<{ name: string, value: string, checked: boolean }>}
|
|
99
|
+
*/
|
|
100
|
+
function checkboxChoices() {
|
|
101
|
+
return ALL_FEATURES.map((key) => ({
|
|
102
|
+
name: `${FEATURE_OPTIONS[key].name} — ${FEATURE_OPTIONS[key].description}`,
|
|
103
|
+
value: key,
|
|
104
|
+
checked: RECOMMENDED_FEATURES.includes(key)
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
module.exports = {
|
|
109
|
+
FEATURE_OPTIONS,
|
|
110
|
+
RECOMMENDED_FEATURES,
|
|
111
|
+
ALL_FEATURES,
|
|
112
|
+
packagesForFeatures,
|
|
113
|
+
resolveFeatureKeys,
|
|
114
|
+
checkboxChoices
|
|
115
|
+
};
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gingee-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "The Gingee Command Line Interface (CLI), official command line tool for creating and managing Gingee projects.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"gingee-cli": "index.js"
|
|
7
|
+
"gingee-cli": "./index.js"
|
|
8
8
|
},
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
|
-
"url": "
|
|
11
|
+
"url": "https://github.com/gingerhome/gingee-cli.git"
|
|
12
12
|
},
|
|
13
13
|
"bugs": {
|
|
14
14
|
"url": "https://github.com/gingerhome/gingee-cli/issues"
|
package/templates/glade.gin
CHANGED
|
Binary file
|
|
@@ -1,34 +1,157 @@
|
|
|
1
|
-
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
1
|
+
{
|
|
2
|
+
"server": {
|
|
3
|
+
"https": {
|
|
4
|
+
"enabled": false,
|
|
5
|
+
"port": 7443,
|
|
6
|
+
"key_file": "settings/ssl/key.pem",
|
|
7
|
+
"cert_file": "settings/ssl/cert.pem"
|
|
8
|
+
},
|
|
9
|
+
"http": {
|
|
10
|
+
"enabled": true,
|
|
11
|
+
"port": 7070
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"content_encoding": {
|
|
15
|
+
"enabled": true
|
|
16
|
+
},
|
|
17
|
+
"box": {
|
|
18
|
+
"allowed_modules": [],
|
|
19
|
+
"local_modules": [],
|
|
20
|
+
"allow_dynamic_code": false
|
|
21
|
+
},
|
|
22
|
+
"web_root": "./web",
|
|
23
|
+
"cache": {
|
|
24
|
+
"provider": "memory",
|
|
25
|
+
"prefix": "gingee:",
|
|
26
|
+
"redis": {
|
|
27
|
+
"url": null,
|
|
28
|
+
"host": "127.0.0.1",
|
|
29
|
+
"port": 6379,
|
|
30
|
+
"password": null,
|
|
31
|
+
"db": 0
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"default_app": "glade",
|
|
35
|
+
"privileged_apps": [
|
|
36
|
+
"glide",
|
|
37
|
+
"glade"
|
|
38
|
+
],
|
|
39
|
+
"logging": {
|
|
40
|
+
"level": "error",
|
|
41
|
+
"rotation": {
|
|
42
|
+
"period_days": 7,
|
|
43
|
+
"max_size_mb": 50
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"scheduler": {
|
|
47
|
+
"enabled": false,
|
|
48
|
+
"timezone": "UTC",
|
|
49
|
+
"coordination": {
|
|
50
|
+
"driver": "none",
|
|
51
|
+
"strategy": "tick",
|
|
52
|
+
"lock_ttl_ms": 300000
|
|
53
|
+
},
|
|
54
|
+
"redis": {
|
|
55
|
+
"url": null,
|
|
56
|
+
"host": "127.0.0.1",
|
|
57
|
+
"port": 6379,
|
|
58
|
+
"password": null,
|
|
59
|
+
"db": 0,
|
|
60
|
+
"key_prefix": "gingee:scheduler:"
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"limits": {
|
|
64
|
+
"request_timeout_ms": 30000,
|
|
65
|
+
"request_timeout_stream_ms": 300000,
|
|
66
|
+
"stream_idle_timeout_ms": 60000,
|
|
67
|
+
"outbound_timeout_ms": 15000,
|
|
68
|
+
"max_concurrent_requests": 100,
|
|
69
|
+
"max_concurrent_requests_per_app": 25,
|
|
70
|
+
"max_concurrent_outbound": 50
|
|
71
|
+
},
|
|
72
|
+
"egress": {
|
|
73
|
+
"mode": "protected",
|
|
74
|
+
"https_only": false,
|
|
75
|
+
"dns_check": true,
|
|
76
|
+
"max_redirects": 3,
|
|
77
|
+
"block_private": true,
|
|
78
|
+
"block_loopback": true,
|
|
79
|
+
"block_link_local": true,
|
|
80
|
+
"block_metadata": true,
|
|
81
|
+
"allow_hosts": [],
|
|
82
|
+
"allow_cidrs": [],
|
|
83
|
+
"deny_hosts": [],
|
|
84
|
+
"deny_cidrs": []
|
|
85
|
+
},
|
|
86
|
+
"secrets": {
|
|
87
|
+
"load_dotenv": false,
|
|
88
|
+
"required": true,
|
|
89
|
+
"file_roots": [
|
|
90
|
+
"./settings/secrets",
|
|
91
|
+
"/run/secrets"
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
"metrics": {
|
|
95
|
+
"enabled": true,
|
|
96
|
+
"path": "/metrics",
|
|
97
|
+
"allow_from": [
|
|
98
|
+
"127.0.0.1",
|
|
99
|
+
"::1",
|
|
100
|
+
"::ffff:127.0.0.1"
|
|
101
|
+
],
|
|
102
|
+
"bearer_token": null
|
|
103
|
+
},
|
|
104
|
+
"audit": {
|
|
105
|
+
"enabled": true,
|
|
106
|
+
"path": "./logs/audit.jsonl"
|
|
107
|
+
},
|
|
108
|
+
"isolation": {
|
|
109
|
+
"mode": "off",
|
|
110
|
+
"default": "inprocess",
|
|
111
|
+
"apps": [],
|
|
112
|
+
"groups": {},
|
|
113
|
+
"worker_ready_timeout_ms": 15000,
|
|
114
|
+
"request_timeout_ms": 120000,
|
|
115
|
+
"auto_restart": true,
|
|
116
|
+
"restart_max": 10,
|
|
117
|
+
"restart_delay_ms": 500,
|
|
118
|
+
"restart_backoff_max_ms": 30000,
|
|
119
|
+
"restart_stable_ms": 60000
|
|
120
|
+
},
|
|
121
|
+
"websockets": {
|
|
122
|
+
"enabled": true,
|
|
123
|
+
"max_connections": 10000,
|
|
124
|
+
"max_connections_per_app": 2000,
|
|
125
|
+
"max_message_bytes": 65536,
|
|
126
|
+
"idle_timeout_ms": 300000,
|
|
127
|
+
"heartbeat_ms": 30000,
|
|
128
|
+
"default_path": "/ws",
|
|
129
|
+
"fanout": {
|
|
130
|
+
"driver": "none"
|
|
131
|
+
},
|
|
132
|
+
"redis": {
|
|
133
|
+
"url": null,
|
|
134
|
+
"host": "127.0.0.1",
|
|
135
|
+
"port": 6379,
|
|
136
|
+
"password": null,
|
|
137
|
+
"db": 0,
|
|
138
|
+
"key_prefix": "gingee:ws:"
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
"queue": {
|
|
142
|
+
"enabled": true,
|
|
143
|
+
"driver": "memory",
|
|
144
|
+
"concurrency": 5,
|
|
145
|
+
"default_attempts": 3,
|
|
146
|
+
"default_backoff_ms": 1000,
|
|
147
|
+
"jobs_dir": "jobs",
|
|
148
|
+
"redis": {
|
|
149
|
+
"url": null,
|
|
150
|
+
"host": "127.0.0.1",
|
|
151
|
+
"port": 6379,
|
|
152
|
+
"password": null,
|
|
153
|
+
"db": 0,
|
|
154
|
+
"key_prefix": "gingee:queue:"
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|