Add quotation and tariff backend flow
All checks were successful
Build Docker Image / build (push) Successful in 3m32s

This commit is contained in:
Ruslan Bakiev
2026-04-11 09:00:20 +07:00
parent 3a84d7aace
commit e8051260db
7 changed files with 1947 additions and 115 deletions

View File

@@ -7,12 +7,14 @@ RUN npm ci
FROM deps AS builder
COPY prisma ./prisma
RUN npx prisma generate
COPY tsconfig.json ./
COPY src ./src
RUN npm run build
FROM deps AS runtime-deps
RUN npm prune --omit=dev
FROM node:22-alpine AS runtime
@@ -23,9 +25,12 @@ WORKDIR /app
COPY package.json ./
COPY --from=runtime-deps /app/node_modules ./node_modules
COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
COPY --from=builder /app/node_modules/@prisma/client ./node_modules/@prisma/client
COPY --from=builder /app/dist ./dist
COPY prisma ./prisma
COPY scripts ./scripts
EXPOSE 8000
CMD ["sh", "-c", ". /app/scripts/load-vault-env.sh && node dist/index.js"]
CMD ["sh", "-c", ". /app/scripts/load-vault-env.sh && set +e && npx prisma migrate resolve --applied 0_init 2>/dev/null; set -e && npx prisma migrate deploy && node dist/index.js"]

427
package-lock.json generated
View File

