@platformatic/db 2.74.3 → 3.0.0-alpha.2
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/config.d.ts +2 -30
- package/eslint.config.js +9 -5
- package/index.d.ts +56 -31
- package/index.js +67 -128
- package/lib/application.js +102 -0
- package/lib/commands/index.js +59 -0
- package/lib/commands/migrations-apply.js +62 -0
- package/lib/commands/migrations-create.js +48 -0
- package/lib/commands/print-schema.js +30 -0
- package/lib/commands/seed.js +72 -0
- package/lib/commands/types.js +21 -0
- package/lib/errors.js +15 -11
- package/lib/generator.js +229 -0
- package/lib/{migrator.mjs → migrator.js} +44 -37
- package/lib/{root-endpoint/index.js → root.js} +6 -7
- package/lib/schema.js +38 -22
- package/lib/stackable.js +14 -26
- package/lib/{generator/code-templates.js → templates.js} +57 -16
- package/lib/types.js +160 -0
- package/lib/upgrade.js +8 -12
- package/lib/utils.js +12 -23
- package/lib/versions/0.18.0.js +3 -5
- package/lib/versions/{from-zero-twenty-height-to-will-see.js → 0.28.0.js} +3 -5
- package/lib/versions/2.0.0.js +3 -5
- package/lib/versions/3.0.0.js +14 -0
- package/package.json +20 -28
- package/schema.json +9 -154
- package/tsconfig.json +16 -6
- package/.snapshots/810d795d512560f3863d8db472c81c27/0.json +0 -1
- package/.snapshots/810d795d512560f3863d8db472c81c27/1.json +0 -1
- package/db.mjs +0 -86
- package/help/compile.txt +0 -17
- package/help/create.txt +0 -13
- package/help/help.txt +0 -11
- package/help/migrations apply.txt +0 -45
- package/help/migrations create.txt +0 -27
- package/help/migrations.txt +0 -4
- package/help/schema.txt +0 -25
- package/help/seed.txt +0 -36
- package/help/start.txt +0 -47
- package/help/types.txt +0 -40
- package/index.test-d.ts +0 -43
- package/lib/adjust-config.js +0 -42
- package/lib/create.mjs +0 -89
- package/lib/gen-migration.mjs +0 -53
- package/lib/gen-schema.mjs +0 -68
- package/lib/gen-types.mjs +0 -202
- package/lib/generator/README.md +0 -38
- package/lib/generator/db-generator.js +0 -262
- package/lib/generator.d.ts +0 -7
- package/lib/migrate.mjs +0 -87
- package/lib/seed.mjs +0 -90
- /package/{lib/root-endpoint/public → public}/images/dark_mode.svg +0 -0
- /package/{lib/root-endpoint/public → public}/images/favicon.ico +0 -0
- /package/{lib/root-endpoint/public → public}/images/light_mode.svg +0 -0
- /package/{lib/root-endpoint/public → public}/images/platformatic-logo-dark.svg +0 -0
- /package/{lib/root-endpoint/public → public}/images/platformatic-logo-light.svg +0 -0
- /package/{lib/root-endpoint/public → public}/images/triangle_dark.svg +0 -0
- /package/{lib/root-endpoint/public → public}/images/triangle_light.svg +0 -0
- /package/{lib/root-endpoint/public → public}/index.html +0 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { abstractLogger, kMetadata, loadConfiguration } from '@platformatic/foundation'
|
|
2
|
+
import { printSchema as printGraphqlSchema } from 'graphql'
|
|
3
|
+
import { create } from '../../index.js'
|
|
4
|
+
import { schema } from '../schema.js'
|
|
5
|
+
|
|
6
|
+
export async function printSchema (logger, configFile, args, { colorette: { bold }, logFatalError }) {
|
|
7
|
+
const config = await loadConfiguration(configFile, schema)
|
|
8
|
+
|
|
9
|
+
const type = args[0]
|
|
10
|
+
|
|
11
|
+
if (!type) {
|
|
12
|
+
logFatalError(logger, `Please specify a schema type between ${bold('openapi')} and ${bold('graphql')}.`)
|
|
13
|
+
} else if (type !== 'openapi' && type !== 'graphql') {
|
|
14
|
+
logFatalError(logger, `Invalid schema type ${bold(type)}. Use ${bold('openapi')} or ${bold('graphql')}.`)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const app = await create(config[kMetadata].root, configFile, { logger: abstractLogger })
|
|
18
|
+
await app.init()
|
|
19
|
+
|
|
20
|
+
let output
|
|
21
|
+
if (type === 'openapi') {
|
|
22
|
+
await app.start({ listen: true })
|
|
23
|
+
output = JSON.stringify(app.getApplication().swagger(), null, 2)
|
|
24
|
+
} else {
|
|
25
|
+
output = printGraphqlSchema(app.getApplication().graphql.schema)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
console.log(output)
|
|
29
|
+
await app.stop()
|
|
30
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { loadConfiguration, loadModule } from '@platformatic/foundation'
|
|
2
|
+
import { access } from 'fs/promises'
|
|
3
|
+
import { createRequire } from 'node:module'
|
|
4
|
+
import { resolve } from 'node:path'
|
|
5
|
+
import { MissingSeedFileError } from '../errors.js'
|
|
6
|
+
import { Migrator } from '../migrator.js'
|
|
7
|
+
import { schema } from '../schema.js'
|
|
8
|
+
import { setupDB } from '../utils.js'
|
|
9
|
+
|
|
10
|
+
export async function seed (logger, configFile, args, { colorette: { bold }, logFatalError }) {
|
|
11
|
+
const config = await loadConfiguration(configFile, schema)
|
|
12
|
+
|
|
13
|
+
if (config.migrations !== undefined) {
|
|
14
|
+
const migrator = new Migrator(config.migrations, config.db, logger)
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const hasMigrationsToApply = await migrator.hasMigrationsToApply()
|
|
18
|
+
if (hasMigrationsToApply) {
|
|
19
|
+
logFatalError(logger, 'You must apply migrations before seeding the database.')
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
} finally {
|
|
23
|
+
await migrator.close()
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!args.length) {
|
|
28
|
+
throw new MissingSeedFileError()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const seedFile = resolve(process.cwd(), args[0])
|
|
32
|
+
await access(seedFile)
|
|
33
|
+
|
|
34
|
+
logger.info(`Seeding from ${bold(seedFile)}`)
|
|
35
|
+
|
|
36
|
+
const importedModule = await loadModule(createRequire(resolve(process.cwd(), 'noop.js')), seedFile)
|
|
37
|
+
|
|
38
|
+
const seedFunction = typeof importedModule?.seed !== 'function' ? importedModule : importedModule
|
|
39
|
+
|
|
40
|
+
if (typeof seedFunction !== 'function') {
|
|
41
|
+
logFatalError(logger, 'Cannot find seed function.')
|
|
42
|
+
logFatalError(logger, "If you use an ESM module use the signature 'export async function seed (opts)'.")
|
|
43
|
+
logFatalError(logger, "If you use a CJS module use the signature 'module.exports = async function seed (opts)'.")
|
|
44
|
+
logFatalError(logger, "If you use Typescript use the signature 'export async function seed(opts)'")
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const { db, sql, entities } = await setupDB(logger, config.db)
|
|
49
|
+
await seedFunction({ db, sql, entities, logger })
|
|
50
|
+
logger.info('Seeding complete.')
|
|
51
|
+
|
|
52
|
+
// Once done seeding, close your connection.
|
|
53
|
+
await db.dispose()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const helpFooter = `
|
|
57
|
+
This is a convenience method that loads a JavaScript file and configure @platformatic/sql-mapper to connect to the database specified in the configuration file.
|
|
58
|
+
|
|
59
|
+
Here is an example of a seed file:
|
|
60
|
+
|
|
61
|
+
\`\`\`
|
|
62
|
+
'use strict'
|
|
63
|
+
|
|
64
|
+
module.exports = async function ({ entities, db, sql }) {
|
|
65
|
+
await entities.graph.save({ input: { name: 'Hello' } })
|
|
66
|
+
await db.query(sql\`INSERT INTO graphs (name) VALUES ('Hello 2');\`)
|
|
67
|
+
}
|
|
68
|
+
\`\`\`
|
|
69
|
+
|
|
70
|
+
You can find more details about the configuration format here:
|
|
71
|
+
* [Platformatic DB Configuration](https://docs.platformatic.dev/docs/db/configuration)
|
|
72
|
+
`
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { loadConfiguration } from '@platformatic/foundation'
|
|
2
|
+
import { schema } from '../schema.js'
|
|
3
|
+
import { execute } from '../types.js'
|
|
4
|
+
|
|
5
|
+
export async function generateTypes (logger, configFile, _args) {
|
|
6
|
+
const config = await loadConfiguration(configFile, schema)
|
|
7
|
+
|
|
8
|
+
const count = await execute({ logger, config })
|
|
9
|
+
|
|
10
|
+
if (count === 0) {
|
|
11
|
+
logger.warn('No entities found in your schema. Types were NOT generated.')
|
|
12
|
+
logger.warn('Make sure you have applied all the migrations and try again.')
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const helpFooter = `
|
|
17
|
+
As a result of executing this command, the Platformatic DB will generate a \`types\` folder with a typescript file for each database entity. It will also generate a \`plt-env.d.ts\` file that injects the types into the Application instance.
|
|
18
|
+
|
|
19
|
+
You can find more details about the configuration format here:
|
|
20
|
+
* [Platformatic DB Configuration](https://docs.platformatic.dev/docs/db/configuration)
|
|
21
|
+
`
|
package/lib/errors.js
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const createError = require('@fastify/error')
|
|
1
|
+
import createError from '@fastify/error'
|
|
4
2
|
|
|
5
3
|
const ERROR_PREFIX = 'PLT_DB'
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
export const MigrateMissingMigrationsError = createError(
|
|
6
|
+
`${ERROR_PREFIX}_MIGRATE_ERROR`,
|
|
7
|
+
'Missing "migrations" section in config file'
|
|
8
|
+
)
|
|
9
|
+
export const UnknownDatabaseError = createError(`${ERROR_PREFIX}_UNKNOWN_DATABASE_ERROR`, 'Unknown database')
|
|
10
|
+
export const MigrateMissingMigrationsDirError = createError(
|
|
11
|
+
`${ERROR_PREFIX}_MIGRATE_ERROR`,
|
|
12
|
+
'Migrations directory %s does not exist'
|
|
13
|
+
)
|
|
14
|
+
export const MissingSeedFileError = createError(`${ERROR_PREFIX}_MISSING_SEED_FILE_ERROR`, 'Missing seed file')
|
|
15
|
+
export const MigrationsToApplyError = createError(
|
|
16
|
+
`${ERROR_PREFIX}_MIGRATIONS_TO_APPLY_ERROR`,
|
|
17
|
+
'You have migrations to apply.'
|
|
18
|
+
)
|
package/lib/generator.js
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { Generator as ServiceGenerator } from '@platformatic/service'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
import {
|
|
4
|
+
ENVIRONMENT_TEMPLATE,
|
|
5
|
+
jsHelperMySQL,
|
|
6
|
+
jsHelperPostgres,
|
|
7
|
+
jsHelperSqlite,
|
|
8
|
+
moviesTestJS,
|
|
9
|
+
moviesTestTS,
|
|
10
|
+
README
|
|
11
|
+
} from './templates.js'
|
|
12
|
+
|
|
13
|
+
export class Generator extends ServiceGenerator {
|
|
14
|
+
constructor (opts = {}) {
|
|
15
|
+
super({
|
|
16
|
+
...opts,
|
|
17
|
+
module: '@platformatic/db'
|
|
18
|
+
})
|
|
19
|
+
this.connectionStrings = {
|
|
20
|
+
postgres: 'postgres://postgres:postgres@127.0.0.1:5432/postgres',
|
|
21
|
+
sqlite: 'sqlite://./db.sqlite',
|
|
22
|
+
mysql: 'mysql://root@127.0.0.1:3306/platformatic',
|
|
23
|
+
mariadb: 'mysql://root@127.0.0.1:3306/platformatic'
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getConfigFieldsDefinitions () {
|
|
28
|
+
return [
|
|
29
|
+
{
|
|
30
|
+
var: 'DATABASE_URL',
|
|
31
|
+
label: 'What is the connection string?',
|
|
32
|
+
default: this.connectionStrings.sqlite,
|
|
33
|
+
type: 'string',
|
|
34
|
+
configValue: 'connectionString'
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
var: 'PLT_APPLY_MIGRATIONS',
|
|
38
|
+
label: 'Should migrations be applied automatically on startup?',
|
|
39
|
+
default: true,
|
|
40
|
+
type: 'boolean'
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
getDefaultConfig () {
|
|
46
|
+
const defaultBaseConfig = super.getDefaultConfig()
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
...defaultBaseConfig,
|
|
50
|
+
database: 'sqlite',
|
|
51
|
+
connectionString: null,
|
|
52
|
+
plugin: true,
|
|
53
|
+
tests: true,
|
|
54
|
+
types: true,
|
|
55
|
+
migrations: 'migrations',
|
|
56
|
+
createMigrations: true
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async prepareQuestions () {
|
|
61
|
+
if (!this.config.connectionString) {
|
|
62
|
+
const def = this.getConfigFieldsDefinitions().find(q => q.var === 'DATABASE_URL')
|
|
63
|
+
this.questions.push({
|
|
64
|
+
type: 'input',
|
|
65
|
+
name: def.configValue,
|
|
66
|
+
message: def.label,
|
|
67
|
+
default: def.default
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this.questions.push({
|
|
72
|
+
type: 'list',
|
|
73
|
+
name: 'createMigrations',
|
|
74
|
+
message: 'Do you want to create default migrations?',
|
|
75
|
+
default: true,
|
|
76
|
+
choices: [
|
|
77
|
+
{ name: 'yes', value: true },
|
|
78
|
+
{ name: 'no', value: false }
|
|
79
|
+
]
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
await super.prepareQuestions()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
getMoviesMigrationDo () {
|
|
86
|
+
const key = {
|
|
87
|
+
postgres: 'SERIAL',
|
|
88
|
+
sqlite: 'INTEGER',
|
|
89
|
+
mysql: 'INTEGER UNSIGNED AUTO_INCREMENT',
|
|
90
|
+
mariadb: 'INTEGER UNSIGNED AUTO_INCREMENT'
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return `
|
|
94
|
+
-- Add SQL in this file to create the database tables for your API
|
|
95
|
+
CREATE TABLE IF NOT EXISTS movies (
|
|
96
|
+
id ${key[this.config.database]} PRIMARY KEY,
|
|
97
|
+
title TEXT NOT NULL
|
|
98
|
+
);
|
|
99
|
+
`
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
getMoviesMigrationUndo () {
|
|
103
|
+
return '-- Add SQL in this file to drop the database tables\nDROP TABLE movies;'
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
setConfigFields (fields) {
|
|
107
|
+
super.setConfigFields(fields)
|
|
108
|
+
this.config.database = this.getDatabaseFromConnectionString()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
getDatabaseFromConnectionString () {
|
|
112
|
+
if (this.config.connectionString) {
|
|
113
|
+
if (this.config.connectionString.indexOf('://') !== -1) {
|
|
114
|
+
const splitted = this.config.connectionString.split('://')
|
|
115
|
+
return splitted[0]
|
|
116
|
+
}
|
|
117
|
+
return null
|
|
118
|
+
}
|
|
119
|
+
return null
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async _beforePrepare () {
|
|
123
|
+
if (this.config.isUpdating) {
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
await super._beforePrepare()
|
|
128
|
+
|
|
129
|
+
this.config.connectionString = this.config.connectionString || this.connectionStrings[this.config.database]
|
|
130
|
+
this.config.dependencies = {
|
|
131
|
+
'@platformatic/db': `^${this.platformaticVersion}`
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (!this.config.isRuntimeContext) {
|
|
135
|
+
this.addEnvVars(
|
|
136
|
+
{
|
|
137
|
+
PLT_SERVER_HOSTNAME: this.config.hostname,
|
|
138
|
+
PLT_SERVER_LOGGER_LEVEL: 'info',
|
|
139
|
+
PORT: 3042
|
|
140
|
+
},
|
|
141
|
+
{ overwrite: false, default: true }
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
this.addEnvVars(
|
|
146
|
+
{
|
|
147
|
+
DATABASE_URL: this.connectionStrings[this.config.database],
|
|
148
|
+
PLT_APPLY_MIGRATIONS: 'true'
|
|
149
|
+
},
|
|
150
|
+
{ overwrite: false, default: true }
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async _afterPrepare () {
|
|
155
|
+
if (this.config.isUpdating) {
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (this.config.createMigrations) {
|
|
160
|
+
this.addFile({ path: 'migrations', file: '001.do.sql', contents: this.getMoviesMigrationDo() })
|
|
161
|
+
this.addFile({ path: 'migrations', file: '001.undo.sql', contents: this.getMoviesMigrationUndo() })
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
this.addFile({ path: '', file: 'README.md', contents: README })
|
|
165
|
+
|
|
166
|
+
if (this.config.plugin) {
|
|
167
|
+
switch (this.config.database) {
|
|
168
|
+
case 'sqlite':
|
|
169
|
+
this.testHelperCustomizations = jsHelperSqlite
|
|
170
|
+
break
|
|
171
|
+
case 'mysql':
|
|
172
|
+
this.testHelperCustomizations = jsHelperMySQL(this.config.connectionString)
|
|
173
|
+
break
|
|
174
|
+
case 'postgres':
|
|
175
|
+
this.testHelperCustomizations = jsHelperPostgres(this.config.connectionString)
|
|
176
|
+
break
|
|
177
|
+
case 'mariadb':
|
|
178
|
+
this.testHelperCustomizations = jsHelperMySQL(this.config.connectionString)
|
|
179
|
+
break
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (this.config.createMigrations) {
|
|
183
|
+
if (this.config.typescript) {
|
|
184
|
+
this.addFile({ path: join('test', 'routes'), file: 'movies.test.ts', contents: moviesTestTS })
|
|
185
|
+
} else {
|
|
186
|
+
this.addFile({ path: join('test', 'routes'), file: 'movies.test.js', contents: moviesTestJS })
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
super._afterPrepare()
|
|
192
|
+
|
|
193
|
+
this.addFile({ path: '', file: 'plt-env.d.ts', contents: ENVIRONMENT_TEMPLATE })
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async _getConfigFileContents () {
|
|
197
|
+
const config = await super._getConfigFileContents()
|
|
198
|
+
delete config.service
|
|
199
|
+
config.$schema = `https://schemas.platformatic.dev/@platformatic/db/${this.platformaticVersion}.json`
|
|
200
|
+
|
|
201
|
+
config.db = {
|
|
202
|
+
connectionString: `{${this.getEnvVarName('DATABASE_URL')}}`,
|
|
203
|
+
graphql: true,
|
|
204
|
+
openapi: true,
|
|
205
|
+
schemalock: true
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
config.watch = {
|
|
209
|
+
ignore: ['*.sqlite', '*.sqlite-journal']
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (this.config.migrations) {
|
|
213
|
+
config.migrations = {
|
|
214
|
+
dir: this.config.migrations,
|
|
215
|
+
autoApply: `{${this.getEnvVarName('PLT_APPLY_MIGRATIONS')}}`
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
this.addFile({ path: 'migrations', file: '.gitkeep', contents: '' })
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (this.config.types === true) {
|
|
222
|
+
config.types = {
|
|
223
|
+
autogenerate: true
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return config
|
|
228
|
+
}
|
|
229
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { basename } from 'path'
|
|
2
|
-
import Postgrator from 'postgrator'
|
|
3
1
|
import { createConnectionPool } from '@platformatic/sql-mapper'
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
2
|
+
import { readdir, stat } from 'node:fs/promises'
|
|
3
|
+
import { basename } from 'node:path'
|
|
4
|
+
import Postgrator from 'postgrator'
|
|
5
|
+
import { MigrateMissingMigrationsDirError, MigrateMissingMigrationsError } from './errors.js'
|
|
6
6
|
|
|
7
|
-
class Migrator {
|
|
7
|
+
export class Migrator {
|
|
8
8
|
constructor (migrationConfig, coreConfig, logger) {
|
|
9
9
|
this.coreConfig = coreConfig
|
|
10
10
|
this.migrationDir = migrationConfig.dir
|
|
@@ -28,7 +28,7 @@ class Migrator {
|
|
|
28
28
|
|
|
29
29
|
const { db, sql } = await createConnectionPool({
|
|
30
30
|
...this.coreConfig,
|
|
31
|
-
log: this.logger
|
|
31
|
+
log: this.logger
|
|
32
32
|
})
|
|
33
33
|
|
|
34
34
|
let driver
|
|
@@ -44,9 +44,7 @@ class Migrator {
|
|
|
44
44
|
driver = 'sqlite3'
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
const database = driver !== 'sqlite3'
|
|
48
|
-
? new URL(this.coreConfig.connectionString).pathname.replace(/^\//, '')
|
|
49
|
-
: ''
|
|
47
|
+
const database = driver !== 'sqlite3' ? new URL(this.coreConfig.connectionString).pathname.replace(/^\//, '') : ''
|
|
50
48
|
|
|
51
49
|
this.db = db
|
|
52
50
|
|
|
@@ -59,40 +57,31 @@ class Migrator {
|
|
|
59
57
|
driver,
|
|
60
58
|
database,
|
|
61
59
|
schemaTable: this.migrationsTable || 'versions',
|
|
62
|
-
execQuery: async
|
|
60
|
+
execQuery: async query => {
|
|
63
61
|
const res = await db.query(sql`${sql.__dangerous__rawValue(query)}`)
|
|
64
62
|
return { rows: res }
|
|
65
63
|
},
|
|
66
64
|
validateChecksums: this.validateChecksums,
|
|
67
65
|
newline: this.newline,
|
|
68
|
-
currentSchema: ['pg', 'mysql'].includes(driver) ? this.currentSchema : undefined
|
|
66
|
+
currentSchema: ['pg', 'mysql'].includes(driver) ? this.currentSchema : undefined
|
|
69
67
|
})
|
|
70
68
|
|
|
71
69
|
if (this.validateChecksums === true) {
|
|
72
|
-
this.postgrator.on(
|
|
73
|
-
|
|
74
|
-
(migration) => {
|
|
75
|
-
/* c8 ignore next 3 */
|
|
76
|
-
const migrationName = basename(migration.filename)
|
|
77
|
-
this.logger.info(`verifying checksum of migration ${migrationName}`)
|
|
78
|
-
}
|
|
79
|
-
)
|
|
80
|
-
}
|
|
81
|
-
this.postgrator.on(
|
|
82
|
-
'migration-started',
|
|
83
|
-
(migration) => {
|
|
84
|
-
const migrationName = basename(migration.filename)
|
|
85
|
-
this.logger.info(`running ${migrationName}`)
|
|
86
|
-
}
|
|
87
|
-
)
|
|
88
|
-
this.postgrator.on(
|
|
89
|
-
'migration-finished',
|
|
90
|
-
(migration) => {
|
|
91
|
-
this.appliedMigrationsCount++
|
|
70
|
+
this.postgrator.on('validation-started', migration => {
|
|
71
|
+
/* c8 ignore next 3 */
|
|
92
72
|
const migrationName = basename(migration.filename)
|
|
93
|
-
this.logger.
|
|
94
|
-
}
|
|
95
|
-
|
|
73
|
+
this.logger.info(`verifying checksum of migration ${migrationName}`)
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
this.postgrator.on('migration-started', migration => {
|
|
77
|
+
const migrationName = basename(migration.filename)
|
|
78
|
+
this.logger.info(`running ${migrationName}`)
|
|
79
|
+
})
|
|
80
|
+
this.postgrator.on('migration-finished', migration => {
|
|
81
|
+
this.appliedMigrationsCount++
|
|
82
|
+
const migrationName = basename(migration.filename)
|
|
83
|
+
this.logger.debug(`completed ${migrationName}`)
|
|
84
|
+
})
|
|
96
85
|
}
|
|
97
86
|
|
|
98
87
|
async checkMigrationsDirectoryExists () {
|
|
@@ -100,7 +89,7 @@ class Migrator {
|
|
|
100
89
|
await stat(this.migrationDir)
|
|
101
90
|
} catch (err) {
|
|
102
91
|
if (err.code === 'ENOENT') {
|
|
103
|
-
throw new
|
|
92
|
+
throw new MigrateMissingMigrationsDirError(this.migrationDir)
|
|
104
93
|
}
|
|
105
94
|
}
|
|
106
95
|
}
|
|
@@ -178,7 +167,7 @@ class Migrator {
|
|
|
178
167
|
}
|
|
179
168
|
} catch (err) {
|
|
180
169
|
if (err.code === 'ENOENT') {
|
|
181
|
-
throw new
|
|
170
|
+
throw new MigrateMissingMigrationsDirError(this.migrationDir)
|
|
182
171
|
}
|
|
183
172
|
}
|
|
184
173
|
}
|
|
@@ -192,4 +181,22 @@ class Migrator {
|
|
|
192
181
|
}
|
|
193
182
|
}
|
|
194
183
|
|
|
195
|
-
export
|
|
184
|
+
export async function execute (logger, config, to, rollback) {
|
|
185
|
+
const migrationsConfig = config.migrations
|
|
186
|
+
if (migrationsConfig === undefined) {
|
|
187
|
+
throw new MigrateMissingMigrationsError()
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const migrator = new Migrator(migrationsConfig, config.db, logger)
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
if (rollback) {
|
|
194
|
+
await migrator.rollbackMigration()
|
|
195
|
+
} else {
|
|
196
|
+
await migrator.applyMigrations(to)
|
|
197
|
+
}
|
|
198
|
+
return migrator.appliedMigrationsCount > 0
|
|
199
|
+
} finally {
|
|
200
|
+
await migrator.close()
|
|
201
|
+
}
|
|
202
|
+
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const userAgentParser = require('my-ua-parser')
|
|
1
|
+
import fastifyStatic from '@fastify/static'
|
|
2
|
+
import userAgentParser from 'my-ua-parser'
|
|
3
|
+
import path from 'node:path'
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
export async function root (app) {
|
|
7
6
|
app.register(fastifyStatic, {
|
|
8
|
-
root: path.join(
|
|
7
|
+
root: path.join(import.meta.dirname, '../public')
|
|
9
8
|
})
|
|
10
9
|
// root endpoint
|
|
11
10
|
app.route({
|
|
@@ -21,6 +20,6 @@ module.exports = async (app, opts) => {
|
|
|
21
20
|
}
|
|
22
21
|
}
|
|
23
22
|
return { message: 'Welcome to Platformatic! Please visit https://docs.platformatic.dev' }
|
|
24
|
-
}
|
|
23
|
+
}
|
|
25
24
|
})
|
|
26
25
|
}
|
package/lib/schema.js
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
#! /usr/bin/env node
|
|
2
|
-
'use strict'
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
import { schemaComponents as basicSchemaComponents } from '@platformatic/basic'
|
|
4
|
+
import {
|
|
5
|
+
fastifyServer as server,
|
|
6
|
+
schemaComponents as utilsSchemaComponents,
|
|
7
|
+
watch,
|
|
8
|
+
wrappedRuntime
|
|
9
|
+
} from '@platformatic/foundation'
|
|
10
|
+
import { schemaComponents as serviceSchemaComponents } from '@platformatic/service'
|
|
11
|
+
import { readFileSync } from 'node:fs'
|
|
12
|
+
import { resolve } from 'node:path'
|
|
8
13
|
|
|
9
|
-
const
|
|
14
|
+
export const packageJson = JSON.parse(readFileSync(resolve(import.meta.dirname, '../package.json'), 'utf8'))
|
|
15
|
+
export const version = packageJson.version
|
|
16
|
+
|
|
17
|
+
const { plugins, openApiBase, $defs } = serviceSchemaComponents
|
|
18
|
+
|
|
19
|
+
export const db = {
|
|
10
20
|
type: 'object',
|
|
11
21
|
properties: {
|
|
12
22
|
connectionString: {
|
|
@@ -235,7 +245,7 @@ const db = {
|
|
|
235
245
|
required: ['connectionString']
|
|
236
246
|
}
|
|
237
247
|
|
|
238
|
-
const sharedAuthorizationRule = {
|
|
248
|
+
export const sharedAuthorizationRule = {
|
|
239
249
|
role: {
|
|
240
250
|
type: 'string',
|
|
241
251
|
description: 'the role name to match the rule'
|
|
@@ -261,7 +271,7 @@ const sharedAuthorizationRule = {
|
|
|
261
271
|
}
|
|
262
272
|
}
|
|
263
273
|
|
|
264
|
-
const authorization = {
|
|
274
|
+
export const authorization = {
|
|
265
275
|
type: 'object',
|
|
266
276
|
properties: {
|
|
267
277
|
adminSecret: {
|
|
@@ -367,7 +377,7 @@ const authorization = {
|
|
|
367
377
|
additionalProperties: false
|
|
368
378
|
}
|
|
369
379
|
|
|
370
|
-
const migrations = {
|
|
380
|
+
export const migrations = {
|
|
371
381
|
type: 'object',
|
|
372
382
|
properties: {
|
|
373
383
|
dir: {
|
|
@@ -410,7 +420,7 @@ const migrations = {
|
|
|
410
420
|
required: ['dir']
|
|
411
421
|
}
|
|
412
422
|
|
|
413
|
-
const types = {
|
|
423
|
+
export const types = {
|
|
414
424
|
type: 'object',
|
|
415
425
|
properties: {
|
|
416
426
|
autogenerate: {
|
|
@@ -433,10 +443,18 @@ const types = {
|
|
|
433
443
|
additionalProperties: false
|
|
434
444
|
}
|
|
435
445
|
|
|
436
|
-
const
|
|
437
|
-
|
|
446
|
+
export const schemaComponents = {
|
|
447
|
+
db,
|
|
448
|
+
sharedAuthorizationRule,
|
|
449
|
+
authorization,
|
|
450
|
+
migrations,
|
|
451
|
+
types
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
export const schema = {
|
|
455
|
+
$id: `https://schemas.platformatic.dev/@platformatic/db/${packageJson.version}.json`,
|
|
438
456
|
$schema: 'http://json-schema.org/draft-07/schema#',
|
|
439
|
-
title: 'Platformatic
|
|
457
|
+
title: 'Platformatic Database Config',
|
|
440
458
|
type: 'object',
|
|
441
459
|
properties: {
|
|
442
460
|
basePath: {
|
|
@@ -455,12 +473,11 @@ const platformaticDBschema = {
|
|
|
455
473
|
db,
|
|
456
474
|
authorization,
|
|
457
475
|
migrations,
|
|
458
|
-
metrics,
|
|
459
476
|
types,
|
|
460
477
|
plugins,
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
runtime:
|
|
478
|
+
application: basicSchemaComponents.application,
|
|
479
|
+
telemetry: utilsSchemaComponents.telemetry,
|
|
480
|
+
runtime: wrappedRuntime,
|
|
464
481
|
watch: {
|
|
465
482
|
anyOf: [
|
|
466
483
|
watch,
|
|
@@ -482,7 +499,7 @@ const platformaticDBschema = {
|
|
|
482
499
|
additionalProperties: false,
|
|
483
500
|
required: ['db'],
|
|
484
501
|
$defs: {
|
|
485
|
-
|
|
502
|
+
...$defs,
|
|
486
503
|
'crud-operation-auth': {
|
|
487
504
|
oneOf: [
|
|
488
505
|
{
|
|
@@ -534,8 +551,7 @@ const platformaticDBschema = {
|
|
|
534
551
|
}
|
|
535
552
|
}
|
|
536
553
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
console.log(JSON.stringify(platformaticDBschema, null, 2))
|
|
554
|
+
/* c8 ignore next 3 */
|
|
555
|
+
if (process.argv[1] === import.meta.filename) {
|
|
556
|
+
console.log(JSON.stringify(schema, null, 2))
|
|
541
557
|
}
|