create-sip 1.1.3 → 1.2.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 CHANGED
@@ -113,17 +113,22 @@ Command:
113
113
  node op conf:generate
114
114
  ```
115
115
 
116
- ### Seed database
116
+ ### Database import
117
117
 
118
118
  Load data from JSON or CSV file.
119
119
 
120
120
  Examples:
121
121
 
122
122
  ```bash
123
- node op db:seed thing somethings.json
124
- node op db:seed thing somethings.csv
123
+ node op db:import thing somethings.json
124
+ node op db:import thing somethings.csv
125
+ node op db:import thing somethings.csv :
126
+ node op db:import thing somethings.csv ";"
127
+ node op db:import thing somethings.csv ,
125
128
  ```
126
129
 
130
+ The last option is the separator.
131
+
127
132
  For example JSON file:
128
133
 
129
134
  employees.json:
@@ -138,7 +143,7 @@ employees.json:
138
143
  The default separator is comma.
139
144
 
140
145
  ```cmd
141
- node op db:seed employee employees.json
146
+ node op db:import employee employees.json
142
147
  ```
143
148
 
144
149
  For example CSV file:
@@ -160,7 +165,7 @@ id:name
160
165
  ```
161
166
 
162
167
  ```cmd
163
- node op db:seed employee employees.csv --sep :
168
+ node op db:import employee employees.csv --sep :
164
169
  ```
165
170
 
166
171
  If the file has semicolon separator, use sep parameter, for example:
@@ -174,5 +179,34 @@ id;name
174
179
  Use next command:
175
180
 
176
181
  ```cmd
177
- node op db:seed employee employees.csv --sep ";"
182
+ node op db:import employee employees.csv --sep ";"
183
+ ```
184
+
185
+ ## Migration
186
+
187
+ Generate migration:
188
+
189
+ ```bash
190
+ node op make:migration <name>
191
+ ```
192
+
193
+ Run migration:
194
+
195
+ ```bash
196
+ node op migration:run
178
197
  ```
198
+
199
+ Reset database:
200
+
201
+ ```bash
202
+ node op migration:reset
203
+ ```
204
+
205
+ Rollback migrations:
206
+
207
+ ```bash
208
+ node op migration:rollback
209
+ node op migration:rollback --step 2
210
+ ```
211
+
212
+ The steop option is the number of migrations to rollback.
@@ -89,12 +89,12 @@ The next command generates the default config file:
89
89
  node op conf:generate
90
90
  ```
91
91
 
92
- ## Database seed
92
+ ## Database import
93
93
 
94
94
  The database can be seeded with the following command:
95
95
 
96
96
  ```bash
97
- node op db:seed <model_name> <file_path>
97
+ node op db:import <model_name> <file_path>
98
98
  ```
99
99
 
100
100
  The model name must be given in the singular and lowercase. The file extension must be:
@@ -106,6 +106,50 @@ The keys in the JSON file and the field names in the CSV file must match the mod
106
106
 
107
107
  If the CSV file contains quotation marks, they are automatically removed.
108
108
 
109
+ ## Migration
110
+
111
+ Generate a migration:
112
+
113
+ ```bash
114
+ node op make/migration thing
115
+ ```
116
+
117
+ Run all migration:
118
+
119
+ ```bash
120
+ node op migration:run
121
+ ```
122
+
123
+ Run a migration:
124
+
125
+ ```bash
126
+ node op migration:run <migration_name>
127
+ ```
128
+
129
+ Rollback a migration:
130
+
131
+ ```bash
132
+ node op migration:rollback
133
+ ```
134
+
135
+ Rollback two migrations:
136
+
137
+ ```bash
138
+ node op migration:rollback 2
139
+ ```
140
+
141
+ Reset the database:
142
+
143
+ ```bash
144
+ node op migration:reset
145
+ ```
146
+
147
+ Reset the database and run all migrations:
148
+
149
+ ```bash
150
+ node op migration:fresh
151
+ ```
152
+
109
153
  ## Licence
110
154
 
111
155
  May be freely distributed under the MIT license.
@@ -62,3 +62,36 @@ Send the bearer token.
62
62
  node op create model thing
63
63
  node op create controller thing
64
64
  ```