@@ -9,6 +9,7 @@
"version": "1.0.0",
"dependencies": {
"@apollo/server": "^4.11.3",
"@prisma/client": "^6.5.0",
"@sentry/node": "^9.5.0",
"cors": "^2.8.5",
"express": "^4.21.2",
@@ -20,6 +21,7 @@
"@types/cors": "^2.8.17",
"@types/express": "^5.0.0",
"@types/node": "^22.13.0",
"prisma": "^6.5.0",
"tsx": "^4.19.0",
"typescript": "^5.7.0"
}
@@ -1330,6 +1332,91 @@
"@opentelemetry/api": "^1.1.0"
}
},
"node_modules/@prisma/client": {
"version": "6.19.3",
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.19.3.tgz",
"integrity": "sha512-mKq3jQFhjvko5LTJFHGilsuQs+W+T3Gm451NzuTDGQxwCzwXHYnIu2zGkRoW+Exq3Rob7yp2MfzSrdIiZVhrBg==",
"hasInstallScript": true,
"license": "Apache-2.0",
"engines": {
"node": ">=18.18"
},
"peerDependencies": {
"prisma": "*",
"typescript": ">=5.1.0"
},
"peerDependenciesMeta": {
"prisma": {
"optional": true
},
"typescript": {
"optional": true
}
}
},
"node_modules/@prisma/config": {
"version": "6.19.3",
"resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.19.3.tgz",
"integrity": "sha512-CBPT44BjlQxEt8kiMEauji2WHTDoVBOKl7UlewXmUgBPnr/oPRZC3psci5chJnYmH0ivEIog2OU9PGWoki3DLQ==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"c12": "3.1.0",
"deepmerge-ts": "7.1.5",
"effect": "3.21.0",
"empathic": "2.0.0"
}
},
"node_modules/@prisma/debug": {
"version": "6.19.3",
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.19.3.tgz",
"integrity": "sha512-ljkJ+SgpXNktLG0Q/n4JGYCkKf0f8oYLyjImS2I8e2q2WCfdRRtWER062ZV/ixaNP2M2VKlWXVJiGzZaUgbKZw==",
"devOptional": true,
"license": "Apache-2.0"
},
"node_modules/@prisma/engines": {
"version": "6.19.3",
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.19.3.tgz",
"integrity": "sha512-RSYxtlYFl5pJ8ZePgMv0lZ9IzVCOdTPOegrs2qcbAEFrBI1G33h6wyC9kjQvo0DnYEhEVY0X4LsuFHXLKQk88g==",
"devOptional": true,
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "6.19.3",
"@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7",
"@prisma/fetch-engine": "6.19.3",
"@prisma/get-platform": "6.19.3"
}
},
"node_modules/@prisma/engines-version": {
"version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7",
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7.tgz",
"integrity": "sha512-03bgb1VD5gvuumNf+7fVGBzfpJPjmqV423l/WxsWk2cNQ42JD0/SsFBPhN6z8iAvdHs07/7ei77SKu7aZfq8bA==",
"devOptional": true,
"license": "Apache-2.0"
},
"node_modules/@prisma/fetch-engine": {
"version": "6.19.3",
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.19.3.tgz",
"integrity": "sha512-tKtl/qco9Nt7LU5iKhpultD8O4vMCZcU2CHjNTnRrL1QvSUr5W/GcyFPjNL87GtRrwBc7ubXXD9xy4EvLvt8JA==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "6.19.3",
"@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7",
"@prisma/get-platform": "6.19.3"
}
},
"node_modules/@prisma/get-platform": {
"version": "6.19.3",
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.19.3.tgz",
"integrity": "sha512-xFj1VcJ1N3MKooOQAGO0W5tsd0W2QzIvW7DD7c/8H14Zmp4jseeWAITm+w2LLoLrlhoHdPPh0NMZ8mfL6puoHA==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "6.19.3"
}
},
"node_modules/@prisma/instrumentation": {
"version": "6.11.1",
"resolved": "https://registry.npmjs.org/@prisma/instrumentation/-/instrumentation-6.11.1.tgz",
@@ -1503,6 +1590,13 @@
"@opentelemetry/semantic-conventions": "^1.34.0"
}
},
"node_modules/@standard-schema/spec": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
"devOptional": true,
"license": "MIT"
},
"node_modules/@types/body-parser": {
"version": "1.19.6",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
@@ -1809,6 +1903,35 @@
"node": ">= 0.8"
}
},
"node_modules/c12": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz",
"integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"chokidar": "^4.0.3",
"confbox": "^0.2.2",
"defu": "^6.1.4",
"dotenv": "^16.6.1",
"exsolve": "^1.0.7",
"giget": "^2.0.0",
"jiti": "^2.4.2",
"ohash": "^2.0.11",
"pathe": "^2.0.3",
"perfect-debounce": "^1.0.0",
"pkg-types": "^2.2.0",
"rc9": "^2.1.2"
},
"peerDependencies": {
"magicast": "^0.3.5"
},
"peerDependenciesMeta": {
"magicast": {
"optional": true
}
}
},
"node_modules/call-bind": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
@@ -1856,6 +1979,32 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/citty": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz",
"integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"consola": "^3.2.3"
}
},
"node_modules/cjs-module-lexer": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz",
@@ -1874,6 +2023,23 @@
"node": ">= 0.8"
}
},
"node_modules/confbox": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz",
"integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==",
"devOptional": true,
"license": "MIT"
},
"node_modules/consola": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz",
"integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==",
"devOptional": true,
"license": "MIT",
"engines": {
"node": "^14.18.0 || >=16.10.0"
}
},
"node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
@@ -1936,6 +2102,16 @@
"ms": "2.0.0"
}
},
"node_modules/deepmerge-ts": {
"version": "7.1.5",
"resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz",
"integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==",
"devOptional": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/define-data-property": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
@@ -1953,6 +2129,13 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/defu": {
"version": "6.1.7",
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz",
"integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==",
"devOptional": true,
"license": "MIT"
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -1971,6 +2154,13 @@
"node": ">= 0.8"
}
},
"node_modules/destr": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz",
"integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==",
"devOptional": true,
"license": "MIT"
},
"node_modules/destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
@@ -1981,6 +2171,19 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/dotenv": {
"version": "16.6.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
"devOptional": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -2001,6 +2204,27 @@
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
"license": "MIT"
},
"node_modules/effect": {
"version": "3.21.0",
"resolved": "https://registry.npmjs.org/effect/-/effect-3.21.0.tgz",
"integrity": "sha512-PPN80qRokCd1f015IANNhrwOnLO7GrrMQfk4/lnZRE/8j7UPWrNNjPV0uBrZutI/nHzernbW+J0hdqQysHiSnQ==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.0.0",
"fast-check": "^3.23.1"
}
},
"node_modules/empathic": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz",
"integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==",
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">=14"
}
},
"node_modules/encodeurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
@@ -2158,6 +2382,36 @@
"url": "https://opencollective.com/express"
}
},
"node_modules/exsolve": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz",
"integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==",
"devOptional": true,
"license": "MIT"
},
"node_modules/fast-check": {
"version": "3.23.2",
"resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz",
"integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==",
"devOptional": true,
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/dubzzz"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fast-check"
}
],
"license": "MIT",
"dependencies": {
"pure-rand": "^6.1.0"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/finalhandler": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
@@ -2305,6 +2559,24 @@
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
}
},
"node_modules/giget": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz",
"integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"citty": "^0.1.6",
"consola": "^3.4.0",
"defu": "^6.1.4",
"node-fetch-native": "^1.6.6",
"nypm": "^0.6.0",
"pathe": "^2.0.3"
},
"bin": {
"giget": "dist/cli.mjs"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@@ -2499,6 +2771,16 @@
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
"license": "MIT"
},
"node_modules/jiti": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
"integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
"devOptional": true,
"license": "MIT",
"bin": {
"jiti": "lib/jiti-cli.mjs"
}
},
"node_modules/jose": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/jose/-/jose-6.2.0.tgz",
@@ -2673,6 +2955,38 @@
}
}
},
"node_modules/node-fetch-native": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz",
"integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==",
"devOptional": true,
"license": "MIT"
},
"node_modules/nypm": {
"version": "0.6.5",
"resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.5.tgz",
"integrity": "sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"citty": "^0.2.0",
"pathe": "^2.0.3",
"tinyexec": "^1.0.2"
},
"bin": {
"nypm": "dist/cli.mjs"
},
"engines": {
"node": ">=18"
}
},
"node_modules/nypm/node_modules/citty": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/citty/-/citty-0.2.2.tgz",
"integrity": "sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==",
"devOptional": true,
"license": "MIT"
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -2694,6 +3008,13 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/ohash": {
"version": "2.0.11",
"resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz",
"integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==",
"devOptional": true,
"license": "MIT"
},
"node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
@@ -2727,6 +3048,20 @@
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
"license": "MIT"
},
"node_modules/pathe": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
"devOptional": true,
"license": "MIT"
},
"node_modules/perfect-debounce": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
"devOptional": true,
"license": "MIT"
},
"node_modules/pg-int8": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
@@ -2758,6 +3093,18 @@
"node": ">=4"
}
},
"node_modules/pkg-types": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz",
"integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"confbox": "^0.2.2",
"exsolve": "^1.0.7",
"pathe": "^2.0.3"
}
},
"node_modules/possible-typed-array-names": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
@@ -2806,6 +3153,32 @@
"node": ">=0.10.0"
}
},
"node_modules/prisma": {
"version": "6.19.3",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-6.19.3.tgz",
"integrity": "sha512-++ZJ0ijLrDJF6hNB4t4uxg2br3fC4H9Yc9tcbjr2fcNFP3rh/SBNrAgjhsqBU4Ght8JPrVofG/ZkXfnSfnYsFg==",
"devOptional": true,
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/config": "6.19.3",
"@prisma/engines": "6.19.3"
},
"bin": {
"prisma": "build/index.js"
},
"engines": {
"node": ">=18.18"
},
"peerDependencies": {
"typescript": ">=5.1.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -2819,6 +3192,23 @@
"node": ">= 0.10"
}
},
"node_modules/pure-rand": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz",
"integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==",
"devOptional": true,
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/dubzzz"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fast-check"
}
],
"license": "MIT"
},
"node_modules/qs": {
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
@@ -2858,6 +3248,31 @@
"node": ">= 0.8"
}
},
"node_modules/rc9": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz",
"integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"defu": "^6.1.4",
"destr": "^2.0.3"
}
},
"node_modules/readdirp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/require-in-the-middle": {
"version": "7.5.2",
"resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.2.tgz",
@@ -3159,6 +3574,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/tinyexec": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz",
"integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==",
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/to-buffer": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz",
@@ -3245,7 +3670,7 @@
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"devOptional": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",

View File

@@ -5,11 +5,12 @@
"type": "module",
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc",
"start": "node dist/index.js"
"build": "prisma generate && tsc",
"start": "prisma migrate deploy && node dist/index.js"
},
"dependencies": {
"@apollo/server": "^4.11.3",
"@prisma/client": "^6.5.0",
"cors": "^2.8.5",
"express": "^4.21.2",
"graphql": "^16.10.0",
@@ -21,6 +22,7 @@
"@types/cors": "^2.8.17",
"@types/express": "^5.0.0",
"@types/node": "^22.13.0",
"prisma": "^6.5.0",
"tsx": "^4.19.0",
"typescript": "^5.7.0"
}

