WIP more auth work and convert to kysely
This commit is contained in:
parent
2c18a61315
commit
04e50a3021
15
.bruno/BRF/Admissions.bru
Normal file
15
.bruno/BRF/Admissions.bru
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
meta {
|
||||||
|
name: Admissions
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{base_url}}/api/admissions
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
|
|
||||||
|
headers {
|
||||||
|
Accept: application/json
|
||||||
|
}
|
||||||
15
.bruno/BRF/Invites.bru
Normal file
15
.bruno/BRF/Invites.bru
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
meta {
|
||||||
|
name: Invites
|
||||||
|
type: http
|
||||||
|
seq: 8
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: http://localhost:4040/api/invites
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
|
|
||||||
|
headers {
|
||||||
|
Accept: application/json
|
||||||
|
}
|
||||||
15
.bruno/BRF/Roles.bru
Normal file
15
.bruno/BRF/Roles.bru
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
meta {
|
||||||
|
name: Roles
|
||||||
|
type: http
|
||||||
|
seq: 17
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{base_url}}/api/roles
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
|
|
||||||
|
headers {
|
||||||
|
Accept: application/json
|
||||||
|
}
|
||||||
15
.bruno/BRF/Users.bru
Normal file
15
.bruno/BRF/Users.bru
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
meta {
|
||||||
|
name: Users
|
||||||
|
type: http
|
||||||
|
seq: 9
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: http://localhost:4040/api/users
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
|
|
||||||
|
headers {
|
||||||
|
Accept: application/json
|
||||||
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: /api/balances
|
name: /api/balances
|
||||||
type: http
|
type: http
|
||||||
seq: 15
|
seq: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
@ -12,4 +12,5 @@ get {
|
|||||||
|
|
||||||
settings {
|
settings {
|
||||||
encodeUrl: true
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: /api/entries/:id
|
name: /api/entries/:id
|
||||||
type: http
|
type: http
|
||||||
seq: 4
|
seq: 5
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: /api/entries
|
name: /api/entries
|
||||||
type: http
|
type: http
|
||||||
seq: 3
|
seq: 4
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: /api/financial-years
|
name: /api/financial-years
|
||||||
type: http
|
type: http
|
||||||
seq: 5
|
seq: 6
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: /api/invoices/:id
|
name: /api/invoices/:id
|
||||||
type: http
|
type: http
|
||||||
seq: 8
|
seq: 10
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
|
|||||||
@ -5,11 +5,17 @@ meta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
url: {{base_url}}/api/invoices/total-amount
|
url: {{base_url}}/api/invoices/total-amount?supplier=150
|
||||||
body: none
|
body: none
|
||||||
auth: inherit
|
auth: inherit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
params:query {
|
||||||
|
supplier: 150
|
||||||
|
:
|
||||||
|
}
|
||||||
|
|
||||||
settings {
|
settings {
|
||||||
encodeUrl: true
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: /api/invoices
|
name: /api/invoices
|
||||||
type: http
|
type: http
|
||||||
seq: 8
|
seq: 9
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: /api/objects/:id
|
name: /api/objects/:id
|
||||||
type: http
|
type: http
|
||||||
seq: 6
|
seq: 7
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: /api/objects
|
name: /api/objects
|
||||||
type: http
|
type: http
|
||||||
seq: 7
|
seq: 8
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: /api/results/:year
|
name: /api/results/:year
|
||||||
type: http
|
type: http
|
||||||
seq: 12
|
seq: 14
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: /api/results
|
name: /api/results
|
||||||
type: http
|
type: http
|
||||||
seq: 11
|
seq: 12
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
|
|||||||
@ -6,10 +6,21 @@ meta {
|
|||||||
|
|
||||||
post {
|
post {
|
||||||
url: {{base_url}}/api/suppliers/merge
|
url: {{base_url}}/api/suppliers/merge
|
||||||
body: none
|
body: json
|
||||||
auth: inherit
|
auth: inherit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"ids": [ 105, 203 ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body:multipart-form {
|
||||||
|
ids: [
|
||||||
|
}
|
||||||
|
|
||||||
settings {
|
settings {
|
||||||
encodeUrl: true
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
meta {
|
meta {
|
||||||
name: /api/suppliers
|
name: /api/suppliers
|
||||||
type: http
|
type: http
|
||||||
seq: 10
|
seq: 11
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
url: {{base_url}}/
|
url: {{base_url}}/api/suppliers
|
||||||
body: none
|
body: none
|
||||||
auth: inherit
|
auth: inherit
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,21 @@
|
|||||||
meta {
|
meta {
|
||||||
name: /api/transactions
|
name: /api/transactions
|
||||||
type: http
|
type: http
|
||||||
seq: 14
|
seq: 16
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
url: {{base_url}}/api/transactions
|
url: {{base_url}}/api/transactions?year=2020&accountNumber=4800
|
||||||
body: none
|
body: none
|
||||||
auth: inherit
|
auth: inherit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
params:query {
|
||||||
|
year: 2020
|
||||||
|
accountNumber: 4800
|
||||||
|
}
|
||||||
|
|
||||||
settings {
|
settings {
|
||||||
encodeUrl: true
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,8 @@
|
|||||||
"test": "pnpm run test:client && pnpm run test:server",
|
"test": "pnpm run test:client && pnpm run test:server",
|
||||||
"test:client": "node --no-warnings --import=./client/test/jsdom_polyfills.ts --import=./client/test/register_tsx_hook.ts --test ./client/**/*.test.ts{,x}",
|
"test:client": "node --no-warnings --import=./client/test/jsdom_polyfills.ts --import=./client/test/register_tsx_hook.ts --test ./client/**/*.test.ts{,x}",
|
||||||
"test:server": "node --env-file .env.testing --no-warnings --test ./server/**/*.test.ts",
|
"test:server": "node --env-file .env.testing --no-warnings --test ./server/**/*.test.ts",
|
||||||
"types": "tsgo --skipLibCheck"
|
"types": "tsgo --skipLibCheck",
|
||||||
|
"types:tsc": "tsc --skipLibCheck"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bmp/console": "^0.1.0",
|
"@bmp/console": "^0.1.0",
|
||||||
@ -37,8 +38,10 @@
|
|||||||
"fastify": "^5.6.2",
|
"fastify": "^5.6.2",
|
||||||
"fastify-plugin": "^5.1.0",
|
"fastify-plugin": "^5.1.0",
|
||||||
"fastify-session-redis-store": "^7.1.2",
|
"fastify-session-redis-store": "^7.1.2",
|
||||||
|
"fastify-type-provider-zod": "^6.1.0",
|
||||||
"ioredis": "^5.8.2",
|
"ioredis": "^5.8.2",
|
||||||
"knex": "^3.1.0",
|
"knex": "^3.1.0",
|
||||||
|
"kysely": "^0.28.9",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lowline": "^0.4.2",
|
"lowline": "^0.4.2",
|
||||||
"mini-qs": "^0.2.0",
|
"mini-qs": "^0.2.0",
|
||||||
@ -48,7 +51,8 @@
|
|||||||
"preact": "^10.28.0",
|
"preact": "^10.28.0",
|
||||||
"preact-iso": "^2.11.0",
|
"preact-iso": "^2.11.0",
|
||||||
"preact-router": "^4.1.2",
|
"preact-router": "^4.1.2",
|
||||||
"rek": "^0.8.1"
|
"rek": "^0.8.1",
|
||||||
|
"zod": "^4.2.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.26.10",
|
"@babel/core": "^7.26.10",
|
||||||
|
|||||||
66
pnpm-lock.yaml
generated
66
pnpm-lock.yaml
generated
@ -50,12 +50,18 @@ importers:
|
|||||||
fastify-session-redis-store:
|
fastify-session-redis-store:
|
||||||
specifier: ^7.1.2
|
specifier: ^7.1.2
|
||||||
version: 7.1.2(@fastify/session@11.1.1)
|
version: 7.1.2(@fastify/session@11.1.1)
|
||||||
|
fastify-type-provider-zod:
|
||||||
|
specifier: ^6.1.0
|
||||||
|
version: 6.1.0(@fastify/swagger@9.6.1)(fastify@5.6.2)(openapi-types@12.1.3)(zod@4.2.1)
|
||||||
ioredis:
|
ioredis:
|
||||||
specifier: ^5.8.2
|
specifier: ^5.8.2
|
||||||
version: 5.8.2
|
version: 5.8.2
|
||||||
knex:
|
knex:
|
||||||
specifier: ^3.1.0
|
specifier: ^3.1.0
|
||||||
version: 3.1.0(pg@8.16.3)
|
version: 3.1.0(pg@8.16.3)
|
||||||
|
kysely:
|
||||||
|
specifier: ^0.28.9
|
||||||
|
version: 0.28.9
|
||||||
lodash:
|
lodash:
|
||||||
specifier: ^4.17.21
|
specifier: ^4.17.21
|
||||||
version: 4.17.21
|
version: 4.17.21
|
||||||
@ -86,6 +92,9 @@ importers:
|
|||||||
rek:
|
rek:
|
||||||
specifier: ^0.8.1
|
specifier: ^0.8.1
|
||||||
version: 0.8.1
|
version: 0.8.1
|
||||||
|
zod:
|
||||||
|
specifier: ^4.2.1
|
||||||
|
version: 4.2.1
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@babel/core':
|
'@babel/core':
|
||||||
specifier: ^7.26.10
|
specifier: ^7.26.10
|
||||||
@ -642,6 +651,9 @@ packages:
|
|||||||
'@fastify/static@8.3.0':
|
'@fastify/static@8.3.0':
|
||||||
resolution: {integrity: sha512-yKxviR5PH1OKNnisIzZKmgZSus0r2OZb8qCSbqmw34aolT4g3UlzYfeBRym+HJ1J471CR8e2ldNub4PubD1coA==}
|
resolution: {integrity: sha512-yKxviR5PH1OKNnisIzZKmgZSus0r2OZb8qCSbqmw34aolT4g3UlzYfeBRym+HJ1J471CR8e2ldNub4PubD1coA==}
|
||||||
|
|
||||||
|
'@fastify/swagger@9.6.1':
|
||||||
|
resolution: {integrity: sha512-fKlpJqFMWoi4H3EdUkDaMteEYRCfQMEkK0HJJ0eaf4aRlKd8cbq0pVkOfXDXmtvMTXYcnx3E+l023eFDBsA1HA==}
|
||||||
|
|
||||||
'@fastify/type-provider-typebox@6.1.0':
|
'@fastify/type-provider-typebox@6.1.0':
|
||||||
resolution: {integrity: sha512-k29cOitDRcZhMXVjtRq0+caKxdWoArz7su+dQWGzGWnFG+fSKhevgiZ7nexHWuXOEEQzgJlh6cptIMu69beaTA==}
|
resolution: {integrity: sha512-k29cOitDRcZhMXVjtRq0+caKxdWoArz7su+dQWGzGWnFG+fSKhevgiZ7nexHWuXOEEQzgJlh6cptIMu69beaTA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -1382,6 +1394,14 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@fastify/session': '>=10'
|
'@fastify/session': '>=10'
|
||||||
|
|
||||||
|
fastify-type-provider-zod@6.1.0:
|
||||||
|
resolution: {integrity: sha512-Sl19VZFSX4W/+AFl3hkL5YgWk3eDXZ4XYOdrq94HunK+o7GQBCAqgk7+3gPXoWkF0bNxOiIgfnFGJJ3i9a2BtQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@fastify/swagger': '>=9.5.1'
|
||||||
|
fastify: ^5.5.0
|
||||||
|
openapi-types: ^12.1.3
|
||||||
|
zod: '>=4.1.5'
|
||||||
|
|
||||||
fastify@5.6.2:
|
fastify@5.6.2:
|
||||||
resolution: {integrity: sha512-dPugdGnsvYkBlENLhCgX8yhyGCsCPrpA8lFWbTNU428l+YOnLgYHR69hzV8HWPC79n536EqzqQtvhtdaCE0dKg==}
|
resolution: {integrity: sha512-dPugdGnsvYkBlENLhCgX8yhyGCsCPrpA8lFWbTNU428l+YOnLgYHR69hzV8HWPC79n536EqzqQtvhtdaCE0dKg==}
|
||||||
|
|
||||||
@ -1655,6 +1675,10 @@ packages:
|
|||||||
json-schema-ref-resolver@3.0.0:
|
json-schema-ref-resolver@3.0.0:
|
||||||
resolution: {integrity: sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A==}
|
resolution: {integrity: sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A==}
|
||||||
|
|
||||||
|
json-schema-resolver@3.0.0:
|
||||||
|
resolution: {integrity: sha512-HqMnbz0tz2DaEJ3ntsqtx3ezzZyDE7G56A/pPY/NGmrPu76UzsWquOpHFRAf5beTNXoH2LU5cQePVvRli1nchA==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
json-schema-traverse@1.0.0:
|
json-schema-traverse@1.0.0:
|
||||||
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
|
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
|
||||||
|
|
||||||
@ -1694,6 +1718,10 @@ packages:
|
|||||||
kolorist@1.8.0:
|
kolorist@1.8.0:
|
||||||
resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
|
resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
|
||||||
|
|
||||||
|
kysely@0.28.9:
|
||||||
|
resolution: {integrity: sha512-3BeXMoiOhpOwu62CiVpO6lxfq4eS6KMYfQdMsN/2kUCRNuF2YiEr7u0HLHaQU+O4Xu8YXE3bHVkwaQ85i72EuA==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
|
||||||
light-my-request@6.6.0:
|
light-my-request@6.6.0:
|
||||||
resolution: {integrity: sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==}
|
resolution: {integrity: sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==}
|
||||||
|
|
||||||
@ -1818,6 +1846,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
|
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
openapi-types@12.1.3:
|
||||||
|
resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
|
||||||
|
|
||||||
oxlint@1.29.0:
|
oxlint@1.29.0:
|
||||||
resolution: {integrity: sha512-YqUVUhTYDqazV2qu3QSQn/H4Z1OP+fTnedgZWDk1/lDZxGfR0b1MqRVaEm3rRjBMLHP0zXlriIWUx+DD6UMaPA==}
|
resolution: {integrity: sha512-YqUVUhTYDqazV2qu3QSQn/H4Z1OP+fTnedgZWDk1/lDZxGfR0b1MqRVaEm3rRjBMLHP0zXlriIWUx+DD6UMaPA==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
@ -2384,6 +2415,9 @@ packages:
|
|||||||
engines: {node: '>= 14.6'}
|
engines: {node: '>= 14.6'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
zod@4.2.1:
|
||||||
|
resolution: {integrity: sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==}
|
||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
|
|
||||||
'@acemir/cssom@0.9.23': {}
|
'@acemir/cssom@0.9.23': {}
|
||||||
@ -2785,6 +2819,16 @@ snapshots:
|
|||||||
fastq: 1.19.1
|
fastq: 1.19.1
|
||||||
glob: 11.1.0
|
glob: 11.1.0
|
||||||
|
|
||||||
|
'@fastify/swagger@9.6.1':
|
||||||
|
dependencies:
|
||||||
|
fastify-plugin: 5.1.0
|
||||||
|
json-schema-resolver: 3.0.0
|
||||||
|
openapi-types: 12.1.3
|
||||||
|
rfdc: 1.4.1
|
||||||
|
yaml: 2.8.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
'@fastify/type-provider-typebox@6.1.0(typebox@1.0.55)':
|
'@fastify/type-provider-typebox@6.1.0(typebox@1.0.55)':
|
||||||
dependencies:
|
dependencies:
|
||||||
typebox: 1.0.55
|
typebox: 1.0.55
|
||||||
@ -3487,6 +3531,14 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@fastify/session': 11.1.1
|
'@fastify/session': 11.1.1
|
||||||
|
|
||||||
|
fastify-type-provider-zod@6.1.0(@fastify/swagger@9.6.1)(fastify@5.6.2)(openapi-types@12.1.3)(zod@4.2.1):
|
||||||
|
dependencies:
|
||||||
|
'@fastify/error': 4.2.0
|
||||||
|
'@fastify/swagger': 9.6.1
|
||||||
|
fastify: 5.6.2
|
||||||
|
openapi-types: 12.1.3
|
||||||
|
zod: 4.2.1
|
||||||
|
|
||||||
fastify@5.6.2:
|
fastify@5.6.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@fastify/ajv-compiler': 4.0.5
|
'@fastify/ajv-compiler': 4.0.5
|
||||||
@ -3790,6 +3842,14 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
dequal: 2.0.3
|
dequal: 2.0.3
|
||||||
|
|
||||||
|
json-schema-resolver@3.0.0:
|
||||||
|
dependencies:
|
||||||
|
debug: 4.4.3
|
||||||
|
fast-uri: 3.1.0
|
||||||
|
rfdc: 1.4.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
json-schema-traverse@1.0.0: {}
|
json-schema-traverse@1.0.0: {}
|
||||||
|
|
||||||
json5@2.2.3: {}
|
json5@2.2.3: {}
|
||||||
@ -3817,6 +3877,8 @@ snapshots:
|
|||||||
|
|
||||||
kolorist@1.8.0: {}
|
kolorist@1.8.0: {}
|
||||||
|
|
||||||
|
kysely@0.28.9: {}
|
||||||
|
|
||||||
light-my-request@6.6.0:
|
light-my-request@6.6.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
cookie: 1.0.2
|
cookie: 1.0.2
|
||||||
@ -3937,6 +3999,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
mimic-function: 5.0.1
|
mimic-function: 5.0.1
|
||||||
|
|
||||||
|
openapi-types@12.1.3: {}
|
||||||
|
|
||||||
oxlint@1.29.0:
|
oxlint@1.29.0:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@oxlint/darwin-arm64': 1.29.0
|
'@oxlint/darwin-arm64': 1.29.0
|
||||||
@ -4470,3 +4534,5 @@ snapshots:
|
|||||||
yallist@3.1.1: {}
|
yallist@3.1.1: {}
|
||||||
|
|
||||||
yaml@2.8.1: {}
|
yaml@2.8.1: {}
|
||||||
|
|
||||||
|
zod@4.2.1: {}
|
||||||
|
|||||||
17
server/lib/kysely.ts
Normal file
17
server/lib/kysely.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// @ts-ignore
|
||||||
|
import { Pool } from 'pg'
|
||||||
|
import { Kysely, PostgresDialect } from 'kysely'
|
||||||
|
import env from '../env.ts'
|
||||||
|
import type { DB } from '../../shared/types.db.ts'
|
||||||
|
|
||||||
|
const dialect = new PostgresDialect({
|
||||||
|
pool: new Pool({
|
||||||
|
database: env.PGDATABASE,
|
||||||
|
host: env.PGHOST,
|
||||||
|
password: env.PGPASSWORD,
|
||||||
|
user: env.PGUSER,
|
||||||
|
port: env.PGPORT,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
export default new Kysely<DB>({ dialect })
|
||||||
47
server/lib/kysely_helpers.ts
Normal file
47
server/lib/kysely_helpers.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// import type { QueryBuilder } from 'knex'
|
||||||
|
import type { SelectQueryBuilder } from 'kysely'
|
||||||
|
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<DB, TB extends keyof DB, O>(builder: SelectQueryBuilder<DB, TB, O>, json: Record<string, ANY>) {
|
||||||
|
return _.reduce(
|
||||||
|
json,
|
||||||
|
(builder, value, key) => {
|
||||||
|
if (value === null) {
|
||||||
|
return builder.where(key, 'is', null)
|
||||||
|
} else if (Array.isArray(value)) {
|
||||||
|
return builder.where(key, 'in', value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.where(key, '=', value)
|
||||||
|
},
|
||||||
|
builder,
|
||||||
|
)
|
||||||
|
}
|
||||||
78
server/lib/kysely_queries.ts
Normal file
78
server/lib/kysely_queries.ts
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
// import { type Knex } from 'kysely'
|
||||||
|
// import _ from 'lodash'
|
||||||
|
|
||||||
|
// import { where } from './kysely_helpers.ts'
|
||||||
|
|
||||||
|
// type QueryName =
|
||||||
|
// | 'create'
|
||||||
|
// | 'createMany'
|
||||||
|
// | 'find'
|
||||||
|
// | 'findOne'
|
||||||
|
// | 'findById'
|
||||||
|
// | 'getAll'
|
||||||
|
// | 'paginate'
|
||||||
|
// | 'remove'
|
||||||
|
// | 'removeById'
|
||||||
|
// | 'replace'
|
||||||
|
// | 'update'
|
||||||
|
|
||||||
|
// interface Options {
|
||||||
|
// kysely: Knex
|
||||||
|
// emitter?: ANY
|
||||||
|
// pick?: QueryName[]
|
||||||
|
// omit?: QueryName[]
|
||||||
|
// columns: string[]
|
||||||
|
// table: string
|
||||||
|
// selects?: Record<string, ANY>
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export const count = ({ kysely, table, columns }: Pick<Options, 'kysely' | 'table' | 'columns'>) =>
|
||||||
|
// function count(query: Record<string, ANY>, client = kysely) {
|
||||||
|
// 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 find = ({
|
||||||
|
kysely,
|
||||||
|
table,
|
||||||
|
columns,
|
||||||
|
allSelects,
|
||||||
|
defaults = {},
|
||||||
|
}: Pick<Options, 'kysely' | '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 = kysely
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
17
server/plugins/db.ts
Normal file
17
server/plugins/db.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import type { FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox'
|
||||||
|
import type { Kysely } from 'kysely'
|
||||||
|
import type { DB } from '../../shared/types.db.ts'
|
||||||
|
import fp from 'fastify-plugin'
|
||||||
|
|
||||||
|
const dbPlugin: FastifyPluginCallbackTypebox<{ kysely: Kysely<DB> }> = (fastify, { kysely }, done) => {
|
||||||
|
fastify.decorate('db', kysely)
|
||||||
|
|
||||||
|
fastify.addHook('onClose', () => kysely.destroy())
|
||||||
|
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
|
||||||
|
export default fp(dbPlugin, {
|
||||||
|
fastify: '5.x',
|
||||||
|
name: 'dbPlugin',
|
||||||
|
})
|
||||||
@ -10,6 +10,7 @@ import journals from './api/journals.ts'
|
|||||||
import objects from './api/objects.ts'
|
import objects from './api/objects.ts'
|
||||||
import process from './api/process.ts'
|
import process from './api/process.ts'
|
||||||
import results from './api/results.ts'
|
import results from './api/results.ts'
|
||||||
|
import roles from './api/roles.ts'
|
||||||
import suppliers from './api/suppliers.ts'
|
import suppliers from './api/suppliers.ts'
|
||||||
import transactions from './api/transactions.ts'
|
import transactions from './api/transactions.ts'
|
||||||
|
|
||||||
@ -23,6 +24,7 @@ const apiRoutes: FastifyPluginCallbackTypebox = (fastify, _, done) => {
|
|||||||
fastify.register(objects, { prefix: '/objects' })
|
fastify.register(objects, { prefix: '/objects' })
|
||||||
fastify.register(process, { prefix: '/process' })
|
fastify.register(process, { prefix: '/process' })
|
||||||
fastify.register(results, { prefix: '/results' })
|
fastify.register(results, { prefix: '/results' })
|
||||||
|
fastify.register(roles, { prefix: '/roles' })
|
||||||
fastify.register(suppliers, { prefix: '/suppliers' })
|
fastify.register(suppliers, { prefix: '/suppliers' })
|
||||||
fastify.register(transactions, { prefix: '/transactions' })
|
fastify.register(transactions, { prefix: '/transactions' })
|
||||||
|
|
||||||
|
|||||||
@ -1,102 +1,151 @@
|
|||||||
import { Type, type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox'
|
import _ from 'lodash'
|
||||||
import knex from '../../lib/knex.ts'
|
import * as z from 'zod'
|
||||||
import Queries from '../../services/roles/queries.ts'
|
import { type FastifyPluginCallbackZod } from 'fastify-type-provider-zod'
|
||||||
|
// import knex from '../../lib/knex.ts'
|
||||||
|
// import Queries from '../../services/roles/queries.ts'
|
||||||
|
|
||||||
export const RoleFullSchema = Type.Object({
|
import { RoleSchema } from '../../schemas/db.ts'
|
||||||
id: Type.Number(),
|
|
||||||
name: Type.String(),
|
|
||||||
createdAt: Type.String(),
|
|
||||||
createdById: Type.Number(),
|
|
||||||
modifiedAt: Type.String(),
|
|
||||||
modifiedById: Type.Number(),
|
|
||||||
})
|
|
||||||
|
|
||||||
export const RoleSchema = Type.Pick(RoleFullSchema, ['id', 'name'])
|
// export const RoleFullSchema = Type.Object({
|
||||||
|
// id: Type.Number(),
|
||||||
|
// name: Type.String(),
|
||||||
|
// createdAt: Type.String(),
|
||||||
|
// createdById: Type.Number(),
|
||||||
|
// modifiedAt: Type.String(),
|
||||||
|
// modifiedById: Type.Number(),
|
||||||
|
// })
|
||||||
|
|
||||||
export const RoleVariableSchema = Type.Pick(RoleFullSchema, ['name', 'createdById'])
|
// console.log(RoleFullSchema)
|
||||||
|
|
||||||
const rolesPlugin: FastifyPluginCallbackTypebox = (fastify, _options, done) => {
|
// export const RoleSchema = Type.Pick(RoleFullSchema, ['id', 'name'])
|
||||||
const queries = Queries({ knex })
|
|
||||||
|
|
||||||
fastify.addHook('onRequest', fastify.auth)
|
// export const RoleVariableSchema = Type.Pick(RoleFullSchema, ['name', 'createdById'])
|
||||||
|
|
||||||
|
const rolesPlugin: FastifyPluginCallbackZod = (fastify, _options, done) => {
|
||||||
|
// const queries = Queries({ knex })
|
||||||
|
const { db } = fastify
|
||||||
|
|
||||||
|
// fastify.addHook('onRequest', fastify.auth)
|
||||||
|
|
||||||
fastify.route({
|
fastify.route({
|
||||||
url: '/',
|
url: '/',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
schema: {
|
schema: {
|
||||||
querystring: RoleFullSchema,
|
querystring: RoleSchema.partial().extend({
|
||||||
|
limit: z.number().optional(),
|
||||||
|
sort: z.keyof(RoleSchema).optional(),
|
||||||
|
offset: z.number().optional(),
|
||||||
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: Type.Array(RoleFullSchema),
|
200: z.array(RoleSchema),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
handler(request) {
|
handler(request) {
|
||||||
return queries.find(request.query)
|
// if (!client) {
|
||||||
|
// // TODO figure out if better, eg instanceof, check is possible
|
||||||
|
// if (select && select.andWhereNotBetween) {
|
||||||
|
// client = select
|
||||||
|
// select = null
|
||||||
|
// } else {
|
||||||
|
// client = kysely
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
const { offset, limit, sort, ...query } = request.query
|
||||||
|
|
||||||
|
let builder = db.selectFrom('role').selectAll()
|
||||||
|
|
||||||
|
// if (!_.isEmpty(query)) {
|
||||||
|
// builder = where(builder, _.pick(query, columns))
|
||||||
|
// }
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(query)) {
|
||||||
|
if (value === null) {
|
||||||
|
builder = builder.where(key, 'is', null)
|
||||||
|
} else if (Array.isArray(value)) {
|
||||||
|
builder = builder.where(key, 'in', value)
|
||||||
|
} else {
|
||||||
|
builder = builder.where(key, '=', value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder = builder.orderBy(sort || 'id', sort?.startsWith('-') ? 'desc' : 'asc')
|
||||||
|
|
||||||
|
if (limit) {
|
||||||
|
builder = builder.limit(limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset) {
|
||||||
|
builder = builder.offset(offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.execute()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
fastify.route({
|
// fastify.route({
|
||||||
url: '/',
|
// url: '/',
|
||||||
method: 'POST',
|
// method: 'POST',
|
||||||
schema: {
|
// schema: {
|
||||||
body: RoleVariableSchema,
|
// body: RoleVariableSchema,
|
||||||
response: {
|
// response: {
|
||||||
201: RoleFullSchema,
|
// 201: RoleFullSchema,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
async handler(request, reply) {
|
// async handler(request, reply) {
|
||||||
const newRole = request.session.userId
|
// const newRole = request.session.userId
|
||||||
? {
|
// ? {
|
||||||
...request.body,
|
// ...request.body,
|
||||||
createdById: request.session.userId,
|
// createdById: request.session.userId,
|
||||||
}
|
// }
|
||||||
: request.body
|
// : request.body
|
||||||
|
|
||||||
return queries.create(newRole).then((row) => {
|
// return queries.create(newRole).then((row) => {
|
||||||
return reply.header('Location', `${request.url}/${row.id}`).status(201).send(row)
|
// return reply.header('Location', `${request.url}/${row.id}`).status(201).send(row)
|
||||||
})
|
// })
|
||||||
},
|
// },
|
||||||
})
|
// })
|
||||||
|
|
||||||
fastify.route({
|
// fastify.route({
|
||||||
url: '/:id',
|
// url: '/:id',
|
||||||
method: 'DELETE',
|
// method: 'DELETE',
|
||||||
schema: {
|
// schema: {
|
||||||
params: Type.Object({
|
// params: Type.Object({
|
||||||
id: Type.Number(),
|
// id: Type.Number(),
|
||||||
}),
|
// }),
|
||||||
response: {
|
// response: {
|
||||||
204: {},
|
// 204: {},
|
||||||
404: {},
|
// 404: {},
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
handler(request, reply) {
|
// handler(request, reply) {
|
||||||
return queries.removeById(request.params.id).then((count) => reply.status(count > 0 ? 204 : 404).send())
|
// return queries.removeById(request.params.id).then((count) => reply.status(count > 0 ? 204 : 404).send())
|
||||||
},
|
// },
|
||||||
})
|
// })
|
||||||
|
|
||||||
fastify.route({
|
// fastify.route({
|
||||||
url: '/:id',
|
// url: '/:id',
|
||||||
method: 'PATCH',
|
// method: 'PATCH',
|
||||||
schema: {
|
// schema: {
|
||||||
params: Type.Object({
|
// params: Type.Object({
|
||||||
id: Type.Number(),
|
// id: Type.Number(),
|
||||||
}),
|
// }),
|
||||||
body: RoleVariableSchema,
|
// body: RoleVariableSchema,
|
||||||
response: {
|
// response: {
|
||||||
204: {},
|
// 204: {},
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
async handler(request) {
|
// async handler(request) {
|
||||||
const patch = request.session.userId
|
// const patch = request.session.userId
|
||||||
? {
|
// ? {
|
||||||
...request.body,
|
// ...request.body,
|
||||||
modifiedById: request.session.userId,
|
// modifiedById: request.session.userId,
|
||||||
}
|
// }
|
||||||
: request.body
|
// : request.body
|
||||||
|
|
||||||
return queries.update(request.params.id, patch)
|
// return queries.update(request.params.id, patch)
|
||||||
},
|
// },
|
||||||
})
|
// })
|
||||||
|
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,21 @@
|
|||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import { Type, type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox'
|
import type { FastifyPluginCallbackZod } from 'fastify-type-provider-zod'
|
||||||
import knex from '../../lib/knex.ts'
|
import z from 'zod'
|
||||||
|
import { SupplierSchema } from '../../schemas/db.ts'
|
||||||
|
|
||||||
|
const supplierRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
||||||
|
const { db } = fastify
|
||||||
|
|
||||||
const journalRoutes: FastifyPluginCallbackTypebox = (fastify, _, done) => {
|
|
||||||
fastify.route({
|
fastify.route({
|
||||||
url: '/',
|
url: '/',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
async handler() {
|
schema: {
|
||||||
return knex('supplier').select('*').orderBy('name')
|
response: {
|
||||||
|
200: z.array(SupplierSchema),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
handler() {
|
||||||
|
return db.selectFrom('supplier').selectAll().orderBy('name').execute()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -15,12 +23,13 @@ const journalRoutes: FastifyPluginCallbackTypebox = (fastify, _, done) => {
|
|||||||
url: '/:id',
|
url: '/:id',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
schema: {
|
schema: {
|
||||||
params: Type.Object({
|
params: z.object({ id: z.number() }),
|
||||||
id: Type.Number(),
|
response: {
|
||||||
}),
|
200: SupplierSchema,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
async handler(req) {
|
handler(req) {
|
||||||
return knex('supplier').first('*').where('id', req.params.id)
|
return db.selectFrom('supplier').selectAll().where('id', '=', req.params.id).executeTakeFirst()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -28,20 +37,23 @@ const journalRoutes: FastifyPluginCallbackTypebox = (fastify, _, done) => {
|
|||||||
url: '/merge',
|
url: '/merge',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
schema: {
|
schema: {
|
||||||
body: Type.Object({
|
body: z.object({
|
||||||
ids: Type.Array(Type.Number()),
|
ids: z.array(z.number()),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
async handler(req) {
|
async handler(req) {
|
||||||
const suppliers = await knex('supplier').select('*').whereIn('id', req.body.ids)
|
const suppliers = await db.selectFrom('supplier').selectAll().where('id', 'in', req.body.ids).execute()
|
||||||
|
|
||||||
const trx = await knex.transaction()
|
const trx = await db.startTransaction().execute()
|
||||||
|
|
||||||
await trx('invoice').update('supplierId', req.body.ids[0]).whereIn('supplierId', req.body.ids.slice(1))
|
await trx
|
||||||
await trx('supplier').delete().whereIn('id', req.body.ids.slice(1))
|
.updateTable('invoice')
|
||||||
// 556744-4301
|
.set('supplierId', req.body.ids[0])
|
||||||
|
.where('supplierId', 'in', req.body.ids.slice(1))
|
||||||
|
.execute()
|
||||||
|
await trx.deleteFrom('supplier').where('id', 'in', req.body.ids.slice(1)).execute()
|
||||||
|
|
||||||
trx.commit()
|
await trx.commit().execute()
|
||||||
|
|
||||||
return suppliers
|
return suppliers
|
||||||
},
|
},
|
||||||
@ -50,4 +62,4 @@ const journalRoutes: FastifyPluginCallbackTypebox = (fastify, _, done) => {
|
|||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
|
|
||||||
export default journalRoutes
|
export default supplierRoutes
|
||||||
|
|||||||
@ -1,25 +1,32 @@
|
|||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import { Type, type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox'
|
import * as z from 'zod'
|
||||||
import knex from '../../lib/knex.ts'
|
import type { FastifyPluginCallbackZod } from 'fastify-type-provider-zod'
|
||||||
import StatusError from '../../lib/status_error.ts'
|
import StatusError from '../../lib/status_error.ts'
|
||||||
|
|
||||||
const transactionRoutes: FastifyPluginCallbackTypebox = (fastify, _, done) => {
|
const transactionRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
||||||
|
const { db } = fastify
|
||||||
|
|
||||||
fastify.route({
|
fastify.route({
|
||||||
url: '/',
|
url: '/',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
schema: {
|
schema: {
|
||||||
querystring: Type.Object({
|
querystring: z.object({
|
||||||
year: Type.Optional(Type.Number()),
|
year: z.optional(z.coerce.number()),
|
||||||
accountNumber: Type.Optional(Type.Number()),
|
accountNumber: z.optional(z.coerce.number()),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
async handler(req) {
|
async handler(req) {
|
||||||
const query: { financialYearId?: number; accountNumber?: number } = {}
|
const query: { financialYearId?: number; accountNumber?: number } = {}
|
||||||
|
|
||||||
if (req.query.year) {
|
if (req.query.year) {
|
||||||
const year = await knex('financialYear').first('*').where('year', req.query.year)
|
const year = await db
|
||||||
|
.selectFrom('financialYear')
|
||||||
|
.selectAll()
|
||||||
|
.where('year', '=', req.query.year)
|
||||||
|
.executeTakeFirst()
|
||||||
|
|
||||||
if (!year) throw new StatusError(404, `Year ${req.query.year} not found.`)
|
if (!year) throw new StatusError(404, `Year ${req.query.year} not found.`)
|
||||||
|
|
||||||
query.financialYearId = year.id
|
query.financialYearId = year.id
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,18 +34,20 @@ const transactionRoutes: FastifyPluginCallbackTypebox = (fastify, _, done) => {
|
|||||||
query.accountNumber = req.query.accountNumber
|
query.accountNumber = req.query.accountNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
return knex('transaction AS t')
|
return db
|
||||||
.select(
|
.selectFrom('transaction as t')
|
||||||
|
.innerJoin('entry as e', 't.entryId', 'e.id')
|
||||||
|
.select([
|
||||||
't.accountNumber',
|
't.accountNumber',
|
||||||
'e.transactionDate',
|
'e.transactionDate',
|
||||||
't.entryId',
|
't.entryId',
|
||||||
't.amount',
|
't.amount',
|
||||||
't.description',
|
't.description',
|
||||||
't.invoiceId',
|
't.invoiceId',
|
||||||
'e.description AS entryDescription',
|
'e.description as entryDescription',
|
||||||
)
|
])
|
||||||
.innerJoin('entry AS e', 't.entryId', 'e.id')
|
.where((eb) => eb.and(query))
|
||||||
.where(query)
|
.execute()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -46,12 +55,12 @@ const transactionRoutes: FastifyPluginCallbackTypebox = (fastify, _, done) => {
|
|||||||
url: '/:id',
|
url: '/:id',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
schema: {
|
schema: {
|
||||||
params: Type.Object({
|
params: z.object({
|
||||||
id: Type.Number(),
|
id: z.number(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
async handler(req) {
|
handler(req) {
|
||||||
return knex('transaction').first('*').where('id', req.params.id)
|
return db.selectFrom('transaction').selectAll().where('id', '=', req.params.id).execute()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
22
server/schemas/db.ts
Normal file
22
server/schemas/db.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { z } from 'zod'
|
||||||
|
|
||||||
|
export const RoleSchema = z.object({
|
||||||
|
id: z.number().int().optional(),
|
||||||
|
name: z.string(),
|
||||||
|
createdAt: z.string().optional(),
|
||||||
|
createdById: z.number().int().nullable().optional(),
|
||||||
|
modifiedAt: z.string().nullable().optional(),
|
||||||
|
modifiedById: z.number().int().nullable().optional(),
|
||||||
|
})
|
||||||
|
|
||||||
|
export const SupplierSchema = z.object({
|
||||||
|
id: z.number().int().optional(),
|
||||||
|
name: z.string().nullable().optional(),
|
||||||
|
supplierTypeId: z.number().int(),
|
||||||
|
taxId: z.string().nullable().optional(),
|
||||||
|
})
|
||||||
|
|
||||||
|
export const SupplierTypeSchema = z.object({
|
||||||
|
id: z.number().int().optional(),
|
||||||
|
name: z.string(),
|
||||||
|
})
|
||||||
@ -3,12 +3,15 @@ import fcookie from '@fastify/cookie'
|
|||||||
import fsession from '@fastify/session'
|
import fsession from '@fastify/session'
|
||||||
import fstatic from '@fastify/static'
|
import fstatic from '@fastify/static'
|
||||||
import RedisStore from 'fastify-session-redis-store'
|
import RedisStore from 'fastify-session-redis-store'
|
||||||
|
import { serializerCompiler, validatorCompiler } from 'fastify-type-provider-zod'
|
||||||
import { Redis } from 'ioredis'
|
import { Redis } from 'ioredis'
|
||||||
|
import kysely from './lib/kysely.ts'
|
||||||
|
|
||||||
import StatusError from './lib/status_error.ts'
|
import StatusError from './lib/status_error.ts'
|
||||||
import env from './env.ts'
|
import env from './env.ts'
|
||||||
import ErrorHandler from './handlers/error.ts'
|
import ErrorHandler from './handlers/error.ts'
|
||||||
import authPlugin from './plugins/auth.ts'
|
import authPlugin from './plugins/auth.ts'
|
||||||
|
import dbPlugin from './plugins/db.ts'
|
||||||
import vitePlugin from './plugins/vite.ts'
|
import vitePlugin from './plugins/vite.ts'
|
||||||
import apiRoutes from './routes/api.ts'
|
import apiRoutes from './routes/api.ts'
|
||||||
import templateAdmin from './templates/admin.ts'
|
import templateAdmin from './templates/admin.ts'
|
||||||
@ -17,6 +20,9 @@ import templatePublic from './templates/public.ts'
|
|||||||
export default async (options?: FastifyServerOptions) => {
|
export default async (options?: FastifyServerOptions) => {
|
||||||
const server = fastify(options)
|
const server = fastify(options)
|
||||||
|
|
||||||
|
server.setValidatorCompiler(validatorCompiler)
|
||||||
|
server.setSerializerCompiler(serializerCompiler)
|
||||||
|
|
||||||
server.register(fcookie)
|
server.register(fcookie)
|
||||||
server.register(fsession, {
|
server.register(fsession, {
|
||||||
cookie: {
|
cookie: {
|
||||||
@ -27,6 +33,7 @@ export default async (options?: FastifyServerOptions) => {
|
|||||||
secret: env.SESSION_SECRET,
|
secret: env.SESSION_SECRET,
|
||||||
store: env.NODE_ENV !== 'testing' ? new RedisStore({ client: new Redis(env.REDIS_HOST) }) : undefined,
|
store: env.NODE_ENV !== 'testing' ? new RedisStore({ client: new Redis(env.REDIS_HOST) }) : undefined,
|
||||||
})
|
})
|
||||||
|
server.register(dbPlugin, { kysely })
|
||||||
|
|
||||||
server.setNotFoundHandler(() => {
|
server.setNotFoundHandler(() => {
|
||||||
throw new StatusError(404)
|
throw new StatusError(404)
|
||||||
|
|||||||
21
server/tests/suppliers.test.ts
Normal file
21
server/tests/suppliers.test.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { test, type TestContext } from 'node:test'
|
||||||
|
|
||||||
|
import supplierPlugin from '../routes/api/suppliers.ts'
|
||||||
|
import fastify from 'fastify'
|
||||||
|
|
||||||
|
test('/api/suppliers', async (t: TestContext) => {
|
||||||
|
const server = fastify()
|
||||||
|
|
||||||
|
// server.decorate('auth', (_request, _reply, done) => done())
|
||||||
|
|
||||||
|
server.register(supplierPlugin, { prefix: '/api/suppliers' })
|
||||||
|
|
||||||
|
const res = await server.inject({
|
||||||
|
method: 'GET',
|
||||||
|
url: '/api/suppliers',
|
||||||
|
})
|
||||||
|
|
||||||
|
t.assert.equal(res.statusCode, 200)
|
||||||
|
|
||||||
|
await server.close()
|
||||||
|
})
|
||||||
3
shared/global.d.ts
vendored
3
shared/global.d.ts
vendored
@ -1,5 +1,7 @@
|
|||||||
import 'fastify'
|
import 'fastify'
|
||||||
import type { onRequestHookHandler } from 'fastify'
|
import type { onRequestHookHandler } from 'fastify'
|
||||||
|
import { DB } from './types.db.ts'
|
||||||
|
import type { Kysely } from 'kysely'
|
||||||
|
|
||||||
import { ViteDevServer } from 'vite'
|
import { ViteDevServer } from 'vite'
|
||||||
|
|
||||||
@ -16,6 +18,7 @@ declare module 'fastify' {
|
|||||||
|
|
||||||
interface FastifyInstance {
|
interface FastifyInstance {
|
||||||
auth: onRequestHookHandler
|
auth: onRequestHookHandler
|
||||||
|
db: Kysely<DB>
|
||||||
devServer: ViteDevServer
|
devServer: ViteDevServer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
261
shared/types.db.ts
Normal file
261
shared/types.db.ts
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
/**
|
||||||
|
* This file was generated by kysely-codegen.
|
||||||
|
* Please do not edit it manually.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { ColumnType } from 'kysely'
|
||||||
|
|
||||||
|
export type Generated<T> =
|
||||||
|
T extends ColumnType<infer S, infer I, infer U> ? ColumnType<S, I | undefined, U> : ColumnType<T, T | undefined, T>
|
||||||
|
|
||||||
|
export type Json = JsonValue
|
||||||
|
|
||||||
|
export type JsonArray = JsonValue[]
|
||||||
|
|
||||||
|
export type JsonObject = {
|
||||||
|
[x: string]: JsonValue | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export type JsonPrimitive = boolean | number | string | null
|
||||||
|
|
||||||
|
export type JsonValue = JsonArray | JsonObject | JsonPrimitive
|
||||||
|
|
||||||
|
export type Numeric = ColumnType<string, number | string, number | string>
|
||||||
|
|
||||||
|
export type Timestamp = ColumnType<Date, Date | string, Date | string>
|
||||||
|
|
||||||
|
export interface Account {
|
||||||
|
description: string
|
||||||
|
financialYearId: number
|
||||||
|
id: Generated<number>
|
||||||
|
number: number
|
||||||
|
sru: number | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AccountBalance {
|
||||||
|
accountNumber: number
|
||||||
|
financialYearId: number
|
||||||
|
in: Generated<Numeric>
|
||||||
|
inQuantity: number | null
|
||||||
|
out: Generated<Numeric>
|
||||||
|
outQuantity: number | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Admission {
|
||||||
|
createdAt: Generated<Timestamp | null>
|
||||||
|
createdById: number
|
||||||
|
id: Generated<number>
|
||||||
|
modifiedAt: Timestamp | null
|
||||||
|
modifiedById: number | null
|
||||||
|
regex: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AdmissionsRoles {
|
||||||
|
admissionId: number
|
||||||
|
roleId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AliasesToSupplier {
|
||||||
|
alias: string
|
||||||
|
id: Generated<number>
|
||||||
|
supplierId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Dimension {
|
||||||
|
id: Generated<number>
|
||||||
|
name: string | null
|
||||||
|
number: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EmailToken {
|
||||||
|
cancelledAt: Timestamp | null
|
||||||
|
consumedAt: Timestamp | null
|
||||||
|
createdAt: Generated<Timestamp>
|
||||||
|
email: string
|
||||||
|
id: Generated<number>
|
||||||
|
token: string
|
||||||
|
userId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Entry {
|
||||||
|
description: string | null
|
||||||
|
entryDate: Timestamp
|
||||||
|
financialYearId: number
|
||||||
|
id: Generated<number>
|
||||||
|
journalId: number
|
||||||
|
number: number
|
||||||
|
signature: string | null
|
||||||
|
transactionDate: Timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Error {
|
||||||
|
createdAt: Timestamp | null
|
||||||
|
details: Json | null
|
||||||
|
headers: Json | null
|
||||||
|
id: Generated<number>
|
||||||
|
ip: string | null
|
||||||
|
message: string | null
|
||||||
|
method: string | null
|
||||||
|
path: string | null
|
||||||
|
reqId: string | null
|
||||||
|
stack: string | null
|
||||||
|
statusCode: number | null
|
||||||
|
type: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface File {
|
||||||
|
filename: string
|
||||||
|
id: Generated<number>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FilesToInvoice {
|
||||||
|
fileId: number
|
||||||
|
invoiceId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FinancialYear {
|
||||||
|
endDate: Timestamp
|
||||||
|
id: Generated<number>
|
||||||
|
startDate: Timestamp
|
||||||
|
year: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Invite {
|
||||||
|
consumedAt: Timestamp | null
|
||||||
|
consumedById: number | null
|
||||||
|
createdAt: Generated<Timestamp>
|
||||||
|
createdById: number | null
|
||||||
|
email: string
|
||||||
|
id: Generated<number>
|
||||||
|
modifiedAt: Timestamp | null
|
||||||
|
modifiedById: number | null
|
||||||
|
token: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InvitesRoles {
|
||||||
|
invitedId: number
|
||||||
|
roleId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Invoice {
|
||||||
|
amount: Numeric | null
|
||||||
|
dueDate: Timestamp | null
|
||||||
|
financialYearId: number | null
|
||||||
|
fiskenNumber: number | null
|
||||||
|
id: Generated<number>
|
||||||
|
invoiceDate: Timestamp | null
|
||||||
|
invoiceNumber: string | null
|
||||||
|
ocr: string | null
|
||||||
|
phmNumber: number | null
|
||||||
|
supplierId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Journal {
|
||||||
|
description: string | null
|
||||||
|
id: Generated<number>
|
||||||
|
identifier: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Object {
|
||||||
|
dimensionId: number
|
||||||
|
id: Generated<number>
|
||||||
|
name: string | null
|
||||||
|
number: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PasswordToken {
|
||||||
|
cancelledAt: Timestamp | null
|
||||||
|
consumedAt: Timestamp | null
|
||||||
|
createdAt: Generated<Timestamp>
|
||||||
|
id: Generated<number>
|
||||||
|
token: string
|
||||||
|
userId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Role {
|
||||||
|
createdAt: Generated<Timestamp>
|
||||||
|
createdById: number | null
|
||||||
|
id: Generated<number>
|
||||||
|
modifiedAt: Timestamp | null
|
||||||
|
modifiedById: number | null
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Supplier {
|
||||||
|
id: Generated<number>
|
||||||
|
name: string | null
|
||||||
|
supplierTypeId: number
|
||||||
|
taxId: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SupplierType {
|
||||||
|
id: Generated<number>
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Transaction {
|
||||||
|
accountNumber: number
|
||||||
|
amount: Numeric
|
||||||
|
description: string | null
|
||||||
|
entryId: number
|
||||||
|
id: Generated<number>
|
||||||
|
invoiceId: number | null
|
||||||
|
objectId: number | null
|
||||||
|
quantity: Numeric | null
|
||||||
|
signature: string | null
|
||||||
|
transactionDate: Timestamp | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TransactionsToObjects {
|
||||||
|
objectId: number
|
||||||
|
transactionId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface User {
|
||||||
|
bannedAt: Timestamp | null
|
||||||
|
bannedById: number | null
|
||||||
|
blockedAt: Timestamp | null
|
||||||
|
blockedById: number | null
|
||||||
|
createdAt: Generated<Timestamp>
|
||||||
|
email: string
|
||||||
|
emailVerifiedAt: Timestamp | null
|
||||||
|
id: Generated<number>
|
||||||
|
lastActivityAt: Timestamp | null
|
||||||
|
lastLoginAt: Timestamp | null
|
||||||
|
lastLoginAttemptAt: Timestamp | null
|
||||||
|
loginAttempts: Generated<number | null>
|
||||||
|
password: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UsersRoles {
|
||||||
|
roleId: number
|
||||||
|
userId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DB {
|
||||||
|
account: Account
|
||||||
|
accountBalance: AccountBalance
|
||||||
|
admission: Admission
|
||||||
|
admissions_roles: AdmissionsRoles
|
||||||
|
aliasesToSupplier: AliasesToSupplier
|
||||||
|
dimension: Dimension
|
||||||
|
emailToken: EmailToken
|
||||||
|
entry: Entry
|
||||||
|
error: Error
|
||||||
|
file: File
|
||||||
|
filesToInvoice: FilesToInvoice
|
||||||
|
financialYear: FinancialYear
|
||||||
|
invite: Invite
|
||||||
|
invites_roles: InvitesRoles
|
||||||
|
invoice: Invoice
|
||||||
|
journal: Journal
|
||||||
|
object: Object
|
||||||
|
passwordToken: PasswordToken
|
||||||
|
role: Role
|
||||||
|
supplier: Supplier
|
||||||
|
supplierType: SupplierType
|
||||||
|
transaction: Transaction
|
||||||
|
transactionsToObjects: TransactionsToObjects
|
||||||
|
user: User
|
||||||
|
users_roles: UsersRoles
|
||||||
|
}
|
||||||
144
shared/types.ts
144
shared/types.ts
@ -1,85 +1,85 @@
|
|||||||
export type Account = {
|
// export type Account = {
|
||||||
id: number
|
// id: number
|
||||||
number: number
|
// number: number
|
||||||
description: string
|
// description: string
|
||||||
}
|
// }
|
||||||
|
|
||||||
export type Balance = {
|
// export type Balance = {
|
||||||
accountNumber: string
|
// accountNumber: string
|
||||||
description: string
|
// description: string
|
||||||
} & Record<number, number>
|
// } & Record<number, number>
|
||||||
|
|
||||||
export interface Entry {
|
// export interface Entry {
|
||||||
id: number
|
// id: number
|
||||||
journal: string
|
// journal: string
|
||||||
number: number
|
// number: number
|
||||||
amount: number
|
// amount: number
|
||||||
description: string
|
// description: string
|
||||||
transactionDate: string
|
// transactionDate: string
|
||||||
entryDate: string
|
// entryDate: string
|
||||||
transactions: {
|
// transactions: {
|
||||||
accountNumber: number
|
// accountNumber: number
|
||||||
description: string
|
// description: string
|
||||||
amount: number
|
// amount: number
|
||||||
}[]
|
// }[]
|
||||||
}
|
// }
|
||||||
|
|
||||||
export type FinancialYear = {
|
// export type FinancialYear = {
|
||||||
year: number
|
// year: number
|
||||||
startDate: string
|
// startDate: string
|
||||||
endDate: string
|
// endDate: string
|
||||||
}
|
// }
|
||||||
|
|
||||||
export type Invoice = {
|
// export type Invoice = {
|
||||||
id: number
|
// id: number
|
||||||
fiskenNumber?: number
|
// fiskenNumber?: number
|
||||||
phmNumber?: number
|
// phmNumber?: number
|
||||||
invoiceDate: string
|
// invoiceDate: string
|
||||||
dueDate: string
|
// dueDate: string
|
||||||
invoiceNumber: number
|
// invoiceNumber: number
|
||||||
amount: number
|
// amount: number
|
||||||
files?: { filename: string }[]
|
// files?: { filename: string }[]
|
||||||
transactions?: {
|
// transactions?: {
|
||||||
accountNumber: number
|
// accountNumber: number
|
||||||
amount: number
|
// amount: number
|
||||||
description: number
|
// description: number
|
||||||
entryId: number
|
// entryId: number
|
||||||
}[]
|
// }[]
|
||||||
}
|
// }
|
||||||
|
|
||||||
export type Journal = {
|
// export type Journal = {
|
||||||
id: number
|
// id: number
|
||||||
identifier: string
|
// identifier: string
|
||||||
}
|
// }
|
||||||
|
|
||||||
export type Object = {
|
// export type Object = {
|
||||||
id: number
|
// id: number
|
||||||
dimensionName: string
|
// dimensionName: string
|
||||||
name: string
|
// name: string
|
||||||
}
|
// }
|
||||||
|
|
||||||
export type Result = {
|
// export type Result = {
|
||||||
accountNumber: number
|
// accountNumber: number
|
||||||
description?: string
|
// description?: string
|
||||||
} & Record<number, number>
|
// } & Record<number, number>
|
||||||
|
|
||||||
export type Supplier = {
|
// export type Supplier = {
|
||||||
id: number
|
// id: number
|
||||||
name: string
|
// name: string
|
||||||
}
|
// }
|
||||||
|
|
||||||
export interface Transaction {
|
// export interface Transaction {
|
||||||
accountNumber: number
|
// accountNumber: number
|
||||||
description: string
|
// description: string
|
||||||
amount: number
|
// amount: number
|
||||||
entryId: number
|
// entryId: number
|
||||||
}
|
// }
|
||||||
|
|
||||||
export interface TransactionFull extends Transaction {
|
// export interface TransactionFull extends Transaction {
|
||||||
transactionDate: string
|
// transactionDate: string
|
||||||
invoiceId: number
|
// invoiceId: number
|
||||||
entryDescription: string
|
// entryDescription: string
|
||||||
}
|
// }
|
||||||
|
|
||||||
export interface Route {
|
export interface Route {
|
||||||
path: string
|
path: string
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user