refactor: split api schemas into own files + update dependencies

scarlett
NGPixel 3 days ago
parent 249758e3f9
commit 6f492f0028
No known key found for this signature in database

@ -2,6 +2,11 @@
* API Routes
*/
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('./locales.js'), { prefix: '/locales' })
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
*/
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(
'/',
{
@ -238,7 +18,7 @@ async function routes(app) {
200: {
description: 'List of all sites',
type: 'array',
items: { $ref: 'siteSchema#' }
items: { $ref: 'Site#' }
}
}
}
@ -291,7 +71,7 @@ async function routes(app) {
200: {
description: 'Site info',
type: 'object',
$ref: 'siteSchema#'
$ref: 'Site#'
}
}
}

@ -10,7 +10,29 @@ async function routes(app, options) {
},
schema: {
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) => {
@ -32,7 +54,7 @@ async function routes(app, options) {
return {
authenticated: true,
...req.session.user,
permissions: ['manage:system']
permissions: ['manage:system'] // TODO: pull actual permissions
}
} else {
return {
@ -50,7 +72,23 @@ async function routes(app, options) {
},
schema: {
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) => {

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

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

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

Loading…
Cancel
Save