refactor: split api schemas into own files + update dependencies

pull/7976/merge
NGPixel 3 months ago
parent 249758e3f9
commit 6f492f0028
No known key found for this signature in database

@ -2,6 +2,11 @@
* API Routes * API Routes
*/ */
async function routes(app) { async function routes(app) {
// Register schemas
await import('./schemas/site.js').then((m) => m.registerSchemas(app))
await import('./schemas/user.js').then((m) => m.registerSchemas(app))
// Register routes
app.register(import('./authentication.js')) app.register(import('./authentication.js'))
app.register(import('./locales.js'), { prefix: '/locales' }) app.register(import('./locales.js'), { prefix: '/locales' })
app.register(import('./pages.js')) app.register(import('./pages.js'))

@ -0,0 +1,225 @@
export async function registerSchemas(app) {
/**
* SITE
*/
app.addSchema({
$id: 'Site',
type: 'object',
properties: {
id: {
type: 'string',
format: 'uuid'
},
hostname: {
type: 'string',
format: 'hostname'
},
isEnabled: {
type: 'boolean'
},
title: {
type: 'string'
},
description: {
type: 'string'
},
company: {
type: 'string'
},
contentLicense: {
type: 'string'
},
footerExtra: {
type: 'string'
},
pageExtensions: {
type: 'array',
items: {
type: 'string'
}
},
pageCasing: {
type: 'boolean'
},
discoverable: {
type: 'boolean'
},
defaults: {
type: 'object',
properties: {
tocDepth: {
type: 'object',
properties: {
min: {
type: 'number'
},
max: {
type: 'number'
}
}
}
}
},
features: {
type: 'object',
properties: {
browse: {
type: 'boolean'
},
ratings: {
type: 'boolean'
},
ratingsMode: {
type: 'string',
enum: ['off', 'stars', 'thumbs']
},
comments: {
type: 'boolean'
},
contributions: {
type: 'boolean'
},
profile: {
type: 'boolean'
},
search: {
type: 'boolean'
}
}
},
logoUrl: {
type: 'string'
},
logoText: {
type: 'boolean'
},
sitemap: {
type: 'boolean'
},
robots: {
type: 'object',
properties: {
index: {
type: 'boolean'
},
follow: {
type: 'boolean'
}
}
},
locales: {
type: 'object',
properties: {
primary: {
type: 'string'
},
active: {
type: 'array',
items: {
type: 'string'
}
}
}
},
assets: {
type: 'object',
properties: {
logo: {
type: 'boolean'
},
logoExt: {
type: 'string'
},
favicon: {
type: 'boolean'
},
faviconExt: {
type: 'string'
},
loginBg: {
type: 'boolean'
}
}
},
editors: {
type: 'object',
properties: {
asciidoc: {
type: 'boolean'
},
markdown: {
type: 'boolean'
},
wysiwyg: {
type: 'boolean'
}
}
},
theme: {
type: 'object',
properties: {
dark: {
type: 'boolean'
},
codeBlocksTheme: {
type: 'string',
format: 'hexcolor'
},
colorPrimary: {
type: 'string',
format: 'hexcolor'
},
colorSecondary: {
type: 'string',
format: 'hexcolor'
},
colorAccent: {
type: 'string',
format: 'hexcolor'
},
colorHeader: {
type: 'string',
format: 'hexcolor'
},
colorSidebar: {
type: 'string',
format: 'hexcolor'
},
injectCSS: {
type: 'string'
},
injectHead: {
type: 'string'
},
injectBody: {
type: 'string'
},
contentWidth: {
type: 'string',
enum: ['centered', 'full']
},
sidebarPosition: {
type: 'string',
enum: ['off', 'left', 'right']
},
tocPosition: {
type: 'string',
enum: ['off', 'left', 'right']
},
showSharingMenu: {
type: 'boolean'
},
showPrintBtn: {
type: 'boolean'
},
baseFont: {
type: 'string'
},
contentFont: {
type: 'string'
}
}
}
}
})
}

@ -0,0 +1,82 @@
export async function registerSchemas(app) {
/**
* USER CORE - Essential fields only
*/
app.addSchema({
$id: 'UserCore',
type: 'object',
properties: {
id: {
type: 'string',
format: 'uuid'
},
name: {
type: 'string',
minLength: 1,
maxLength: 255
},
email: {
type: 'string',
format: 'email'
},
hasAvatar: {
type: 'boolean'
},
isSystem: {
type: 'boolean'
},
isActive: {
type: 'boolean'
},
isVerified: {
type: 'boolean'
},
createdAt: {
type: 'string',
format: 'date-time',
description: 'RFC 3339 Date Time'
},
updatedAt: {
type: 'string',
format: 'date-time',
description: 'RFC 3339 Date Time'
},
lastLoginAt: {
type: 'string',
format: 'date-time',
description: 'RFC 3339 Date Time'
}
}
})
/**
* USER - All fields
*/
app.addSchema({
$id: 'User',
allOf: [
{
$ref: 'UserCore#'
},
{
type: 'object',
properties: {
meta: {
type: 'object',
additionalProperties: true
},
prefs: {
type: 'object',
additionalProperties: true
},
auth: {
type: 'string'
},
passkeys: {
type: 'string'
}
}
}
]
})
}

@ -5,226 +5,6 @@ import { CustomError } from '../helpers/common.js'
* Sites API Routes * Sites API Routes
*/ */
async function routes(app) { async function routes(app) {
app.addSchema({
$id: 'siteSchema',
type: 'object',
properties: {
id: {
type: 'string',
format: 'uuid'
},
hostname: {
type: 'string',
format: 'hostname'
},
isEnabled: {
type: 'boolean'
},
title: {
type: 'string'
},
description: {
type: 'string'
},
company: {
type: 'string'
},
contentLicense: {
type: 'string'
},
footerExtra: {
type: 'string'
},
pageExtensions: {
type: 'array',
items: {
type: 'string'
}
},
pageCasing: {
type: 'boolean'
},
discoverable: {
type: 'boolean'
},
defaults: {
type: 'object',
properties: {
tocDepth: {
type: 'object',
properties: {
min: {
type: 'number'
},
max: {
type: 'number'
}
}
}
}
},
features: {
type: 'object',
properties: {
browse: {
type: 'boolean'
},
ratings: {
type: 'boolean'
},
ratingsMode: {
type: 'string',
enum: ['off', 'stars', 'thumbs']
},
comments: {
type: 'boolean'
},
contributions: {
type: 'boolean'
},
profile: {
type: 'boolean'
},
search: {
type: 'boolean'
}
}
},
logoUrl: {
type: 'string'
},
logoText: {
type: 'boolean'
},
sitemap: {
type: 'boolean'
},
robots: {
type: 'object',
properties: {
index: {
type: 'boolean'
},
follow: {
type: 'boolean'
}
}
},
locales: {
type: 'object',
properties: {
primary: {
type: 'string'
},
active: {
type: 'array',
items: {
type: 'string'
}
}
}
},
assets: {
type: 'object',
properties: {
logo: {
type: 'boolean'
},
logoExt: {
type: 'string'
},
favicon: {
type: 'boolean'
},
faviconExt: {
type: 'string'
},
loginBg: {
type: 'boolean'
}
}
},
editors: {
type: 'object',
properties: {
asciidoc: {
type: 'boolean'
},
markdown: {
type: 'boolean'
},
wysiwyg: {
type: 'boolean'
}
}
},
theme: {
type: 'object',
properties: {
dark: {
type: 'boolean'
},
codeBlocksTheme: {
type: 'string',
format: 'hexcolor'
},
colorPrimary: {
type: 'string',
format: 'hexcolor'
},
colorSecondary: {
type: 'string',
format: 'hexcolor'
},
colorAccent: {
type: 'string',
format: 'hexcolor'
},
colorHeader: {
type: 'string',
format: 'hexcolor'
},
colorSidebar: {
type: 'string',
format: 'hexcolor'
},
injectCSS: {
type: 'string'
},
injectHead: {
type: 'string'
},
injectBody: {
type: 'string'
},
contentWidth: {
type: 'string',
enum: ['centered', 'full']
},
sidebarPosition: {
type: 'string',
enum: ['off', 'left', 'right']
},
tocPosition: {
type: 'string',
enum: ['off', 'left', 'right']
},
showSharingMenu: {
type: 'boolean'
},
showPrintBtn: {
type: 'boolean'
},
baseFont: {
type: 'string'
},
contentFont: {
type: 'string'
}
}
}
}
})
app.get( app.get(
'/', '/',
{ {
@ -238,7 +18,7 @@ async function routes(app) {
200: { 200: {
description: 'List of all sites', description: 'List of all sites',
type: 'array', type: 'array',
items: { $ref: 'siteSchema#' } items: { $ref: 'Site#' }
} }
} }
} }
@ -291,7 +71,7 @@ async function routes(app) {
200: { 200: {
description: 'Site info', description: 'Site info',
type: 'object', type: 'object',
$ref: 'siteSchema#' $ref: 'Site#'
} }
} }
} }

@ -10,7 +10,29 @@ async function routes(app, options) {
}, },
schema: { schema: {
summary: 'List all users', summary: 'List all users',
tags: ['Users'] tags: ['Users'],
querystring: {
type: 'object',
properties: {
page: { type: 'integer', minimum: 1, default: 1 },
limit: { type: 'integer', minimum: 1, maximum: 100, default: 20 }
}
},
response: {
200: {
description: 'List of Users',
type: 'object',
properties: {
page: { type: 'integer' },
limit: { type: 'integer' },
total: { type: 'integer' },
users: {
type: 'array',
items: { $ref: 'UserCore#' }
}
}
}
}
} }
}, },
async (request, reply) => { async (request, reply) => {
@ -32,7 +54,7 @@ async function routes(app, options) {
return { return {
authenticated: true, authenticated: true,
...req.session.user, ...req.session.user,
permissions: ['manage:system'] permissions: ['manage:system'] // TODO: pull actual permissions
} }
} else { } else {
return { return {
@ -50,7 +72,23 @@ async function routes(app, options) {
}, },
schema: { schema: {
summary: 'Get user info', summary: 'Get user info',
tags: ['Users'] tags: ['Users'],
params: {
type: 'object',
properties: {
userId: {
type: 'string',
format: 'uuid'
}
}
},
response: {
200: {
description: 'User info',
type: 'object',
$ref: 'User#'
}
}
} }
}, },
async (request, reply) => { async (request, reply) => {

File diff suppressed because it is too large Load Diff

@ -38,7 +38,7 @@
"node": ">=24.0" "node": ">=24.0"
}, },
"dependencies": { "dependencies": {
"@fastify/compress": "8.3.1", "@fastify/compress": "9.0.0",
"@fastify/cookie": "11.0.2", "@fastify/cookie": "11.0.2",
"@fastify/cors": "11.2.0", "@fastify/cors": "11.2.0",
"@fastify/formbody": "8.0.2", "@fastify/formbody": "8.0.2",
@ -46,40 +46,40 @@
"@fastify/passport": "3.0.2", "@fastify/passport": "3.0.2",
"@fastify/sensible": "6.0.4", "@fastify/sensible": "6.0.4",
"@fastify/session": "11.1.1", "@fastify/session": "11.1.1",
"@fastify/static": "9.0.0", "@fastify/static": "9.1.3",
"@fastify/swagger": "9.6.1", "@fastify/swagger": "9.7.0",
"@fastify/swagger-ui": "5.2.4", "@fastify/swagger-ui": "6.0.0",
"@fastify/view": "11.1.1", "@fastify/view": "12.0.0",
"@gquittet/graceful-server": "6.0.3", "@gquittet/graceful-server": "6.0.10",
"ajv-formats": "3.0.1", "ajv-formats": "3.0.1",
"bcryptjs": "3.0.3", "bcryptjs": "3.0.3",
"chalk": "5.6.2", "chalk": "5.6.2",
"cron-parser": "5.5.0", "cron-parser": "5.5.0",
"drizzle-orm": "1.0.0-beta.15-859cf75", "drizzle-orm": "1.0.0-beta.15-859cf75",
"emittery": "2.0.0", "emittery": "2.0.0",
"es-toolkit": "1.45.1", "es-toolkit": "1.47.1",
"fastify": "5.7.1", "fastify": "5.8.5",
"fastify-favicon": "5.0.0", "fastify-favicon": "5.0.0",
"filesize": "11.0.13", "filesize": "11.0.17",
"fs-extra": "11.3.4", "fs-extra": "11.3.5",
"js-yaml": "4.1.1", "js-yaml": "4.2.0",
"luxon": "3.7.2", "luxon": "3.7.2",
"mime": "4.1.0", "mime": "4.1.0",
"nanoid": "5.1.6", "nanoid": "5.1.11",
"node-cache": "5.1.2", "node-cache": "5.1.2",
"pem-jwk": "2.0.0", "pem-jwk": "2.0.0",
"pg": "8.17.2", "pg": "8.21.0",
"poolifier": "5.3.2", "poolifier": "5.3.2",
"pug": "3.0.3", "pug": "3.0.4",
"semver": "7.7.3", "semver": "7.8.4",
"uuid": "13.0.0" "uuid": "14.0.0"
}, },
"devDependencies": { "devDependencies": {
"drizzle-kit": "1.0.0-beta.15-859cf75", "drizzle-kit": "1.0.0-beta.15-859cf75",
"nodemon": "3.1.11", "nodemon": "3.1.14",
"npm-check-updates": "19.3.1", "npm-check-updates": "22.2.3",
"oxfmt": "0.36.0", "oxfmt": "0.54.0",
"oxlint": "1.51.0" "oxlint": "1.69.0"
}, },
"collective": { "collective": {
"type": "opencollective", "type": "opencollective",

File diff suppressed because it is too large Load Diff

@ -13,31 +13,31 @@
"ncu-u": "ncu -u" "ncu-u": "ncu -u"
}, },
"dependencies": { "dependencies": {
"@lezer/common": "1.5.1", "@lezer/common": "1.5.2",
"@mdi/font": "7.4.47", "@mdi/font": "7.4.47",
"@quasar/extras": "1.17.0", "@quasar/extras": "2.0.1",
"@simplewebauthn/browser": "13.2.2", "@simplewebauthn/browser": "13.3.0",
"@tanstack/vue-query": "5.92.9", "@tanstack/vue-query": "5.101.0",
"@xterm/xterm": "6.0.0", "@xterm/xterm": "6.0.0",
"browser-fs-access": "0.38.0", "browser-fs-access": "0.38.0",
"clipboard": "2.0.11", "clipboard": "2.0.11",
"codemirror": "5.65.11", "codemirror": "5.65.11",
"codemirror-asciidoc": "1.0.4", "codemirror-asciidoc": "1.0.4",
"dependency-graph": "1.0.0", "dependency-graph": "1.0.0",
"filesize": "11.0.13", "filesize": "11.0.17",
"filesize-parser": "1.5.1", "filesize-parser": "1.5.1",
"fuse.js": "7.1.0", "fuse.js": "7.4.2",
"highlight.js": "11.11.1", "highlight.js": "11.11.1",
"js-cookie": "3.0.5", "js-cookie": "3.0.8",
"jwt-decode": "4.0.0", "jwt-decode": "4.0.0",
"katex": "0.16.33", "katex": "0.17.0",
"ky": "1.14.3", "ky": "2.0.2",
"lodash-es": "4.17.23", "lodash-es": "4.18.1",
"lowlight": "3.3.0", "lowlight": "3.3.0",
"luxon": "3.7.2", "luxon": "3.7.2",
"markdown-it": "14.1.1", "markdown-it": "14.2.0",
"markdown-it-abbr": "2.0.0", "markdown-it-abbr": "2.0.0",
"markdown-it-attrs": "4.3.1", "markdown-it-attrs": "5.0.0",
"markdown-it-decorate": "1.2.2", "markdown-it-decorate": "1.2.2",
"markdown-it-emoji": "3.0.0", "markdown-it-emoji": "3.0.0",
"markdown-it-expand-tabs": "1.0.13", "markdown-it-expand-tabs": "1.0.13",
@ -56,43 +56,43 @@
"prosemirror-commands": "1.7.1", "prosemirror-commands": "1.7.1",
"prosemirror-history": "1.5.0", "prosemirror-history": "1.5.0",
"prosemirror-keymap": "1.2.3", "prosemirror-keymap": "1.2.3",
"prosemirror-model": "1.25.4", "prosemirror-model": "1.25.8",
"prosemirror-schema-list": "1.5.1", "prosemirror-schema-list": "1.5.1",
"prosemirror-state": "1.4.4", "prosemirror-state": "1.4.4",
"prosemirror-transform": "1.11.0", "prosemirror-transform": "1.12.0",
"prosemirror-view": "1.41.6", "prosemirror-view": "1.41.9",
"pug": "3.0.3", "pug": "3.0.4",
"quasar": "2.18.6", "quasar": "2.20.0",
"slugify": "1.6.6", "slugify": "1.6.9",
"socket.io-client": "4.8.3", "socket.io-client": "4.8.3",
"sortablejs": "1.15.7", "sortablejs": "1.15.7",
"sortablejs-vue3": "1.3.0", "sortablejs-vue3": "1.3.0",
"tabulator-tables": "6.3.1", "tabulator-tables": "6.4.0",
"tippy.js": "6.3.7", "tippy.js": "6.3.7",
"twemoji": "14.0.2", "twemoji": "14.0.2",
"typescript": "5.9.3", "typescript": "6.0.3",
"uuid": "13.0.0", "uuid": "14.0.0",
"v-network-graph": "0.9.22", "v-network-graph": "0.9.22",
"vue": "3.5.29", "vue": "3.5.38",
"vue-i18n": "11.2.8", "vue-i18n": "11.4.5",
"vue-router": "5.0.3", "vue-router": "5.1.0",
"vue3-otp-input": "0.5.40", "vue3-otp-input": "0.5.40",
"vuedraggable": "4.1.0", "vuedraggable": "4.1.0",
"zxcvbn": "4.4.2" "zxcvbn": "4.4.2"
}, },
"devDependencies": { "devDependencies": {
"@intlify/unplugin-vue-i18n": "11.0.7", "@intlify/unplugin-vue-i18n": "11.2.3",
"@quasar/app-vite": "2.4.1", "@quasar/app-vite": "2.6.2",
"@quasar/vite-plugin": "1.10.0", "@quasar/vite-plugin": "1.12.0",
"@types/lodash": "4.17.24", "@types/lodash": "4.17.24",
"@vue/language-plugin-pug": "3.2.5", "@vue/language-plugin-pug": "3.3.5",
"autoprefixer": "10.4.27", "autoprefixer": "10.5.0",
"browserlist": "latest", "browserlist": "latest",
"npm-check-updates": "19.6.3", "npm-check-updates": "22.2.3",
"oxfmt": "0.36.0", "oxfmt": "0.54.0",
"oxlint": "1.51.0", "oxlint": "1.69.0",
"sass": "1.97.3", "sass": "1.101.0",
"vite-plugin-vue-devtools": "8.0.6" "vite-plugin-vue-devtools": "8.1.2"
}, },
"engines": { "engines": {
"node": ">= 18.0", "node": ">= 18.0",

@ -9,12 +9,12 @@ export function initializeApi(store) {
let fetching = false let fetching = false
const client = ky.create({ const client = ky.create({
prefixUrl: '/_api', prefix: '/_api',
credentials: 'same-origin', credentials: 'same-origin',
throwHttpErrors: (statusNumber) => statusNumber > 400, // Don't throw for 400 throwHttpErrors: (statusNumber) => statusNumber > 400, // Don't throw for 400
hooks: { hooks: {
beforeRequest: [ beforeRequest: [
async (request) => { async ({ request }) => {
// -> Guest // -> Guest
if (!userStore.token) { if (!userStore.token) {
request.headers.set('Authorization', '') request.headers.set('Authorization', '')

Loading…
Cancel
Save