65
+
66
+ ## Key generation
67
+
68
+ ```cmd
69
+ node op key:generate
70
+ ```
71
+
72
+ ## Generate admin user
73
+
74
+ ```cmd
75
+ node op admin:generate
76
+ ```
77
+
78
+ ## Database import
79
+
80
+ ```cmd
81
+ node op db:import thing things.json
82
+ node op db:import thing things.csv
83
+ node op db:import thing things.csv ,
84
+ node op db:import thing things.csv ";"
85
+ node op db:import thing things.csv :
86
+ ```
87
+
88
+ ## Migration
89
+
90
+ ```cmd
91
+ node op make:migration thing
92
+ node op migrate:run
93
+ node op migrate:fresh
94
+ node op migrate:rollback
95
+ node op migrate:rollback --step 2
96
+ node op migrate:reset
97
+ ```
package/expressapi/op CHANGED
@@ -1,3 +1,5 @@
1
+ import yargs from 'yargs';
2
+ import { hideBin } from 'yargs/helpers';
1
3
  import replace from 'replace';
2
4
  import fse from 'fs-extra';
3
5
  import fsp from 'fs/promises';
@@ -6,78 +8,162 @@ import bcrypt from 'bcryptjs';
6
8
  import path from 'path';
7
9
  import { read } from 'read';
8
10
 
