vestauth 0.16.0 → 0.17.0
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/CHANGELOG.md +7 -1
- package/package.json +4 -2
- package/src/cli/actions/server/dbCreate.js +18 -0
- package/src/cli/actions/server/dbDrop.js +18 -0
- package/src/cli/actions/server/dbMigrate.js +18 -0
- package/src/cli/actions/server/start.js +4 -1
- package/src/cli/commands/server.js +23 -0
- package/src/db/migrations/.gitkeep +0 -0
- package/src/db/migrations/20260223204000_create_agents_table.js +19 -0
- package/src/db/migrations/20260223205500_create_public_jwks_table.js +25 -0
- package/src/lib/helpers/databaseUrl.js +7 -0
- package/src/lib/helpers/dbCreate.js +54 -0
- package/src/lib/helpers/dbDrop.js +60 -0
- package/src/lib/helpers/dbMigrate.js +30 -0
- package/src/lib/helpers/errors.js +11 -0
- package/src/lib/helpers/serverStart.js +2 -2
- package/src/lib/server/index.js +1 -1
- package/src/lib/server.js +9 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
-
[Unreleased](https://github.com/vestauth/vestauth/compare/v0.
|
|
5
|
+
[Unreleased](https://github.com/vestauth/vestauth/compare/v0.17.0...main)
|
|
6
|
+
|
|
7
|
+
## [0.17.0](https://github.com/vestauth/vestauth/compare/v0.16.0...v0.17.0) (2026-02-23)
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
* Add `db:*` scripts ([#32](https://github.com/vestauth/vestauth/pull/32))
|
|
6
12
|
|
|
7
13
|
## [0.16.0](https://github.com/vestauth/vestauth/compare/v0.15.1...v0.16.0) (2026-02-23)
|
|
8
14
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vestauth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "auth for agents–from the creator of dotenvx",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"vestauth",
|
|
@@ -48,8 +48,10 @@
|
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@dotenvx/dotenvx": "^1.52.0",
|
|
50
50
|
"commander": "^11.1.0",
|
|
51
|
-
"express": "^4.21.2",
|
|
52
51
|
"execa": "^5.1.1",
|
|
52
|
+
"express": "^4.21.2",
|
|
53
|
+
"knex": "^3.1.0",
|
|
54
|
+
"pg": "^8.18.0",
|
|
53
55
|
"structured-headers": "^2.0.2",
|
|
54
56
|
"undici": "7.11.0"
|
|
55
57
|
},
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const { logger } = require('./../../../shared/logger')
|
|
2
|
+
const catchAndLog = require('./../../../lib/helpers/catchAndLog')
|
|
3
|
+
|
|
4
|
+
const server = require('./../../../lib/server')
|
|
5
|
+
|
|
6
|
+
async function dbCreate () {
|
|
7
|
+
try {
|
|
8
|
+
const options = this.opts()
|
|
9
|
+
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
10
|
+
|
|
11
|
+
await server.db.create({ databaseUrl: options.databaseUrl })
|
|
12
|
+
} catch (error) {
|
|
13
|
+
catchAndLog(error)
|
|
14
|
+
process.exit(1)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = dbCreate
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const { logger } = require('./../../../shared/logger')
|
|
2
|
+
const catchAndLog = require('./../../../lib/helpers/catchAndLog')
|
|
3
|
+
|
|
4
|
+
const server = require('./../../../lib/server')
|
|
5
|
+
|
|
6
|
+
async function dbDrop () {
|
|
7
|
+
try {
|
|
8
|
+
const options = this.opts()
|
|
9
|
+
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
10
|
+
|
|
11
|
+
await server.db.drop({ databaseUrl: options.databaseUrl })
|
|
12
|
+
} catch (error) {
|
|
13
|
+
catchAndLog(error)
|
|
14
|
+
process.exit(1)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = dbDrop
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const { logger } = require('./../../../shared/logger')
|
|
2
|
+
const catchAndLog = require('./../../../lib/helpers/catchAndLog')
|
|
3
|
+
|
|
4
|
+
const server = require('./../../../lib/server')
|
|
5
|
+
|
|
6
|
+
async function dbMigrate () {
|
|
7
|
+
try {
|
|
8
|
+
const options = this.opts()
|
|
9
|
+
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
10
|
+
|
|
11
|
+
await server.db.migrate({ databaseUrl: options.databaseUrl })
|
|
12
|
+
} catch (error) {
|
|
13
|
+
catchAndLog(error)
|
|
14
|
+
process.exit(1)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = dbMigrate
|
|
@@ -8,7 +8,10 @@ async function start () {
|
|
|
8
8
|
const options = this.opts()
|
|
9
9
|
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
10
10
|
|
|
11
|
-
await server.start({
|
|
11
|
+
await server.start({
|
|
12
|
+
port: options.port,
|
|
13
|
+
databaseUrl: options.databaseUrl
|
|
14
|
+
})
|
|
12
15
|
} catch (error) {
|
|
13
16
|
catchAndLog(error)
|
|
14
17
|
process.exit(1)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const { Command } = require('commander')
|
|
2
2
|
const env = require('./../../lib/helpers/env')
|
|
3
|
+
const databaseUrl = require('./../../lib/helpers/databaseUrl')
|
|
3
4
|
|
|
4
5
|
const server = new Command('server')
|
|
5
6
|
|
|
@@ -12,6 +13,28 @@ const startAction = require('./../actions/server/start')
|
|
|
12
13
|
server.command('start')
|
|
13
14
|
.description('start vestauth server')
|
|
14
15
|
.option('--port <port>', 'port', env('PORT'))
|
|
16
|
+
.option('--database-url <databaseUrl>', 'DATABASE_URL', databaseUrl())
|
|
15
17
|
.action(startAction)
|
|
16
18
|
|
|
19
|
+
// vestauth server db:create
|
|
20
|
+
const dbCreateAction = require('./../actions/server/dbCreate')
|
|
21
|
+
server.command('db:create')
|
|
22
|
+
.description('create vestauth database')
|
|
23
|
+
.option('--database-url <databaseUrl>', 'DATABASE_URL', databaseUrl())
|
|
24
|
+
.action(dbCreateAction)
|
|
25
|
+
|
|
26
|
+
// vestauth server db:migrate
|
|
27
|
+
const dbMigrateAction = require('./../actions/server/dbMigrate')
|
|
28
|
+
server.command('db:migrate')
|
|
29
|
+
.description('run db migrations')
|
|
30
|
+
.option('--database-url <databaseUrl>', 'DATABASE_URL', databaseUrl())
|
|
31
|
+
.action(dbMigrateAction)
|
|
32
|
+
|
|
33
|
+
// vestauth server db:drop
|
|
34
|
+
const dbDropAction = require('./../actions/server/dbDrop')
|
|
35
|
+
server.command('db:drop')
|
|
36
|
+
.description('delete vestauth database')
|
|
37
|
+
.option('--database-url <databaseUrl>', 'DATABASE_URL', databaseUrl())
|
|
38
|
+
.action(dbDropAction)
|
|
39
|
+
|
|
17
40
|
module.exports = server
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {import('knex').Knex} knex
|
|
3
|
+
*/
|
|
4
|
+
exports.up = async function (knex) {
|
|
5
|
+
await knex.schema.createTable('agents', function (table) {
|
|
6
|
+
table.bigIncrements('id').primary()
|
|
7
|
+
table.string('uid')
|
|
8
|
+
table.datetime('created_at').notNullable()
|
|
9
|
+
table.datetime('updated_at').notNullable()
|
|
10
|
+
table.unique(['uid'], { indexName: 'index_agents_on_uid' })
|
|
11
|
+
})
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {import('knex').Knex} knex
|
|
16
|
+
*/
|
|
17
|
+
exports.down = async function (knex) {
|
|
18
|
+
await knex.schema.dropTableIfExists('agents')
|
|
19
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {import('knex').Knex} knex
|
|
3
|
+
*/
|
|
4
|
+
exports.up = async function (knex) {
|
|
5
|
+
await knex.schema.createTable('public_jwks', function (table) {
|
|
6
|
+
table.bigIncrements('id').primary()
|
|
7
|
+
table.bigInteger('agent_id').notNullable()
|
|
8
|
+
table.string('kid').notNullable()
|
|
9
|
+
table.jsonb('value').notNullable().defaultTo({})
|
|
10
|
+
table.string('state').notNullable().defaultTo('active')
|
|
11
|
+
table.datetime('created_at').notNullable()
|
|
12
|
+
table.datetime('updated_at').notNullable()
|
|
13
|
+
|
|
14
|
+
table.index(['agent_id'], 'index_public_jwks_on_agent_id')
|
|
15
|
+
table.unique(['kid'], { indexName: 'index_public_jwks_on_kid' })
|
|
16
|
+
table.foreign('agent_id').references('agents.id')
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {import('knex').Knex} knex
|
|
22
|
+
*/
|
|
23
|
+
exports.down = async function (knex) {
|
|
24
|
+
await knex.schema.dropTableIfExists('public_jwks')
|
|
25
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const knex = require('knex')
|
|
2
|
+
|
|
3
|
+
function quoteIdentifier (value) {
|
|
4
|
+
return `"${String(value).replace(/"/g, '""')}"`
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function parseConnectionUrl (value) {
|
|
8
|
+
if (!value) throw new Error('missing DATABASE_URL')
|
|
9
|
+
|
|
10
|
+
let url
|
|
11
|
+
try {
|
|
12
|
+
url = new URL(value)
|
|
13
|
+
} catch {
|
|
14
|
+
throw new Error('invalid DATABASE_URL')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const database = decodeURIComponent((url.pathname || '').replace(/^\//, ''))
|
|
18
|
+
if (!database) throw new Error('missing database name in DATABASE_URL')
|
|
19
|
+
|
|
20
|
+
return { url, database }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function dbCreate ({ databaseUrl } = {}) {
|
|
24
|
+
const targetUrl = databaseUrl
|
|
25
|
+
const { url, database } = parseConnectionUrl(targetUrl)
|
|
26
|
+
|
|
27
|
+
// Connect to the maintenance DB (`postgres`) to create the target DB.
|
|
28
|
+
const maintenanceUrl = (() => {
|
|
29
|
+
const copy = new URL(url.toString())
|
|
30
|
+
copy.pathname = '/postgres'
|
|
31
|
+
return copy.toString()
|
|
32
|
+
})()
|
|
33
|
+
|
|
34
|
+
const db = knex({
|
|
35
|
+
client: 'pg',
|
|
36
|
+
connection: maintenanceUrl
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const result = await db.raw('select 1 from pg_database where datname = ?', [database])
|
|
41
|
+
const exists = Array.isArray(result.rows) && result.rows.length > 0
|
|
42
|
+
|
|
43
|
+
if (exists) {
|
|
44
|
+
return { created: false, database }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
await db.raw(`create database ${quoteIdentifier(database)}`)
|
|
48
|
+
return { created: true, database }
|
|
49
|
+
} finally {
|
|
50
|
+
await db.destroy()
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = dbCreate
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const knex = require('knex')
|
|
2
|
+
|
|
3
|
+
function quoteIdentifier (value) {
|
|
4
|
+
return `"${String(value).replace(/"/g, '""')}"`
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function parseConnectionUrl (value) {
|
|
8
|
+
if (!value) throw new Error('missing DATABASE_URL')
|
|
9
|
+
|
|
10
|
+
let url
|
|
11
|
+
try {
|
|
12
|
+
url = new URL(value)
|
|
13
|
+
} catch {
|
|
14
|
+
throw new Error('invalid DATABASE_URL')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const database = decodeURIComponent((url.pathname || '').replace(/^\//, ''))
|
|
18
|
+
if (!database) throw new Error('missing database name in DATABASE_URL')
|
|
19
|
+
|
|
20
|
+
return { url, database }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function dbDrop ({ databaseUrl } = {}) {
|
|
24
|
+
const targetUrl = databaseUrl
|
|
25
|
+
const { url, database } = parseConnectionUrl(targetUrl)
|
|
26
|
+
|
|
27
|
+
// Connect to the maintenance DB (`postgres`) to drop the target DB.
|
|
28
|
+
const maintenanceUrl = (() => {
|
|
29
|
+
const copy = new URL(url.toString())
|
|
30
|
+
copy.pathname = '/postgres'
|
|
31
|
+
return copy.toString()
|
|
32
|
+
})()
|
|
33
|
+
|
|
34
|
+
const db = knex({
|
|
35
|
+
client: 'pg',
|
|
36
|
+
connection: maintenanceUrl
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const result = await db.raw('select 1 from pg_database where datname = ?', [database])
|
|
41
|
+
const exists = Array.isArray(result.rows) && result.rows.length > 0
|
|
42
|
+
|
|
43
|
+
if (!exists) {
|
|
44
|
+
return { dropped: false, database }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Terminate active connections so DROP DATABASE succeeds in local workflows.
|
|
48
|
+
await db.raw(
|
|
49
|
+
'select pg_terminate_backend(pid) from pg_stat_activity where datname = ? and pid <> pg_backend_pid()',
|
|
50
|
+
[database]
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
await db.raw(`drop database ${quoteIdentifier(database)}`)
|
|
54
|
+
return { dropped: true, database }
|
|
55
|
+
} finally {
|
|
56
|
+
await db.destroy()
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = dbDrop
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const knex = require('knex')
|
|
3
|
+
const Errors = require('./errors')
|
|
4
|
+
|
|
5
|
+
async function dbMigrate ({ databaseUrl } = {}) {
|
|
6
|
+
const connection = databaseUrl
|
|
7
|
+
if (!connection) throw new Errors().missingDatabaseUrl()
|
|
8
|
+
|
|
9
|
+
const db = knex({
|
|
10
|
+
client: 'pg',
|
|
11
|
+
connection,
|
|
12
|
+
ssl: { rejectUnauthorized: false },
|
|
13
|
+
migrations: {
|
|
14
|
+
directory: path.resolve(__dirname, '../../db/migrations')
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const [batchNo, migrations] = await db.migrate.latest()
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
batchNo,
|
|
23
|
+
migrations
|
|
24
|
+
}
|
|
25
|
+
} finally {
|
|
26
|
+
await db.destroy()
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = dbMigrate
|
|
@@ -121,6 +121,17 @@ class Errors {
|
|
|
121
121
|
return e
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
missingDatabaseUrl () {
|
|
125
|
+
const code = 'MISSING_DATABASE_URL'
|
|
126
|
+
const message = `[${code}] missing DATABASE_URL`
|
|
127
|
+
const help = `[${code}] pass --database-url or set DATABASE_URL`
|
|
128
|
+
|
|
129
|
+
const e = new Error(message)
|
|
130
|
+
e.code = code
|
|
131
|
+
e.help = help
|
|
132
|
+
return e
|
|
133
|
+
}
|
|
134
|
+
|
|
124
135
|
commandFailed () {
|
|
125
136
|
const code = 'COMMAND_FAILED'
|
|
126
137
|
const message = `[${code}] command failed with exit code ${this.exitCode}`
|
package/src/lib/server/index.js
CHANGED
package/src/lib/server.js
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
const serverStart = require('./helpers/serverStart')
|
|
2
|
+
const dbCreate = require('./helpers/dbCreate')
|
|
3
|
+
const dbMigrate = require('./helpers/dbMigrate')
|
|
4
|
+
const dbDrop = require('./helpers/dbDrop')
|
|
2
5
|
|
|
3
6
|
module.exports = {
|
|
4
|
-
start: serverStart
|
|
7
|
+
start: serverStart,
|
|
8
|
+
db: {
|
|
9
|
+
create: dbCreate,
|
|
10
|
+
migrate: dbMigrate,
|
|
11
|
+
drop: dbDrop
|
|
12
|
+
}
|
|
5
13
|
}
|