View File

@@ -0,0 +1,166 @@
-- CreateSchema
CREATE SCHEMA IF NOT EXISTS "public";
-- CreateTable
CREATE TABLE "orders_tariff_reference" (
"id" SERIAL NOT NULL,
"uuid" TEXT NOT NULL,
"team_uuid" VARCHAR(100) NOT NULL,
"name" VARCHAR(255) NOT NULL,
"status" VARCHAR(20) NOT NULL DEFAULT 'active',
"operation_code" VARCHAR(50),
"incoterms_code" VARCHAR(20),
"transport_type_code" VARCHAR(50),
"tare_type_code" VARCHAR(50),
"source_country_code" VARCHAR(10),
"destination_country_code" VARCHAR(10),
"source_hub_uuid" VARCHAR(100),
"destination_hub_uuid" VARCHAR(100),
"min_weight_kg" DECIMAL(12,2),
"max_weight_kg" DECIMAL(12,2),
"min_volume_cbm" DECIMAL(12,3),
"max_volume_cbm" DECIMAL(12,3),
"min_distance_km" INTEGER,
"max_distance_km" INTEGER,
"amount_usd" DECIMAL(12,2) NOT NULL,
"min_price_usd" DECIMAL(12,2),
"eta_days" INTEGER,
"priority" INTEGER NOT NULL DEFAULT 100,
"currency" VARCHAR(10) NOT NULL DEFAULT 'USD',
"dmn_expression" TEXT,
"notes" TEXT,
"created_by_user_id" VARCHAR(255),
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "orders_tariff_reference_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "orders_quotation" (
"id" SERIAL NOT NULL,
"uuid" TEXT NOT NULL,
"team_uuid" VARCHAR(100) NOT NULL,
"created_by_user_id" VARCHAR(255),
"title" VARCHAR(255) NOT NULL DEFAULT 'Quotation',
"status" VARCHAR(30) NOT NULL DEFAULT 'draft',
"operation_code" VARCHAR(50),
"incoterms_code" VARCHAR(20),
"transport_type_code" VARCHAR(50),
"tare_type_code" VARCHAR(50),
"source_country_code" VARCHAR(10),
"source_hub_uuid" VARCHAR(100),
"source_location_uuid" VARCHAR(100),
"source_location_name" VARCHAR(255),
"source_latitude" DOUBLE PRECISION,
"source_longitude" DOUBLE PRECISION,
"destination_country_code" VARCHAR(10),
"destination_hub_uuid" VARCHAR(100),
"destination_location_uuid" VARCHAR(100),
"destination_location_name" VARCHAR(255),
"destination_latitude" DOUBLE PRECISION,
"destination_longitude" DOUBLE PRECISION,
"chargeable_weight_kg" DECIMAL(12,2),
"gross_weight_kg" DECIMAL(12,2),
"volume_cbm" DECIMAL(12,3),
"units_count" INTEGER,
"route_distance_km" INTEGER,
"selected_tariff_id" INTEGER,
"tariff_match_summary" TEXT,
"tariff_snapshot" TEXT,
"total_amount" DECIMAL(12,2) NOT NULL DEFAULT 0,
"currency" VARCHAR(10) NOT NULL DEFAULT 'USD',
"eta_days" INTEGER,
"notes" TEXT,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "orders_quotation_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "orders_quotation_change" (
"id" SERIAL NOT NULL,
"uuid" TEXT NOT NULL,
"quotation_id" INTEGER NOT NULL,
"actor_user_id" VARCHAR(255),
"actor_label" VARCHAR(255),
"source" VARCHAR(50) NOT NULL,
"summary" TEXT,
"payload_json" TEXT,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "orders_quotation_change_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "orders_order" (
"id" SERIAL NOT NULL,
"uuid" TEXT NOT NULL,
"team_uuid" VARCHAR(100) NOT NULL,
"quotation_id" INTEGER,
"created_by_user_id" VARCHAR(255),
"name" VARCHAR(255) NOT NULL,
"status" VARCHAR(30) NOT NULL DEFAULT 'draft',
"total_amount" DECIMAL(12,2) NOT NULL DEFAULT 0,
"currency" VARCHAR(10) NOT NULL DEFAULT 'USD',
"source_location_uuid" VARCHAR(100),
"source_location_name" VARCHAR(255),
"source_country_code" VARCHAR(10),
"source_latitude" DOUBLE PRECISION,
"source_longitude" DOUBLE PRECISION,
"destination_location_uuid" VARCHAR(100),
"destination_location_name" VARCHAR(255),
"destination_country_code" VARCHAR(10),
"destination_latitude" DOUBLE PRECISION,
"destination_longitude" DOUBLE PRECISION,
"eta_days" INTEGER,
"notes" TEXT,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "orders_order_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "orders_tariff_reference_uuid_key" ON "orders_tariff_reference"("uuid");
-- CreateIndex
CREATE INDEX "orders_tariff_reference_team_uuid_status_priority_idx" ON "orders_tariff_reference"("team_uuid", "status", "priority");
-- CreateIndex
CREATE INDEX "orders_tariff_reference_team_uuid_source_country_code_desti_idx" ON "orders_tariff_reference"("team_uuid", "source_country_code", "destination_country_code");
-- CreateIndex
CREATE UNIQUE INDEX "orders_quotation_uuid_key" ON "orders_quotation"("uuid");
-- CreateIndex
CREATE INDEX "orders_quotation_team_uuid_status_created_at_idx" ON "orders_quotation"("team_uuid", "status", "created_at");
-- CreateIndex
CREATE INDEX "orders_quotation_team_uuid_source_country_code_destination__idx" ON "orders_quotation"("team_uuid", "source_country_code", "destination_country_code");
-- CreateIndex
CREATE UNIQUE INDEX "orders_quotation_change_uuid_key" ON "orders_quotation_change"("uuid");
-- CreateIndex
CREATE INDEX "orders_quotation_change_quotation_id_created_at_idx" ON "orders_quotation_change"("quotation_id", "created_at");
-- CreateIndex
CREATE UNIQUE INDEX "orders_order_uuid_key" ON "orders_order"("uuid");
-- CreateIndex
CREATE UNIQUE INDEX "orders_order_quotation_id_key" ON "orders_order"("quotation_id");
-- CreateIndex
CREATE INDEX "orders_order_team_uuid_created_at_idx" ON "orders_order"("team_uuid", "created_at");
-- AddForeignKey
ALTER TABLE "orders_quotation" ADD CONSTRAINT "orders_quotation_selected_tariff_id_fkey" FOREIGN KEY ("selected_tariff_id") REFERENCES "orders_tariff_reference"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "orders_quotation_change" ADD CONSTRAINT "orders_quotation_change_quotation_id_fkey" FOREIGN KEY ("quotation_id") REFERENCES "orders_quotation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "orders_order" ADD CONSTRAINT "orders_order_quotation_id_fkey" FOREIGN KEY ("quotation_id") REFERENCES "orders_quotation"("id") ON DELETE SET NULL ON UPDATE CASCADE;

139
prisma/schema.prisma Normal file
View File

@@ -0,0 +1,139 @@
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("ORDERS_DATABASE_URL")
}
model TariffReference {
id Int @id @default(autoincrement())
uuid String @unique @default(uuid())
teamUuid String @map("team_uuid") @db.VarChar(100)
name String @db.VarChar(255)
status String @default("active") @db.VarChar(20)
operationCode String? @map("operation_code") @db.VarChar(50)
incotermsCode String? @map("incoterms_code") @db.VarChar(20)
transportTypeCode String? @map("transport_type_code") @db.VarChar(50)
tareTypeCode String? @map("tare_type_code") @db.VarChar(50)
sourceCountryCode String? @map("source_country_code") @db.VarChar(10)
destinationCountryCode String? @map("destination_country_code") @db.VarChar(10)
sourceHubUuid String? @map("source_hub_uuid") @db.VarChar(100)
destinationHubUuid String? @map("destination_hub_uuid") @db.VarChar(100)
minWeightKg Decimal? @map("min_weight_kg") @db.Decimal(12, 2)
maxWeightKg Decimal? @map("max_weight_kg") @db.Decimal(12, 2)
minVolumeCbm Decimal? @map("min_volume_cbm") @db.Decimal(12, 3)
maxVolumeCbm Decimal? @map("max_volume_cbm") @db.Decimal(12, 3)
minDistanceKm Int? @map("min_distance_km")
maxDistanceKm Int? @map("max_distance_km")
amountUsd Decimal @map("amount_usd") @db.Decimal(12, 2)
minPriceUsd Decimal? @map("min_price_usd") @db.Decimal(12, 2)
etaDays Int? @map("eta_days")
priority Int @default(100)
currency String @default("USD") @db.VarChar(10)
dmnExpression String? @map("dmn_expression")
notes String?
createdByUserId String? @map("created_by_user_id") @db.VarChar(255)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
quotations Quotation[] @relation("QuotationSelectedTariff")
@@index([teamUuid, status, priority])
@@index([teamUuid, sourceCountryCode, destinationCountryCode])
@@map("orders_tariff_reference")
}
model Quotation {
id Int @id @default(autoincrement())
uuid String @unique @default(uuid())
teamUuid String @map("team_uuid") @db.VarChar(100)
createdByUserId String? @map("created_by_user_id") @db.VarChar(255)
title String @default("Quotation") @db.VarChar(255)
status String @default("draft") @db.VarChar(30)
operationCode String? @map("operation_code") @db.VarChar(50)
incotermsCode String? @map("incoterms_code") @db.VarChar(20)
transportTypeCode String? @map("transport_type_code") @db.VarChar(50)
tareTypeCode String? @map("tare_type_code") @db.VarChar(50)
sourceCountryCode String? @map("source_country_code") @db.VarChar(10)
sourceHubUuid String? @map("source_hub_uuid") @db.VarChar(100)
sourceLocationUuid String? @map("source_location_uuid") @db.VarChar(100)
sourceLocationName String? @map("source_location_name") @db.VarChar(255)
sourceLatitude Float? @map("source_latitude")
sourceLongitude Float? @map("source_longitude")
destinationCountryCode String? @map("destination_country_code") @db.VarChar(10)
destinationHubUuid String? @map("destination_hub_uuid") @db.VarChar(100)
destinationLocationUuid String? @map("destination_location_uuid") @db.VarChar(100)
destinationLocationName String? @map("destination_location_name") @db.VarChar(255)
destinationLatitude Float? @map("destination_latitude")
destinationLongitude Float? @map("destination_longitude")
chargeableWeightKg Decimal? @map("chargeable_weight_kg") @db.Decimal(12, 2)
grossWeightKg Decimal? @map("gross_weight_kg") @db.Decimal(12, 2)
volumeCbm Decimal? @map("volume_cbm") @db.Decimal(12, 3)
unitsCount Int? @map("units_count")
routeDistanceKm Int? @map("route_distance_km")
selectedTariffId Int? @map("selected_tariff_id")
selectedTariff TariffReference? @relation("QuotationSelectedTariff", fields: [selectedTariffId], references: [id], onDelete: SetNull)
tariffMatchSummary String? @map("tariff_match_summary")
tariffSnapshot String? @map("tariff_snapshot")
totalAmount Decimal @default(0) @map("total_amount") @db.Decimal(12, 2)
currency String @default("USD") @db.VarChar(10)
etaDays Int? @map("eta_days")
notes String?
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
orders Order[]
changes QuotationChange[]
@@index([teamUuid, status, createdAt])
@@index([teamUuid, sourceCountryCode, destinationCountryCode])
@@map("orders_quotation")
}
model QuotationChange {
id Int @id @default(autoincrement())
uuid String @unique @default(uuid())
quotationId Int @map("quotation_id")
quotation Quotation @relation(fields: [quotationId], references: [id], onDelete: Cascade)
actorUserId String? @map("actor_user_id") @db.VarChar(255)
actorLabel String? @map("actor_label") @db.VarChar(255)
source String @db.VarChar(50)
summary String?
payloadJson String? @map("payload_json")
createdAt DateTime @default(now()) @map("created_at")
@@index([quotationId, createdAt])
@@map("orders_quotation_change")
}
model Order {
id Int @id @default(autoincrement())
uuid String @unique @default(uuid())
teamUuid String @map("team_uuid") @db.VarChar(100)
quotationId Int? @unique @map("quotation_id")
quotation Quotation? @relation(fields: [quotationId], references: [id], onDelete: SetNull)
createdByUserId String? @map("created_by_user_id") @db.VarChar(255)
name String @db.VarChar(255)
status String @default("draft") @db.VarChar(30)
totalAmount Decimal @default(0) @map("total_amount") @db.Decimal(12, 2)
currency String @default("USD") @db.VarChar(10)
sourceLocationUuid String? @map("source_location_uuid") @db.VarChar(100)
sourceLocationName String? @map("source_location_name") @db.VarChar(255)
sourceCountryCode String? @map("source_country_code") @db.VarChar(10)
sourceLatitude Float? @map("source_latitude")
sourceLongitude Float? @map("source_longitude")
destinationLocationUuid String? @map("destination_location_uuid") @db.VarChar(100)
destinationLocationName String? @map("destination_location_name") @db.VarChar(255)
destinationCountryCode String? @map("destination_country_code") @db.VarChar(10)
destinationLatitude Float? @map("destination_latitude")
destinationLongitude Float? @map("destination_longitude")
etaDays Int? @map("eta_days")
notes String?
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@index([teamUuid, createdAt])
@@map("orders_order")
}

3
src/db.ts Normal file
View File

@@ -0,0 +1,3 @@
import { PrismaClient } from '@prisma/client'
export const prisma = new PrismaClient()

File diff suppressed because it is too large Load Diff