9
- if(process.argv.length == 3 && process.argv[2] == 'help') {
10
- console.log('Usable commands:');
11
- console.log(' node op make:model <name>');
12
- console.log(' node op make:controller <name>');
13
- console.log(' node op key:generate');
14
- console.log(' node op conf:generate');
15
- console.log(' node op admin:generate');
16
- console.log(' node op db:seed <model> <filePath> [sep]');
17
- process.exit(0);
18
- } else if(process.argv.length == 3 && process.argv[2] == 'key:generate') {
19
- startGenerateKey();
20
- } else if(process.argv.length == 3 && process.argv[2] == 'conf:generate') {
21
- startGenerateConf();
22
- } else if(process.argv.length == 3 && process.argv[2] == 'admin:generate') {
23
- startGenerateAdmin();
24
- } else if(process.argv[2] === 'db:seed') {
25
- if(process.argv.length < 5) {
26
- console.log('Usage:');
27
- console.log('node op db:seed <model> <filePath> [sep]');
28
- console.log('Examples:');
29
- console.log('node op db:seed thing somethings.json');
30
- console.log('node op db:seed thing somethings.csv');
31
- console.log('node op db:seed thing somethings.csv ,');
32
- console.log('node op db:seed thing somethings.csv :');
33
- console.log('node op db:seed thing somethings.csv ";"');
11
+ const { argv } = yargs(hideBin(process.argv))
12
+ .version()
13
+ .usage('Használat: node op <command> [name]')
14
+ .help()
15
+ .demandCommand(1, 'Not enough arguments!')
16
+ .command('make:model <name>',
17
+ 'Generates a new Sequelize model',
18
+ (yargs) => {
19
+ return yargs
20
+ .positional('name', {
21
+ type: 'string',
22
+ description: 'Name of the model'
23
+ })
24
+ },
25
+ async (argv) => {
26
+ const { name } = argv;
27
+ await copyModel(name);
34
28
  }
35
- const model = process.argv[3];
36
- const filePath = process.argv[4];
37
- if(process.argv.length == 6) {
38
- const sep = process.argv[5];
39
- runSeed(model, filePath, sep);
40
- }else {
41
- runSeed(model, filePath);
29
+ )
30
+ .command('make:controller <name>',
31
+ 'Generates a new controller',
32
+ (yargs) => {
33
+ return yargs
34
+ .positional('name', {
35
+ type: 'string',
36
+ description: 'Name of the controller'
37
+ })
38
+ },
39
+ async (argv) => {
40
+ const { name } = argv;
41
+ await copyController(name);
42
42
  }
43
-
44
- } else if(process.argv[2] === 'make:model') {
45
- if(process.argv.length < 4) {
46
- console.log('Usage:');
47
- console.log('node op make:model <name>');
48
- console.log('Examples:');
49
- console.log('node op make:model something');
43
+ )
44
+ .command('key:generate',
45
+ 'Generates a new API key',
46
+ (yargs) => {
47
+ return yargs
48
+ },
49
+ async (argv) => {
50
+ startGenerateKey();
50
51
  }
51
- copyModel(process.argv[3]);
52
-
53
- } else if(process.argv[2] === 'make:controller') {
54
- if(process.argv.length < 4) {
55
- console.log('Usage:');
56
- console.log('node op make:controller <name>');
57
- console.log('Examples:');
58
- console.log('node op make:controller something');
52
+ )
53
+ .command('conf:generate',
54
+ 'Generates a new config file',
55
+ (yargs) => {
56
+ return yargs
57
+ },
58
+ async (argv) => {
59
+ startGenerateConf();
59
60
  }
60
- copyController(process.argv[3]);
61
-
62
- } else if(process.argv.length < 4) {
63
- console.log('Usage:');
64
- console.log('node op <command> <type> <name>');
65
- console.log('Example:');
66
- console.log('node op create model something');
67
- console.log('node op create controller something');
68
- console.log('node op key:generate');
69
- process.exit(1);
70
- } else {
71
- if(process.argv[2] === 'create') {
72
- console.log('Create a new ' + process.argv[3] + '...');
73
- if(process.argv[3] === 'model') {
74
- copyModel(process.argv[4]);
75
- }
76
- if(process.argv[3] === 'controller') {
77
- copyController(process.argv[4]);
78
- }
61
+ )
62
+ .command('admin:generate',
63
+ 'Generates a new admin user',
64
+ (yargs) => {
65
+ return yargs
66
+ },
67
+ async (argv) => {
68
+ startGenerateAdmin();
79
69
  }
80
- }
70
+ )
71
+ .command('db:import <model> <filePath> [sep]',
72
+ `Import CSV or JSON file to database table
73
+
74
+ Examples:
75
+ node op db:import thing somethings.json
76
+ node op db:import thing somethings.csv
77
+ node op db:import thing somethings.csv ,
78
+ node op db:import thing somethings.csv :
79
+ node op db:import thing somethings.csv ";"
80
+
81
+ In CSV file the field names must match the model fields.
82
+ `,
83
+ (yargs) => {
84
+ return yargs
85
+ .positional('model', {
86
+ type: 'string',
87
+ description: 'Name of the model'
88
+ })
89
+ .positional('filePath', {
90
+ type: 'string',
91
+ description: 'Path to the file'
92
+ })
93
+ .positional('sep', {
94
+ type: 'string',
95
+ description: 'Separator for the CSV file'
96
+ })
97
+ },
98
+ async (argv) => {
99
+ const { model, filePath, sep } = argv;
100
+ await runImportData(model, filePath, sep);
101
+ }
102
+ )
103
+ .command('make:migration <name>',
104
+ 'Generates a new migration file',
105
+ (yargs) => {
106
+ return yargs
107
+ .positional('name', {
108
+ type: 'string',
109
+ description: 'Name of the migration'
110
+ })
111
+ },
112
+ async (argv) => {
113
+ const { name } = argv;
114
+ await createMigration(name);
115
+ }
116
+ )
117
+ .command('migration:run [migrationName]',
118
+ 'Runs migrations',
119
+ (yargs) => {
120
+ return yargs
121
+ .positional('migrationName', {
122
+ type: 'string',
123
+ description: 'Name of the migration'
124
+ })
125
+ },
126
+ async (argv) => {
127
+ const { migrationName } = argv;
128
+ await startMigrations(migrationName);
129
+ }
130
+ )
131
+ .command('migration:rollback',
132
+ 'Rolls back migrations',
133
+ (yargs) => {
134
+ return yargs
135
+ .options({
136
+ step: {
137
+ type: 'number',
138
+ description: 'Number of migrations to rollback',
139
+ default: 1
140
+ }
141
+ })
142
+ },
143
+ async (argv) => {
144
+ const { step } = argv;
145
+ await rollbackMigrations(step);
146
+ }
147
+ )
148
+ .command('migration:fresh',
149
+ 'Rolls back all migrations',
150
+ (yargs) => {
151
+ return yargs
152
+ },
153
+ async (argv) => {
154
+ await freshMigrations();
155
+ }
156
+ )
157
+ .command('migration:reset',
158
+ 'Rolls back all migrations',
159
+ (yargs) => {
160
+ return yargs
161
+ },
162
+ async (argv) => {
163
+ await resetMigrations();
164
+ }
165
+ )
166
+ .parse();
81
167
 
82
168
  async function copyController(className) {
83
169
  const lowerName = className.toLowerCase()
@@ -232,17 +318,17 @@ async function startGenerateAdmin() {
232
318
  }
233
319
  }
234
320
 
235
- const seedFromJson = async (model, filePath) => {
321
+ const importFromJson = async (model, filePath) => {
236
322
  try {
237
323
  const data = JSON.parse(await fsp.readFile(filePath, 'utf8'))
238
324
  await model.bulkCreate(data)
239
- console.log(`Data seeded successfully! ${model.name}`)
325
+ console.log(`Data imported successfully! ${model.name}`)
240
326
  } catch (error) {
241
327
  console.error(error)
242
328
  }
243
329
  }
244
330
 
245
- const seedFromCsv = async (model, filePath, sep) => {
331
+ const importFromCsv = async (model, filePath, sep) => {
246
332
  try {
247
333
  const data = await fsp.readFile(filePath, 'utf8')
248
334
  const clearData = data.replace(/"/g, '').trim()
@@ -261,16 +347,16 @@ const seedFromCsv = async (model, filePath, sep) => {
261
347
  })
262
348
 
263
349
  await model.bulkCreate(dataToInsert)
264
- console.log(`Data seeded successfully! ${model.name}`)
350
+ console.log(`Data imported successfully! ${model.name}`)
265
351
  } catch (error) {
266
352
  console.error(error)
267
353
  }
268
354
  }
269
355
 
270
- async function runSeed(model, filePath, sep=',') {
356
+ async function runImportData(model, filePath, sep=',') {
271
357
 
272
358
  if(!filePath || !model) {
273
- console.log('Usage: node seed.js <modelName> <filePath> [sep]')
359
+ console.log('Usage: node db:import <modelName> <filePath> [sep]')
274
360
  process.exit(1)
275
361
  }
276
362
 
@@ -306,12 +392,107 @@ async function runSeed(model, filePath, sep=',') {
306
392
  await sequelize.sync({ force: true })
307
393
  await sequelize.authenticate()
308
394
  if(ext === '.csv') {
309
- await seedFromCsv(modelObject, filePath, sep)
395
+ await importFromCsv(modelObject, filePath, sep)
310
396
  }else {
311
- await seedFromJson(modelObject, filePath)
397
+ await importFromJson(modelObject, filePath)
312
398
  }
313
399
  } catch (error) {
314
400
  console.error(error)
315
401
  }
316
402
 
317
403
  }
404
+
405
+ async function createMigration(name) {
406
+ console.log('Create a new migration...', name)
407
+
408
+ const lowerName = name.toLowerCase()
409
+ const date = new Date()
410
+ const year = date.getFullYear()
411
+ const month = String(date.getMonth() + 1).padStart(2, '0')
412
+ const day = String(date.getDate()).padStart(2, '0')
413
+ const hours = String(date.getHours()).padStart(2, '0')
414
+ const minutes = String(date.getMinutes()).padStart(2, '0')
415
+ const seconds = String(date.getSeconds()).padStart(2, '0')
416
+ const timestamp = `${year}_${month}_${day}_${hours}${minutes}${seconds}`
417
+
418
+ const migrationName = `${timestamp}_${lowerName}`
419
+
420
+ const src = 'templates/migrationTemplate.js'
421
+ const dest = `database/migrations/${migrationName}.js`
422
+
423
+ if(await startCheckIfFileExists(dest)) {
424
+ process.exit(1);
425
+ }
426
+
427
+ await fse.copy(src, dest)
428
+
429
+ replace({
430
+ regex: 'thing',
431
+ replacement: name,
432
+ paths: [dest]
433
+ })
434
+
435
+ }
436
+
437
+ async function getUmzug() {
438
+ const { default: sequelize } = await import('./app/database/database.js')
439
+ const { Umzug, SequelizeStorage } = await import('umzug')
440
+
441
+ const umzug = new Umzug({
442
+ migrations: { glob: './database/migrations/*.js' },
443
+ context: sequelize.getQueryInterface(),
444
+ storage: new SequelizeStorage({ sequelize }),
445
+ logger: console
446
+ })
447
+
448
+ return umzug
449
+ }
450
+
451
+ async function startMigrations(name) {
452
+ if(name) {
453
+ await runOneMigration(name)
454
+ } else {
455
+ await runMigrations()
456
+ }
457
+ }
458
+
459
+ async function runOneMigration(name) {
460
+ console.log('Run one migration...', name)
461
+
462
+ if(!name.endsWith('.js')) {
463
+ name += '.js'
464
+ }
465
+
466
+ const migrationPath = `database/migrations/${name}`
467
+ if(!await startCheckIfFileExists(migrationPath)) {
468
+ console.log(`The migration file ${migrationPath} not already exists.`)
469
+ process.exit(1)
470
+ }
471
+
472
+ const umzug = await getUmzug()
473
+ await umzug.up()
474
+ }
475
+
476
+ async function runMigrations() {
477
+ console.log('Run migrations...')
478
+ const umzug = await getUmzug()
479
+ await umzug.up()
480
+ }
481
+
482
+ async function resetMigrations() {
483
+ console.log('Reset migrations...')
484
+ const umzug = await getUmzug()
485
+ await umzug.down({ to: 0 })
486
+ }
487
+
488
+ async function freshMigrations() {
489
+ console.log('Fresh migrations...')
490
+ await resetMigrations()
491
+ await runMigrations()
492
+ }
493
+
494
+ async function rollbackMigrations(step = 1) {
495
+ console.log('Rollback migrations...')
496
+ const umzug = await getUmzug()
497
+ await umzug.down({ step: step })
498
+ }
@@ -5,14 +5,14 @@
5
5
  "main": "index.js",
6
6
  "scripts": {
7
7
  "test": "mocha",
8
- "dev": "nodemon app",
8
+ "dev": "nodemon app --watch app --watch config",
9
9
  "start": "node app"
10
10
  },
11
11
  "keywords": [
12
12
  "express",
13
13
  "api"
14
14
  ],
15
- "author": "Sallai András",
15
+ "author": "your name",
16
16
  "license": "ISC",
17
17
  "dependencies": {
18
18
  "bcryptjs": "^2.4.3",
@@ -25,10 +25,13 @@
25
25
  "read": "^4.1.0",
26
26
  "replace": "^1.2.2",
27
27
  "sequelize": "^6.32.0",
28
- "sqlite3": "^5.1.6"
28
+ "sqlite3": "^5.1.6",
29
+ "umzug": "^3.8.2",
30
+ "yargs": "^18.0.0"
29
31
  },
30
32
  "devDependencies": {
31
- "fs-extra": "^11.3.0",
33
+ "@types/bcryptjs": "^2.4.6",
34
+ "fs-extra": "^11.3.0",
32
35
  "nodemon": "^2.0.22",
33
36
  "supertest": "^6.3.3"
34
37
  },
@@ -0,0 +1,21 @@
1
+ import { DataTypes } from 'sequelize';
2
+
3
+ async function up({context: QueryInterface}) {
4
+ await QueryInterface.createTable('things', {
5
+ id: {
6
+ allowNull: false,
7
+ autoIncrement: true,
8
+ primaryKey: true,
9
+ type: DataTypes.INTEGER
10
+ },
11
+ name: {
12
+ type: DataTypes.STRING
13
+ }
14
+ });
15
+ }
16
+
17
+ async function down({context: QueryInterface}) {
18
+ await QueryInterface.dropTable('things');
19
+ }
20
+
21
+ export { up, down }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-sip",
3
- "version": "1.1.3",
3
+ "version": "1.2.1",
4
4
  "main": "index.js",
5
5
  "bin": {
6
6
  "create-sip": "create-sip.js"
@@ -1,15 +0,0 @@
1
- {
2
- "app": {
3
- "port": 8000,
4
- "key": "",
5
- "log": "console.log"
6
- },
7
- "db": {
8
- "dialect": "sqlite",
9
- "host": "127.0.0.1",
10
- "name": "",
11
- "user": "",
12
- "pass": "",
13
- "path": ":memory:"
14
- }
15
- }