WIP set up error routes and remove knex
This commit is contained in:
parent
d6a5445ab5
commit
5af010f3af
20
.bruno/API/api-errors.bru
Normal file
20
.bruno/API/api-errors.bru
Normal file
@ -0,0 +1,20 @@
|
||||
meta {
|
||||
name: /api/errors
|
||||
type: http
|
||||
seq: 22
|
||||
}
|
||||
|
||||
delete {
|
||||
url: {{base_url}}/api/errors?id=4
|
||||
body: none
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
params:query {
|
||||
id: 4
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
@ -2,6 +2,7 @@ import { h } from 'preact'
|
||||
import { useCallback, useEffect, useState } from 'preact/hooks'
|
||||
import { route } from 'preact-router'
|
||||
import rek, { type FetchError } from 'rek'
|
||||
import type { Selectable } from 'kysely'
|
||||
|
||||
import { useNotifications } from '../contexts/notifications.tsx'
|
||||
import useItemsReducer from '../hooks/use_items_reducer.ts'
|
||||
@ -11,12 +12,12 @@ import RolesTable from './roles_table.tsx'
|
||||
import PageHeader from './page_header.tsx'
|
||||
import Section from './section.tsx'
|
||||
|
||||
import type { Role } from '../../../server/services/roles/types.ts'
|
||||
import type { Role } from '../../../shared/types.db.ts'
|
||||
|
||||
const RolesPage = () => {
|
||||
const { notify } = useNotifications()
|
||||
const [roles, actions] = useItemsReducer<Role>()
|
||||
const [edit, setEdit] = useState<Role | null | undefined>(null)
|
||||
const [roles, actions] = useItemsReducer<Selectable<Role>>()
|
||||
const [edit, setEdit] = useState<Selectable<Role> | null | undefined>(null)
|
||||
|
||||
useEffect(() => {
|
||||
rek('/api/roles' + location.search).then(actions.reset)
|
||||
|
||||
@ -2,16 +2,17 @@ import { h } from 'preact'
|
||||
import { useCallback, useEffect } from 'preact/hooks'
|
||||
import { route } from 'preact-router'
|
||||
import rek from 'rek'
|
||||
import type { Selectable } from 'kysely'
|
||||
|
||||
import useItemsReducer from '../hooks/use_items_reducer.ts'
|
||||
import UsersTable from './users_table.tsx'
|
||||
import PageHeader from './page_header.tsx'
|
||||
import Section from './section.tsx'
|
||||
|
||||
import type { User } from '../../../server/services/users/types.ts'
|
||||
import type { User } from '../../../shared/types.db.ts'
|
||||
|
||||
const UsersPage = () => {
|
||||
const [users, actions] = useItemsReducer<User>()
|
||||
const [users, actions] = useItemsReducer<Selectable<User>>()
|
||||
|
||||
useEffect(() => {
|
||||
rek('/api/users' + location.search).then(actions.reset)
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
import { h, type FunctionComponent } from 'preact'
|
||||
import { Table, Th, type onSortByFunction } from './table.tsx'
|
||||
import type { Selectable } from 'kysely'
|
||||
|
||||
import type { User } from '../../../server/services/users/types.ts'
|
||||
import type { User, Role } from '../../../shared/types.db.ts'
|
||||
|
||||
const UsersTable: FunctionComponent<{ users: User[]; onSortBy: onSortByFunction }> = ({ users, onSortBy }) => (
|
||||
const UsersTable: FunctionComponent<{
|
||||
users: (Selectable<User> & { roles?: Role[] })[]
|
||||
onSortBy: onSortByFunction
|
||||
}> = ({ users, onSortBy }) => (
|
||||
<Table onSortBy={onSortBy}>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@ -40,7 +40,6 @@
|
||||
"fastify-session-redis-store": "^7.1.2",
|
||||
"fastify-type-provider-zod": "^6.1.0",
|
||||
"ioredis": "^5.8.2",
|
||||
"knex": "^3.1.0",
|
||||
"kysely": "^0.28.9",
|
||||
"lodash": "^4.17.21",
|
||||
"lowline": "^0.4.2",
|
||||
|
||||
165
pnpm-lock.yaml
generated
165
pnpm-lock.yaml
generated
@ -56,9 +56,6 @@ importers:
|
||||
ioredis:
|
||||
specifier: ^5.8.2
|
||||
version: 5.8.2
|
||||
knex:
|
||||
specifier: ^3.1.0
|
||||
version: 3.1.0(pg@8.16.3)
|
||||
kysely:
|
||||
specifier: ^0.28.9
|
||||
version: 0.28.9
|
||||
@ -1167,16 +1164,9 @@ packages:
|
||||
color-name@1.1.4:
|
||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||
|
||||
colorette@2.0.19:
|
||||
resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==}
|
||||
|
||||
colorette@2.0.20:
|
||||
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
|
||||
|
||||
commander@10.0.1:
|
||||
resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
commander@14.0.2:
|
||||
resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==}
|
||||
engines: {node: '>=20'}
|
||||
@ -1224,15 +1214,6 @@ packages:
|
||||
resolution: {integrity: sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
debug@4.3.4:
|
||||
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
||||
engines: {node: '>=6.0'}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
|
||||
debug@4.4.3:
|
||||
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
|
||||
engines: {node: '>=6.0'}
|
||||
@ -1360,10 +1341,6 @@ packages:
|
||||
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
|
||||
engines: {node: '>=0.8.0'}
|
||||
|
||||
esm@3.2.25:
|
||||
resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
estree-walker@2.0.2:
|
||||
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||
|
||||
@ -1456,17 +1433,10 @@ packages:
|
||||
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
get-package-type@0.1.0:
|
||||
resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
|
||||
get-proto@1.0.1:
|
||||
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
getopts@2.3.0:
|
||||
resolution: {integrity: sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==}
|
||||
|
||||
glob@11.1.0:
|
||||
resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==}
|
||||
engines: {node: 20 || >=22}
|
||||
@ -1546,10 +1516,6 @@ packages:
|
||||
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
interpret@2.2.0:
|
||||
resolution: {integrity: sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
||||
ioredis@5.8.2:
|
||||
resolution: {integrity: sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q==}
|
||||
engines: {node: '>=12.22.0'}
|
||||
@ -1578,10 +1544,6 @@ packages:
|
||||
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
is-core-module@2.16.1:
|
||||
resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
is-date-object@1.1.0:
|
||||
resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -1687,34 +1649,6 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
hasBin: true
|
||||
|
||||
knex@3.1.0:
|
||||
resolution: {integrity: sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==}
|
||||
engines: {node: '>=16'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
better-sqlite3: '*'
|
||||
mysql: '*'
|
||||
mysql2: '*'
|
||||
pg: '*'
|
||||
pg-native: '*'
|
||||
sqlite3: '*'
|
||||
tedious: '*'
|
||||
peerDependenciesMeta:
|
||||
better-sqlite3:
|
||||
optional: true
|
||||
mysql:
|
||||
optional: true
|
||||
mysql2:
|
||||
optional: true
|
||||
pg:
|
||||
optional: true
|
||||
pg-native:
|
||||
optional: true
|
||||
sqlite3:
|
||||
optional: true
|
||||
tedious:
|
||||
optional: true
|
||||
|
||||
kolorist@1.8.0:
|
||||
resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
|
||||
|
||||
@ -1795,9 +1729,6 @@ packages:
|
||||
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
|
||||
ms@2.1.2:
|
||||
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
||||
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
@ -1869,9 +1800,6 @@ packages:
|
||||
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
path-parse@1.0.7:
|
||||
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
|
||||
|
||||
path-scurry@2.0.1:
|
||||
resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==}
|
||||
engines: {node: 20 || >=22}
|
||||
@ -1882,9 +1810,6 @@ packages:
|
||||
pg-cloudflare@1.2.7:
|
||||
resolution: {integrity: sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==}
|
||||
|
||||
pg-connection-string@2.6.2:
|
||||
resolution: {integrity: sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==}
|
||||
|
||||
pg-connection-string@2.9.1:
|
||||
resolution: {integrity: sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==}
|
||||
|
||||
@ -2021,10 +1946,6 @@ packages:
|
||||
resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
|
||||
engines: {node: '>= 12.13.0'}
|
||||
|
||||
rechoir@0.8.0:
|
||||
resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==}
|
||||
engines: {node: '>= 10.13.0'}
|
||||
|
||||
redis-errors@1.2.0:
|
||||
resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==}
|
||||
engines: {node: '>=4'}
|
||||
@ -2044,15 +1965,6 @@ packages:
|
||||
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
resolve-from@5.0.0:
|
||||
resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
resolve@1.22.11:
|
||||
resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
hasBin: true
|
||||
|
||||
restore-cursor@5.1.0:
|
||||
resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==}
|
||||
engines: {node: '>=18'}
|
||||
@ -2229,24 +2141,12 @@ packages:
|
||||
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
supports-preserve-symlinks-flag@1.0.0:
|
||||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
symbol-tree@3.2.4:
|
||||
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
|
||||
|
||||
tarn@3.0.2:
|
||||
resolution: {integrity: sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
|
||||
thread-stream@3.1.0:
|
||||
resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==}
|
||||
|
||||
tildify@2.0.0:
|
||||
resolution: {integrity: sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
tinyglobby@0.2.15:
|
||||
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
@ -3264,12 +3164,8 @@ snapshots:
|
||||
|
||||
color-name@1.1.4: {}
|
||||
|
||||
colorette@2.0.19: {}
|
||||
|
||||
colorette@2.0.20: {}
|
||||
|
||||
commander@10.0.1: {}
|
||||
|
||||
commander@14.0.2: {}
|
||||
|
||||
commander@7.2.0: {}
|
||||
@ -3320,10 +3216,6 @@ snapshots:
|
||||
whatwg-mimetype: 4.0.0
|
||||
whatwg-url: 15.1.0
|
||||
|
||||
debug@4.3.4:
|
||||
dependencies:
|
||||
ms: 2.1.2
|
||||
|
||||
debug@4.4.3:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
@ -3500,8 +3392,6 @@ snapshots:
|
||||
|
||||
escape-string-regexp@1.0.5: {}
|
||||
|
||||
esm@3.2.25: {}
|
||||
|
||||
estree-walker@2.0.2: {}
|
||||
|
||||
eventemitter3@5.0.1: {}
|
||||
@ -3608,15 +3498,11 @@ snapshots:
|
||||
hasown: 2.0.2
|
||||
math-intrinsics: 1.1.0
|
||||
|
||||
get-package-type@0.1.0: {}
|
||||
|
||||
get-proto@1.0.1:
|
||||
dependencies:
|
||||
dunder-proto: 1.0.1
|
||||
es-object-atoms: 1.1.1
|
||||
|
||||
getopts@2.3.0: {}
|
||||
|
||||
glob@11.1.0:
|
||||
dependencies:
|
||||
foreground-child: 3.3.1
|
||||
@ -3694,8 +3580,6 @@ snapshots:
|
||||
hasown: 2.0.2
|
||||
side-channel: 1.1.0
|
||||
|
||||
interpret@2.2.0: {}
|
||||
|
||||
ioredis@5.8.2:
|
||||
dependencies:
|
||||
'@ioredis/commands': 1.4.0
|
||||
@ -3734,10 +3618,6 @@ snapshots:
|
||||
|
||||
is-callable@1.2.7: {}
|
||||
|
||||
is-core-module@2.16.1:
|
||||
dependencies:
|
||||
hasown: 2.0.2
|
||||
|
||||
is-date-object@1.1.0:
|
||||
dependencies:
|
||||
call-bound: 1.0.4
|
||||
@ -3854,27 +3734,6 @@ snapshots:
|
||||
|
||||
json5@2.2.3: {}
|
||||
|
||||
knex@3.1.0(pg@8.16.3):
|
||||
dependencies:
|
||||
colorette: 2.0.19
|
||||
commander: 10.0.1
|
||||
debug: 4.3.4
|
||||
escalade: 3.2.0
|
||||
esm: 3.2.25
|
||||
get-package-type: 0.1.0
|
||||
getopts: 2.3.0
|
||||
interpret: 2.2.0
|
||||
lodash: 4.17.21
|
||||
pg-connection-string: 2.6.2
|
||||
rechoir: 0.8.0
|
||||
resolve-from: 5.0.0
|
||||
tarn: 3.0.2
|
||||
tildify: 2.0.0
|
||||
optionalDependencies:
|
||||
pg: 8.16.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
kolorist@1.8.0: {}
|
||||
|
||||
kysely@0.28.9: {}
|
||||
@ -3953,8 +3812,6 @@ snapshots:
|
||||
|
||||
minipass@7.1.2: {}
|
||||
|
||||
ms@2.1.2: {}
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
nano-spawn@2.0.0: {}
|
||||
@ -4020,8 +3877,6 @@ snapshots:
|
||||
|
||||
path-key@3.1.1: {}
|
||||
|
||||
path-parse@1.0.7: {}
|
||||
|
||||
path-scurry@2.0.1:
|
||||
dependencies:
|
||||
lru-cache: 11.2.2
|
||||
@ -4032,8 +3887,6 @@ snapshots:
|
||||
pg-cloudflare@1.2.7:
|
||||
optional: true
|
||||
|
||||
pg-connection-string@2.6.2: {}
|
||||
|
||||
pg-connection-string@2.9.1: {}
|
||||
|
||||
pg-int8@1.0.1: {}
|
||||
@ -4153,10 +4006,6 @@ snapshots:
|
||||
|
||||
real-require@0.2.0: {}
|
||||
|
||||
rechoir@0.8.0:
|
||||
dependencies:
|
||||
resolve: 1.22.11
|
||||
|
||||
redis-errors@1.2.0: {}
|
||||
|
||||
redis-parser@3.0.0:
|
||||
@ -4176,14 +4025,6 @@ snapshots:
|
||||
|
||||
require-from-string@2.0.2: {}
|
||||
|
||||
resolve-from@5.0.0: {}
|
||||
|
||||
resolve@1.22.11:
|
||||
dependencies:
|
||||
is-core-module: 2.16.1
|
||||
path-parse: 1.0.7
|
||||
supports-preserve-symlinks-flag: 1.0.0
|
||||
|
||||
restore-cursor@5.1.0:
|
||||
dependencies:
|
||||
onetime: 7.0.0
|
||||
@ -4386,18 +4227,12 @@ snapshots:
|
||||
dependencies:
|
||||
has-flag: 4.0.0
|
||||
|
||||
supports-preserve-symlinks-flag@1.0.0: {}
|
||||
|
||||
symbol-tree@3.2.4: {}
|
||||
|
||||
tarn@3.0.2: {}
|
||||
|
||||
thread-stream@3.1.0:
|
||||
dependencies:
|
||||
real-require: 0.2.0
|
||||
|
||||
tildify@2.0.0: {}
|
||||
|
||||
tinyglobby@0.2.15:
|
||||
dependencies:
|
||||
fdir: 6.5.0(picomatch@4.0.3)
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
import knex from 'knex'
|
||||
import env from '../env.ts'
|
||||
|
||||
export default knex({
|
||||
client: 'pg',
|
||||
|
||||
connection: {
|
||||
database: env.PGDATABASE,
|
||||
host: env.PGHOST,
|
||||
password: env.PGPASSWORD,
|
||||
port: env.PGPORT,
|
||||
user: env.PGUSER,
|
||||
},
|
||||
|
||||
acquireConnectionTimeout: 30000,
|
||||
})
|
||||
@ -1,53 +0,0 @@
|
||||
import type { QueryBuilder } from 'knex'
|
||||
import _ from 'lodash'
|
||||
|
||||
export function convertToReturning(obj: Record<string, string>) {
|
||||
return _.map(obj, (value, key) => _.isString(value) && `${value} as ${key}`).filter(_.identity)
|
||||
}
|
||||
|
||||
export function columnAs(name: string, table: string, transformer = _.snakeCase) {
|
||||
const transformed = transformer(name)
|
||||
|
||||
if (transformed !== name) {
|
||||
// knex automagically wraps everything in ", so they are not needed around ${name}
|
||||
return `${table ? table + '.' : ''}${transformed} as ${name}`
|
||||
}
|
||||
|
||||
return table ? `${table}.${name}` : name
|
||||
}
|
||||
|
||||
export function columnBuilder(builder: QueryBuilder, columns: Record<string, string>, columnNames: string[]) {
|
||||
for (const columnName of columnNames) {
|
||||
const column = columns[columnName]
|
||||
|
||||
if (column) {
|
||||
// @ts-ignore
|
||||
builder.column(column)
|
||||
}
|
||||
}
|
||||
|
||||
return builder
|
||||
}
|
||||
|
||||
export function where(builder: QueryBuilder, json: Record<string, ANY>) {
|
||||
return _.reduce(
|
||||
json,
|
||||
(builder, value, key) => {
|
||||
if (value === null) {
|
||||
// @ts-ignore
|
||||
return builder.whereNull(key)
|
||||
} else if (Array.isArray(value)) {
|
||||
// @ts-ignore
|
||||
return builder.whereIn(key, value)
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
return builder.where(key, value)
|
||||
},
|
||||
builder,
|
||||
)
|
||||
}
|
||||
|
||||
export const one = (result: ANY) => (result.rows ? result.rows[0] : undefined)
|
||||
|
||||
export const many = (result: ANY) => (result.rows ? result.rows : [])
|
||||
@ -1,316 +0,0 @@
|
||||
// @ts-nocheck
|
||||
import { type Knex } from 'knex'
|
||||
import _ from 'lodash'
|
||||
|
||||
import { where } from './knex_helpers.ts'
|
||||
|
||||
type QueryName =
|
||||
| 'create'
|
||||
| 'createMany'
|
||||
| 'find'
|
||||
| 'findOne'
|
||||
| 'findById'
|
||||
| 'getAll'
|
||||
| 'paginate'
|
||||
| 'remove'
|
||||
| 'removeById'
|
||||
| 'replace'
|
||||
| 'update'
|
||||
|
||||
interface Options {
|
||||
knex: Knex
|
||||
emitter?: ANY
|
||||
pick?: QueryName[]
|
||||
omit?: QueryName[]
|
||||
columns: string[]
|
||||
table: string
|
||||
selects?: Record<string, ANY>
|
||||
}
|
||||
|
||||
export const count = ({ knex, table, columns }: Pick<Options, 'knex' | 'table' | 'columns'>) =>
|
||||
function count(query: Record<string, ANY>, client = knex) {
|
||||
let builder = client.table(table)
|
||||
|
||||
if (!_.isEmpty(query)) {
|
||||
builder = where(builder, _.pick(query, columns))
|
||||
}
|
||||
|
||||
return builder.count().then((count) => parseInt(count[0].count, 10))
|
||||
}
|
||||
|
||||
export const create = ({ knex, table, columns, emitter }: Pick<Options, 'knex' | 'table' | 'columns' | 'emitter'>) =>
|
||||
function create(json, client = knex) {
|
||||
return client
|
||||
.table(table)
|
||||
.insert(_.pickBy(json, (value, key) => value !== undefined && columns.includes(key)))
|
||||
.returning('*')
|
||||
.then((result) => {
|
||||
if (emitter) {
|
||||
emitter.emit('db', { table, action: 'create', result })
|
||||
}
|
||||
|
||||
return result[0]
|
||||
})
|
||||
}
|
||||
|
||||
export const createMany = ({
|
||||
knex,
|
||||
table,
|
||||
columns,
|
||||
emitter,
|
||||
}: Pick<Options, 'knex' | 'table' | 'columns' | 'emitter'>) =>
|
||||
function createMany(collection, client = knex) {
|
||||
if (!Array.isArray(collection)) {
|
||||
collection = [collection]
|
||||
}
|
||||
|
||||
const values = collection.map((item) =>
|
||||
_.pickBy(item, (value, key) => value !== undefined && columns.includes(key)),
|
||||
)
|
||||
|
||||
return client
|
||||
.table(table)
|
||||
.insert(values)
|
||||
.returning('*')
|
||||
.then((result) => {
|
||||
if (emitter) {
|
||||
emitter.emit('db', { table, action: 'create', result })
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
export const find = ({
|
||||
knex,
|
||||
table,
|
||||
columns,
|
||||
allSelects,
|
||||
defaults = {},
|
||||
}: Pick<Options, 'knex' | 'table' | 'columns'> & { allSelects: string[]; defaults: Record<string, any> }) =>
|
||||
function find(json, select, client: Knex) {
|
||||
if (!client) {
|
||||
// TODO figure out if better, eg instanceof, check is possible
|
||||
if (select && select.andWhereNotBetween) {
|
||||
client = select
|
||||
select = null
|
||||
} else {
|
||||
client = knex
|
||||
}
|
||||
}
|
||||
|
||||
const { offset = defaults.offset, limit = defaults.limit, sort = defaults.sort, ...query } = json
|
||||
|
||||
let builder = client.table(table).select(select ? _.pick(allSelects, select) : allSelects)
|
||||
|
||||
if (!_.isEmpty(query)) {
|
||||
builder = where(builder, _.pick(query, columns))
|
||||
}
|
||||
|
||||
builder = builder.orderBy(sort || 'id', sort?.startsWith('-') ? 'desc' : 'asc')
|
||||
|
||||
if (limit) {
|
||||
builder = builder.limit(limit)
|
||||
}
|
||||
|
||||
if (offset) {
|
||||
builder = builder.offset(offset)
|
||||
}
|
||||
|
||||
return builder
|
||||
}
|
||||
|
||||
export const findById = ({ knex, table, allSelects }: Pick<Options, 'knex' | 'table'> & { allSelects: string[] }) =>
|
||||
function findById(id: number | string, select, client: Knex) {
|
||||
if (!client) {
|
||||
// TODO figure out if better, eg instanceof, check is possible
|
||||
if (select && select.andWhereNotBetween) {
|
||||
client = select
|
||||
select = null
|
||||
} else {
|
||||
client = knex
|
||||
}
|
||||
}
|
||||
|
||||
return client
|
||||
.table(table)
|
||||
.first(select ? _.pick(allSelects, select) : allSelects)
|
||||
.where('id', id)
|
||||
}
|
||||
|
||||
export const findOne = ({
|
||||
knex,
|
||||
table,
|
||||
columns,
|
||||
allSelects,
|
||||
}: Pick<Options, 'knex' | 'table' | 'columns'> & { allSelects: string[] }) =>
|
||||
function findOne(query, select, client: Knex) {
|
||||
if (!client) {
|
||||
// TODO figure out if better, eg instanceof, check is possible
|
||||
if (select && select.andWhereNotBetween) {
|
||||
client = select
|
||||
select = null
|
||||
} else {
|
||||
client = knex
|
||||
}
|
||||
}
|
||||
|
||||
let builder = client.table(table).first(select ? _.pick(allSelects, select) : allSelects)
|
||||
|
||||
if (!_.isEmpty(query)) {
|
||||
builder = where(builder, _.pick(query, columns))
|
||||
}
|
||||
|
||||
if (query?.sort) {
|
||||
builder = builder.orderBy(query.sort)
|
||||
}
|
||||
|
||||
return builder
|
||||
}
|
||||
|
||||
export const getAll = ({ knex, table, allSelects }: Pick<Options, 'knex' | 'table'> & { allSelects: string[] }) =>
|
||||
function getAll(select, client: Knex) {
|
||||
if (!client) {
|
||||
// TODO figure out if better, eg instanceof, check is possible
|
||||
if (select && select.andWhereNotBetween) {
|
||||
client = select
|
||||
select = null
|
||||
} else {
|
||||
client = knex
|
||||
}
|
||||
}
|
||||
|
||||
return client.table(table).select(select ? _.pick(allSelects, select) : allSelects)
|
||||
}
|
||||
|
||||
export const remove = ({ knex, table, columns, emitter }: Pick<Options, 'knex' | 'table' | 'columns' | 'emitter'>) =>
|
||||
function remove(query, client = knex) {
|
||||
let builder = client.table(table).delete()
|
||||
|
||||
if (!_.isEmpty(query)) {
|
||||
builder = where(builder, _.pick(query, columns))
|
||||
}
|
||||
|
||||
return builder.then((result) => {
|
||||
if (emitter) {
|
||||
emitter.emit('db', { table, action: 'delete', result })
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
export const removeById = ({ knex, table, emitter }: Pick<Options, 'knex' | 'table' | 'emitter'>) =>
|
||||
function removeById(id: number | string, client = knex) {
|
||||
return client
|
||||
.table(table)
|
||||
.delete()
|
||||
.where('id', id)
|
||||
.then((result) => {
|
||||
if (emitter) {
|
||||
emitter.emit('db', { table, action: 'delete', result })
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
// should be used with PUT
|
||||
export const replace = ({ knex, table, columns, emitter }: Pick<Options, 'knex' | 'table' | 'columns' | 'emitter'>) =>
|
||||
function replace(id: number | string, object, client = knex) {
|
||||
object = columns.reduce((result, key) => {
|
||||
const value = object[key]
|
||||
|
||||
result[columns[key]] = value === undefined ? null : value
|
||||
|
||||
return result
|
||||
}, {})
|
||||
|
||||
return client
|
||||
.table(table)
|
||||
.update(object)
|
||||
.where('id', id)
|
||||
.returning('*')
|
||||
.then((result) => {
|
||||
if (result.length === 0) return null
|
||||
|
||||
if (emitter) {
|
||||
emitter.emit('db', { table, action: 'update', result })
|
||||
}
|
||||
|
||||
return result[0]
|
||||
})
|
||||
}
|
||||
|
||||
// should be used with PATCH
|
||||
export const update = ({ knex, table, columns, emitter }: Pick<Options, 'knex' | 'table' | 'columns' | 'emitter'>) =>
|
||||
function update(id: number | string, object: Record<string, any>, client = knex) {
|
||||
object = _.pickBy(object, (value, key) => value !== undefined && columns.includes(key))
|
||||
|
||||
if (columns.includes('modifiedAt')) {
|
||||
object.modifiedAt = knex.fn.now()
|
||||
}
|
||||
|
||||
return client
|
||||
.table(table)
|
||||
.update(object)
|
||||
.where('id', id)
|
||||
.returning('*')
|
||||
.then((result) => {
|
||||
if (result.length === 0) return null
|
||||
|
||||
if (emitter) {
|
||||
emitter.emit('db', { table, action: 'update', result })
|
||||
}
|
||||
|
||||
return result[0]
|
||||
})
|
||||
}
|
||||
|
||||
export const updateMany = ({ knex, table, emitter }: Pick<Options, 'knex' | 'table' | 'emitter'>) =>
|
||||
function updateMany(query, object, client = knex) {
|
||||
const builder = client.table(table).update(typeof object === 'function' ? object(knex) : object)
|
||||
|
||||
return where(builder, query)
|
||||
.returning('*')
|
||||
.then((result) => {
|
||||
if (result.length === 0) return null
|
||||
|
||||
if (emitter) {
|
||||
emitter.emit('db', { table, action: 'update', result })
|
||||
}
|
||||
|
||||
return result[0]
|
||||
})
|
||||
}
|
||||
|
||||
const factories = {
|
||||
count,
|
||||
create,
|
||||
createMany,
|
||||
find,
|
||||
findOne,
|
||||
findById,
|
||||
getAll,
|
||||
remove,
|
||||
removeById,
|
||||
replace,
|
||||
update,
|
||||
updateMany,
|
||||
}
|
||||
|
||||
const queryNames = Object.keys(factories)
|
||||
|
||||
export default function restQueriesFactory({ knex, emitter, table, columns, selects, omit, pick }: Options) {
|
||||
const allSelects = selects ? [...columns, selects] : columns
|
||||
|
||||
pick = pick || (_.difference(queryNames, omit || []) as QueryName[])
|
||||
|
||||
return pick.reduce((result, value) => {
|
||||
if (factories[value]) {
|
||||
result[value] = factories[value]({ columns, selects, allSelects, knex, emitter, table })
|
||||
}
|
||||
|
||||
return result
|
||||
}, {}) as Record<QueryName, (...args: any) => Promise<ANY>>
|
||||
}
|
||||
@ -23,10 +23,14 @@ type WhereInput<DB, TB extends keyof DB> = Partial<{
|
||||
| null
|
||||
}>
|
||||
|
||||
export function applyWhere<DB, TB extends keyof DB, O>(
|
||||
builder: SelectQueryBuilder<DB, TB, O>,
|
||||
type WhereCapable<DB, TB extends keyof DB, Self> = {
|
||||
where(lhs: ReferenceExpression<DB, TB>, op: any, rhs: any): Self
|
||||
}
|
||||
|
||||
export function applyWhere<DB, TB extends keyof DB, QB extends WhereCapable<DB, TB, QB>>(
|
||||
builder: QB,
|
||||
where: WhereInput<DB, TB>,
|
||||
): SelectQueryBuilder<DB, TB, O> {
|
||||
): QB {
|
||||
return Object.entries(where).reduce((builder, [key, value]) => {
|
||||
const column = key as ReferenceExpression<DB, TB>
|
||||
|
||||
@ -48,8 +52,8 @@ export function applyWhere<DB, TB extends keyof DB, O>(
|
||||
|
||||
interface PaginationInput<DB, TB extends keyof DB> {
|
||||
where: WhereInput<DB, TB>
|
||||
limit?: number
|
||||
offset?: number
|
||||
limit: number
|
||||
offset: number
|
||||
sort?: UnambiguousColumn<DB, TB> | `-${UnambiguousColumn<DB, TB>}`
|
||||
}
|
||||
|
||||
@ -57,7 +61,7 @@ export function applyPagination<DB, TB extends keyof DB, O>(
|
||||
builder: SelectQueryBuilder<DB, TB, O>,
|
||||
{ limit, offset, sort }: PaginationInput<DB, TB>,
|
||||
): SelectQueryBuilder<DB, TB, O> {
|
||||
let qb = builder
|
||||
let qb = builder.limit(limit).offset(offset)
|
||||
|
||||
if (sort) {
|
||||
const columnName = (sort.startsWith('-') ? sort.slice(1) : sort) as UnambiguousColumn<DB, TB>
|
||||
@ -65,14 +69,6 @@ export function applyPagination<DB, TB extends keyof DB, O>(
|
||||
qb = qb.orderBy(columnName, columnName === sort ? 'asc' : 'desc')
|
||||
}
|
||||
|
||||
if (limit !== undefined) {
|
||||
qb = qb.limit(limit)
|
||||
}
|
||||
|
||||
if (offset !== undefined) {
|
||||
qb = qb.offset(offset)
|
||||
}
|
||||
|
||||
return qb
|
||||
}
|
||||
|
||||
|
||||
27
server/lib/pino_transport_db.ts
Normal file
27
server/lib/pino_transport_db.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import build from 'pino-abstract-transport'
|
||||
|
||||
export default function dbTransport(create) {
|
||||
return build(async (source) => {
|
||||
for await (const obj of source) {
|
||||
// TODO decide how to handle lower log levels
|
||||
if (obj.level < 50) continue
|
||||
|
||||
try {
|
||||
create({
|
||||
statusCode: obj.err?.status || 500,
|
||||
type: obj.err?.type,
|
||||
message: obj.err?.message,
|
||||
details: obj.err,
|
||||
stack: obj.err?.stack,
|
||||
method: obj.req?.method,
|
||||
path: obj.req?.url,
|
||||
headers: obj.req?.headers,
|
||||
reqId: obj.reqId,
|
||||
createdAt: new Date(obj.time),
|
||||
}).catch(console.error)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
import type { FastifyPluginCallback, FastifyRequest } from 'fastify'
|
||||
import fp from 'fastify-plugin'
|
||||
import type { Selectable } from 'kysely'
|
||||
import { jsonArrayFrom } from 'kysely/helpers/postgres'
|
||||
import changePassword from './auth/routes/change_password.ts'
|
||||
import resetPassword from './auth/routes/reset_password.ts'
|
||||
@ -8,11 +9,11 @@ import logout from './auth/routes/logout.ts'
|
||||
import register from './auth/routes/register.ts'
|
||||
import verifyEmail from './auth/routes/verify_email.ts'
|
||||
|
||||
import type { User } from '../services/users/types.ts'
|
||||
import type { User } from '../../shared/types.db.ts'
|
||||
|
||||
const userPromiseSymbol = Symbol('user')
|
||||
|
||||
const serialize = (user: User) => Promise.resolve(user.id)
|
||||
const serialize = (user: Selectable<User>) => Promise.resolve(user.id)
|
||||
|
||||
const auth: FastifyPluginCallback<{ prefix?: string }> = (fastify, options, done) => {
|
||||
const prefix = options.prefix || ''
|
||||
|
||||
@ -5,6 +5,7 @@ import accounts from './api/accounts.ts'
|
||||
import admissions from './api/admissions.ts'
|
||||
import balances from './api/balances.ts'
|
||||
import entries from './api/entries.ts'
|
||||
import errors from './api/errors.ts'
|
||||
import financialYears from './api/financial_years.ts'
|
||||
import invites from './api/invites.ts'
|
||||
import invoices from './api/invoices.ts'
|
||||
@ -22,6 +23,7 @@ const apiRoutes: FastifyPluginCallbackTypebox = (fastify, _, done) => {
|
||||
fastify.register(admissions, { prefix: '/admissions' })
|
||||
fastify.register(balances, { prefix: '/balances' })
|
||||
fastify.register(entries, { prefix: '/entries' })
|
||||
fastify.register(errors, { prefix: '/errors' })
|
||||
fastify.register(financialYears, { prefix: '/financial-years' })
|
||||
fastify.register(invites, { prefix: '/invites' })
|
||||
fastify.register(invoices, { prefix: '/invoices' })
|
||||
|
||||
102
server/routes/api/errors.ts
Normal file
102
server/routes/api/errors.ts
Normal file
@ -0,0 +1,102 @@
|
||||
import _ from 'lodash'
|
||||
import * as z from 'zod'
|
||||
import type { FastifyPluginCallbackZod } from 'fastify-type-provider-zod'
|
||||
import { PaginationMetaSchema } from '../../schemas/misc.ts'
|
||||
import { ErrorSchema } from '../../schemas/db.ts'
|
||||
import { paginate, applyWhere } from '../../lib/kysely_helpers.ts'
|
||||
|
||||
const errorRoutes: FastifyPluginCallbackZod = (fastify, _options, done) => {
|
||||
const { db } = fastify
|
||||
// addParentSchema({
|
||||
// $id: 'logged-error',
|
||||
// type: 'object',
|
||||
// properties: {
|
||||
// id: { type: 'integer' },
|
||||
// statusCode: { type: 'integer' },
|
||||
// type: { type: 'string' },
|
||||
// message: { type: 'string' },
|
||||
// details: { type: ['object', 'null'] },
|
||||
// stack: { type: 'string' },
|
||||
// method: { type: 'string' },
|
||||
// path: { type: 'string' },
|
||||
// headers: { type: ['object', 'null'], additionalProperties: { type: 'string' } },
|
||||
// ip: { type: 'string' },
|
||||
// reqId: { type: 'string' },
|
||||
// createdAt: { type: 'string' },
|
||||
// },
|
||||
// })
|
||||
|
||||
// fastify.addHook('onRequest', fastify.auth)
|
||||
|
||||
fastify.route({
|
||||
method: 'GET',
|
||||
url: '/',
|
||||
schema: {
|
||||
querystring: z.object({
|
||||
statusCode: z.coerce.number(),
|
||||
sort: ErrorSchema.keyof(),
|
||||
limit: z.coerce.number().default(40),
|
||||
offset: z.coerce.number().default(0),
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
meta: PaginationMetaSchema,
|
||||
data: z.array(ErrorSchema),
|
||||
}),
|
||||
},
|
||||
},
|
||||
// @ts-ignore
|
||||
handler(request) {
|
||||
const { limit, offset, sort, ...where } = request.query
|
||||
|
||||
const baseQuery = db.selectFrom('error').selectAll()
|
||||
|
||||
return paginate(baseQuery, baseQuery, { where, limit, offset, sort })
|
||||
},
|
||||
})
|
||||
|
||||
fastify.route({
|
||||
method: 'DELETE',
|
||||
url: '/',
|
||||
schema: {
|
||||
querystring: z.object({
|
||||
statusCode: z.coerce.number().optional(),
|
||||
id: z.union([z.coerce.number(), z.array(z.coerce.number()).optional()]),
|
||||
}),
|
||||
},
|
||||
handler(request) {
|
||||
return applyWhere(db.deleteFrom('error'), request.query)
|
||||
},
|
||||
})
|
||||
|
||||
fastify.route({
|
||||
method: 'GET',
|
||||
url: '/:id',
|
||||
schema: {
|
||||
params: z.object({
|
||||
id: z.coerce.number(),
|
||||
}),
|
||||
},
|
||||
handler(request) {
|
||||
return db.selectFrom('error').selectAll().where('id', '=', request.params.id).executeTakeFirst()
|
||||
},
|
||||
})
|
||||
|
||||
fastify.route({
|
||||
method: 'DELETE',
|
||||
url: '/:id',
|
||||
schema: {
|
||||
params: z.object({
|
||||
id: z.coerce.number(),
|
||||
}),
|
||||
response: { 204: {} },
|
||||
},
|
||||
handler(request) {
|
||||
return db.deleteFrom('error').where('id', '=', request.params.id).execute()
|
||||
},
|
||||
})
|
||||
|
||||
done()
|
||||
}
|
||||
|
||||
export default errorRoutes
|
||||
@ -15,7 +15,7 @@ const invoiceRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
||||
year: z.coerce.number().optional(),
|
||||
supplierId: z.coerce.number().optional(),
|
||||
limit: z.coerce.number().default(100),
|
||||
offset: z.coerce.number().optional(),
|
||||
offset: z.coerce.number().default(0),
|
||||
sort: z.literal(['supplierId', 'i.id', 'invoiceDate', 'dueDate']).default('i.id'),
|
||||
}),
|
||||
},
|
||||
|
||||
@ -14,7 +14,7 @@ const transactionRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
||||
year: z.optional(z.coerce.number()),
|
||||
accountNumber: z.optional(z.coerce.number()),
|
||||
limit: z.coerce.number().default(100),
|
||||
offset: z.coerce.number().optional(),
|
||||
offset: z.coerce.number().default(0),
|
||||
sort: z.literal(['accountNumber', 'e.transactionDate', 't.id']).default('t.id'),
|
||||
}),
|
||||
},
|
||||
|
||||
@ -26,6 +26,21 @@ export const AccountBalanceSchema = z.object({
|
||||
outQuantity: z.number().int().nullable().optional(),
|
||||
})
|
||||
|
||||
export const ErrorSchema = z.object({
|
||||
id: z.number().int().optional(),
|
||||
statusCode: z.number().int().nullable().optional(),
|
||||
type: z.string().nullable().optional(),
|
||||
message: z.string().nullable().optional(),
|
||||
details: z.json().nullable().optional(),
|
||||
stack: z.string().nullable().optional(),
|
||||
method: z.string().nullable().optional(),
|
||||
path: z.string().nullable().optional(),
|
||||
headers: z.json().nullable().optional(),
|
||||
ip: z.string().nullable().optional(),
|
||||
reqId: z.string().nullable().optional(),
|
||||
createdAt: z.string().nullable().optional(),
|
||||
})
|
||||
|
||||
export const InviteSchema = z.object({
|
||||
id: z.number().int().optional(),
|
||||
email: z.string(),
|
||||
|
||||
8
server/schemas/misc.ts
Normal file
8
server/schemas/misc.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import * as z from 'zod'
|
||||
|
||||
export const PaginationMetaSchema = z.object({
|
||||
totalCount: z.number(),
|
||||
limit: z.number(),
|
||||
offset: z.number(),
|
||||
count: z.number(),
|
||||
})
|
||||
@ -1,27 +0,0 @@
|
||||
import type EventEmitter from 'node:events'
|
||||
import type { Knex } from 'knex'
|
||||
import RestQueriesFactory from '../../lib/knex_rest_queries.ts'
|
||||
|
||||
const columnNames = [
|
||||
'id',
|
||||
'statusCode',
|
||||
'type',
|
||||
'message',
|
||||
'details',
|
||||
'stack',
|
||||
'method',
|
||||
'path',
|
||||
'headers',
|
||||
'ip',
|
||||
'reqId',
|
||||
'createdAt',
|
||||
]
|
||||
|
||||
export default ({ emitter, knex }: { emitter: EventEmitter; knex: Knex }) => {
|
||||
return RestQueriesFactory({
|
||||
knex: knex,
|
||||
emitter: emitter,
|
||||
table: 'error',
|
||||
columns: columnNames,
|
||||
})
|
||||
}
|
||||
@ -10,7 +10,7 @@ interface Options {
|
||||
export default ({ css, preload, script, state }: Options) => html`<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Carson Admin</title>
|
||||
<title>BRF Admin</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
|
||||
Loading…
Reference in New Issue
Block a user