This commit is contained in:
seb
2026-10-09 10:41:21 +02:00
parent 969c064f3a
commit 011d84c3c1
31 changed files with 1845 additions and 4 deletions

1
.gitignore vendored
View File

@@ -4,3 +4,4 @@ data/
logs/
print_spool/
certs/
output/

View File

@@ -31,6 +31,16 @@ npm run docs:api
That writes `docs/api/routes.md`, `docs/api/routes-all.md`, and per-route Markdown under `docs/api/routes/`. There is no generated `openapi.json` on disk — the interactive Swagger UI at `/docs/` builds the spec from the same `@openapi` JSDoc at runtime (`src/api/openapiSpec.js`).
### Function review files
Create one JavaScript file per function in the project:
```bash
npm run review:functions
```
The generated files are written to `output/` and include each function's source filename and line numbers. The same directory also contains `functions.txt`, an index of source filenames, line numbers, and function signatures. Existing generated function files and the index are replaced each time the command runs. Dependencies, runtime data, and the output directory itself are excluded from the scan.
### Brands (company-scoped, `selectedCompanyId` aus der Session)
| Methode | Route | Store-Methode |

View File

@@ -32,6 +32,9 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route
| File | Endpoint |
|------|----------|
| [`routes/account.get.md`](routes/account.get.md) | `GET /account` |
| [`routes/account.post.md`](routes/account.post.md) | `POST /account` |
| [`routes/account/_id.md`](routes/account/_id.md) | `DELETE /account/{id}` |
| [`routes/article/create.md`](routes/article/create.md) | `POST /article` |
| [`routes/article/export.zip.md`](routes/article/export.zip.md) | `GET /article/export.zip` |
| [`routes/article/delete.md`](routes/article/delete.md) | `DELETE /article/{sku}` |
@@ -163,6 +166,12 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route
| [`routes/order/delete.md`](routes/order/delete.md) | `DELETE /order/{id}` |
| [`routes/order/get.md`](routes/order/get.md) | `GET /order/{id}` |
| [`routes/order/update.md`](routes/order/update.md) | `PATCH /order/{id}` |
| [`routes/order/_id/payment.get.md`](routes/order/_id/payment.get.md) | `GET /order/{id}/payment` |
| [`routes/order/_id/payment.post.md`](routes/order/_id/payment.post.md) | `POST /order/{id}/payment` |
| [`routes/order/_id/shipping.get.md`](routes/order/_id/shipping.get.md) | `GET /order/{id}/shipping` |
| [`routes/order/_id/shipping.post.md`](routes/order/_id/shipping.post.md) | `POST /order/{id}/shipping` |
| [`routes/package/_id.md`](routes/package/_id.md) | `DELETE /package/{id}` |
| [`routes/payment/_id.md`](routes/payment/_id.md) | `DELETE /payment/{id}` |
| [`routes/posterminal/list.md`](routes/posterminal/list.md) | `GET /posterminal` |
| [`routes/posterminal/upsert.md`](routes/posterminal/upsert.md) | `PUT /posterminal` |
| [`routes/posterminal/delete.md`](routes/posterminal/delete.md) | `DELETE /posterminal/{id}` |
@@ -194,6 +203,9 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route
| [`routes/quote/update.md`](routes/quote/update.md) | `PATCH /quote/{id}` |
| [`routes/serper/image.md`](routes/serper/image.md) | `POST /serper/image` |
| [`routes/serper/images.md`](routes/serper/images.md) | `POST /serper/images` |
| [`routes/shipping/_id.md`](routes/shipping/_id.md) | `DELETE /shipping/{id}` |
| [`routes/shipping/_id/package.get.md`](routes/shipping/_id/package.get.md) | `GET /shipping/{id}/package` |
| [`routes/shipping/_id/package.post.md`](routes/shipping/_id/package.post.md) | `POST /shipping/{id}/package` |
| [`routes/stock/adjust.md`](routes/stock/adjust.md) | `POST /stock/adjust` |
| [`routes/supplier/list.md`](routes/supplier/list.md) | `GET /supplier` |
| [`routes/supplier/create.md`](routes/supplier/create.md) | `POST /supplier` |

View File

@@ -1,6 +1,6 @@
# Dash API Routes Overview (All)
Overview of all 192 API endpoints across all modules.
Overview of all 204 API endpoints across all modules.
### Notation
@@ -57,6 +57,15 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body).
### Routes
#### List accounts — `GET /account`
_none_
- Returns: `200 { accounts: { id: int, companyId: int, type: str, name: str }[] }`
#### Create account — `POST /account`
- Params: `body: { type: str, name: str }`
- Returns: `201 { account: { id: int, companyId: int, type: str, name: str } }`
#### Delete account — `DELETE /account/{id}`
- Params: `id: int`
- Returns: `204 void`
#### Create article in category — `POST /article`
- Params: `body: { categoryId: int, name: str, desc?: str, seo?: str, track_stock?: bool }`
- Returns: `201 T31`
@@ -450,6 +459,24 @@ _none_
#### Update order — `PATCH /order/{id}`
- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, lines?: T25[] }`
- Returns: `200 T7`
#### List payments of an order — `GET /order/{id}/payment`
- Params: `id: int`
- Returns: `200 { payments: { id: int, accountId: int, manifestId: int, type: str, amount: num }[] }`
#### Add payment to an order — `POST /order/{id}/payment`
- Params: `id: int, body: { accountId: int, type: str, amount: num }`
- Returns: `201 { payment: { id: int, accountId: int, manifestId: int, type: str, amount: num } }`
#### List shippings of an order — `GET /order/{id}/shipping`
- Params: `id: int`
- Returns: `200 { shippings: { id: int, manifestId: int, number: str, type: str, state: str }[] }`
#### Create shipping for an order — `POST /order/{id}/shipping`
- Params: `id: int, body: { type: str, state: str }`
- Returns: `201 { shipping: { id: int, manifestId: int, number: str, type: str, state: str } }`
#### Delete package — `DELETE /package/{id}`
- Params: `id: int`
- Returns: `204 void`
#### Delete payment — `DELETE /payment/{id}`
- Params: `id: int`
- Returns: `204 void`
#### List POS terminals — `GET /posterminal`
_none_
- Returns: `200 { posterminals: T10[] }`
@@ -543,6 +570,15 @@ _none_
#### Search images via Serper.dev and stream WebP thumbnails (SSE) — `POST /serper/images`
- Params: `body: { q: str, gl?: str, hl?: str, num?: int, preferSource?: bool }`
- Returns: `200 SSE`
#### Delete shipping — `DELETE /shipping/{id}`
- Params: `id: int`
- Returns: `204 void`
#### List packages of a shipping — `GET /shipping/{id}/package`
- Params: `id: int`
- Returns: `200 { packages: { id: int, shippingId: int, entityaddressId: int, number: str, type: str, state: str, details?: str|null }[] }`
#### Create package for a shipping — `POST /shipping/{id}/package`
- Params: `id: int, body: { entityaddressId: int, type: str, state: str, details?: str }`
- Returns: `201 { package: { id: int, shippingId: int, entityaddressId: int, number: str, type: str, state: str, details?: str|null } }`
#### Adjust stock manually — `POST /stock/adjust`
- Params: `body: { sku?: str, warehouseId?: int, amount?: num, text?: str|null, lines?: { sku: str, amount: num, warehouseId?: int, text?: str }[] }`
- Returns: `201 { manifest: { id: int, number: str, type: str, date: str, createdAt: str }, lines: { manifestarticle_id: int, sku: str, name: str, warehouse_id: int, amount: num, text: str }[], ledger: T15[] }`

View File

@@ -1,6 +1,6 @@
# Dash API Routes Overview
Overview of all 146 API endpoints across all modules.
Overview of all 158 API endpoints across all modules.
### Notation
@@ -52,6 +52,15 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body).
### Routes
#### List accounts — `GET /account`
_none_
- Returns: `200 { accounts: { id: int, companyId: int, type: str, name: str }[] }`
#### Create account — `POST /account`
- Params: `body: { type: str, name: str }`
- Returns: `201 { account: { id: int, companyId: int, type: str, name: str } }`
#### Delete account — `DELETE /account/{id}`
- Params: `id: int`
- Returns: `204 void`
#### Create article in category — `POST /article`
- Params: `body: { categoryId: int, name: str, desc?: str, seo?: str, track_stock?: bool }`
- Returns: `201 T29`
@@ -388,6 +397,24 @@ _none_
#### Update order — `PATCH /order/{id}`
- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, lines?: T24[] }`
- Returns: `200 T7`
#### List payments of an order — `GET /order/{id}/payment`
- Params: `id: int`
- Returns: `200 { payments: { id: int, accountId: int, manifestId: int, type: str, amount: num }[] }`
#### Add payment to an order — `POST /order/{id}/payment`
- Params: `id: int, body: { accountId: int, type: str, amount: num }`
- Returns: `201 { payment: { id: int, accountId: int, manifestId: int, type: str, amount: num } }`
#### List shippings of an order — `GET /order/{id}/shipping`
- Params: `id: int`
- Returns: `200 { shippings: { id: int, manifestId: int, number: str, type: str, state: str }[] }`
#### Create shipping for an order — `POST /order/{id}/shipping`
- Params: `id: int, body: { type: str, state: str }`
- Returns: `201 { shipping: { id: int, manifestId: int, number: str, type: str, state: str } }`
#### Delete package — `DELETE /package/{id}`
- Params: `id: int`
- Returns: `204 void`
#### Delete payment — `DELETE /payment/{id}`
- Params: `id: int`
- Returns: `204 void`
#### List printers — `GET /printer`
_none_
- Returns: `200 { printers: T22[] }`
@@ -439,6 +466,15 @@ _none_
#### Update quote — `PATCH /quote/{id}`
- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, lines?: T24[] }`
- Returns: `200 T8`
#### Delete shipping — `DELETE /shipping/{id}`
- Params: `id: int`
- Returns: `204 void`
#### List packages of a shipping — `GET /shipping/{id}/package`
- Params: `id: int`
- Returns: `200 { packages: { id: int, shippingId: int, entityaddressId: int, number: str, type: str, state: str, details?: str|null }[] }`
#### Create package for a shipping — `POST /shipping/{id}/package`
- Params: `id: int, body: { entityaddressId: int, type: str, state: str, details?: str }`
- Returns: `201 { package: { id: int, shippingId: int, entityaddressId: int, number: str, type: str, state: str, details?: str|null } }`
#### Adjust stock manually — `POST /stock/adjust`
- Params: `body: { sku?: str, warehouseId?: int, amount?: num, text?: str|null, lines?: { sku: str, amount: num, warehouseId?: int, text?: str }[] }`
- Returns: `201 { manifest: { id: int, number: str, type: str, date: str, createdAt: str }, lines: { manifestarticle_id: int, sku: str, name: str, warehouse_id: int, amount: num, text: str }[], ledger: T14[] }`

View File

@@ -0,0 +1,35 @@
# `GET /account`
Protected. Requires a valid session cookie (`credentials: 'include'`).
List accounts
Lists payment accounts for the selected company.
## Response `200`
List of accounts
```json
{
"accounts": [
{
"id": 1,
"companyId": 1,
"type": "…",
"name": "…"
}
]
}
```
## Errors
| Status | Description |
|--------|-------------|
| 400 | No company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

View File

@@ -0,0 +1,48 @@
# `POST /account`
Protected. Requires a valid session cookie (`credentials: 'include'`).
Create account
Creates a payment account for the selected company.
## Body (JSON)
| Field | Required | Type | Description |
|-------|----------|------|-------------|
| `type` | yes | string | |
| `name` | yes | string | |
```json
{
"type": "…",
"name": "…"
}
```
## Response `201`
Created account
```json
{
"account": {
"id": 1,
"companyId": 1,
"type": "…",
"name": "…"
}
}
```
## Errors
| Status | Description |
|--------|-------------|
| 400 | Validation error or no company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Not authorized — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

View File

@@ -0,0 +1,32 @@
# `DELETE /account/{id}`
Protected. Requires a valid session cookie (`credentials: 'include'`).
Delete account
Deletes an account of the selected company. Its payments are removed as well.
## Path parameters
| Param | Required | Type | Description |
|-------|----------|------|-------------|
| `id` | yes | integer | Account ID |
## Response `204`
Account deleted
_No content_
## Errors
| Status | Description |
|--------|-------------|
| 400 | Invalid ID or no company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Not authorized — `{"error":"…"}` |
| 404 | Account not found — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

View File

@@ -0,0 +1,43 @@
# `GET /order/{id}/payment`
Protected. Requires a valid session cookie (`credentials: 'include'`).
List payments of an order
Lists payments of the order (manifest) for the selected company.
## Path parameters
| Param | Required | Type | Description |
|-------|----------|------|-------------|
| `id` | yes | integer | Order ID |
## Response `200`
List of payments
```json
{
"payments": [
{
"id": 1,
"accountId": 1,
"manifestId": 1,
"type": "…",
"amount": 1
}
]
}
```
## Errors
| Status | Description |
|--------|-------------|
| 400 | Invalid ID or no company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 404 | Order not found — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

View File

@@ -0,0 +1,58 @@
# `POST /order/{id}/payment`
Protected. Requires a valid session cookie (`credentials: 'include'`).
Add payment to an order
Adds a payment to the order using an account of the selected company.
## Path parameters
| Param | Required | Type | Description |
|-------|----------|------|-------------|
| `id` | yes | integer | Order ID |
## Body (JSON)
| Field | Required | Type | Description |
|-------|----------|------|-------------|
| `accountId` | yes | integer | |
| `type` | yes | string | |
| `amount` | yes | number | |
```json
{
"accountId": 1,
"type": "…",
"amount": 1
}
```
## Response `201`
Created payment
```json
{
"payment": {
"id": 1,
"accountId": 1,
"manifestId": 1,
"type": "…",
"amount": 1
}
}
```
## Errors
| Status | Description |
|--------|-------------|
| 400 | Validation error or no company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Not authorized — `{"error":"…"}` |
| 404 | Order or account not found — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

View File

@@ -0,0 +1,43 @@
# `GET /order/{id}/shipping`
Protected. Requires a valid session cookie (`credentials: 'include'`).
List shippings of an order
Lists shippings of the order (manifest) for the selected company.
## Path parameters
| Param | Required | Type | Description |
|-------|----------|------|-------------|
| `id` | yes | integer | Order ID |
## Response `200`
List of shippings
```json
{
"shippings": [
{
"id": 1,
"manifestId": 1,
"number": "…",
"type": "…",
"state": "…"
}
]
}
```
## Errors
| Status | Description |
|--------|-------------|
| 400 | Invalid ID or no company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 404 | Order not found — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

View File

@@ -0,0 +1,58 @@
# `POST /order/{id}/shipping`
Protected. Requires a valid session cookie (`credentials: 'include'`).
Create shipping for an order
Creates a shipping for the order. The shipping number is allocated from the
`shipping` number sequence (prefix and counter from the numbers API).
## Path parameters
| Param | Required | Type | Description |
|-------|----------|------|-------------|
| `id` | yes | integer | Order ID |
## Body (JSON)
| Field | Required | Type | Description |
|-------|----------|------|-------------|
| `type` | yes | string | |
| `state` | yes | string | |
```json
{
"type": "…",
"state": "…"
}
```
## Response `201`
Created shipping
```json
{
"shipping": {
"id": 1,
"manifestId": 1,
"number": "…",
"type": "…",
"state": "…"
}
}
```
## Errors
| Status | Description |
|--------|-------------|
| 400 | Validation error or no company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Not authorized — `{"error":"…"}` |
| 404 | Order not found — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

View File

@@ -0,0 +1,31 @@
# `DELETE /package/{id}`
Protected. Requires a valid session cookie (`credentials: 'include'`).
Delete package
Deletes a package of the selected company.
## Path parameters
| Param | Required | Type | Description |
|-------|----------|------|-------------|
| `id` | yes | integer | Package ID |
## Response `204`
Package deleted
_No content_
## Errors
| Status | Description |
|--------|-------------|
| 400 | Invalid ID or no company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Not authorized — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

View File

@@ -0,0 +1,31 @@
# `DELETE /payment/{id}`
Protected. Requires a valid session cookie (`credentials: 'include'`).
Delete payment
Deletes a payment of an order in the selected company.
## Path parameters
| Param | Required | Type | Description |
|-------|----------|------|-------------|
| `id` | yes | integer | Payment ID |
## Response `204`
Payment deleted
_No content_
## Errors
| Status | Description |
|--------|-------------|
| 400 | Invalid ID or no company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Not authorized — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

View File

@@ -0,0 +1,31 @@
# `DELETE /shipping/{id}`
Protected. Requires a valid session cookie (`credentials: 'include'`).
Delete shipping
Deletes a shipping of the selected company. Its packages are removed as well.
## Path parameters
| Param | Required | Type | Description |
|-------|----------|------|-------------|
| `id` | yes | integer | Shipping ID |
## Response `204`
Shipping deleted
_No content_
## Errors
| Status | Description |
|--------|-------------|
| 400 | Invalid ID or no company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Not authorized — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

View File

@@ -0,0 +1,45 @@
# `GET /shipping/{id}/package`
Protected. Requires a valid session cookie (`credentials: 'include'`).
List packages of a shipping
Lists packages of the shipping for the selected company.
## Path parameters
| Param | Required | Type | Description |
|-------|----------|------|-------------|
| `id` | yes | integer | Shipping ID |
## Response `200`
List of packages
```json
{
"packages": [
{
"id": 1,
"shippingId": 1,
"entityaddressId": 1,
"number": "…",
"type": "…",
"state": "…",
"details": "…"
}
]
}
```
## Errors
| Status | Description |
|--------|-------------|
| 400 | Invalid ID or no company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 404 | Shipping not found — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

View File

@@ -0,0 +1,64 @@
# `POST /shipping/{id}/package`
Protected. Requires a valid session cookie (`credentials: 'include'`).
Create package for a shipping
Creates a package for the shipping. The package number is allocated from the
`package` number sequence. `entityaddressId` is required (target address).
## Path parameters
| Param | Required | Type | Description |
|-------|----------|------|-------------|
| `id` | yes | integer | Shipping ID |
## Body (JSON)
| Field | Required | Type | Description |
|-------|----------|------|-------------|
| `entityaddressId` | yes | integer | |
| `type` | yes | string | |
| `state` | yes | string | |
| `details` | no | string | |
```json
{
"entityaddressId": 1,
"type": "…",
"state": "…",
"details": "…"
}
```
## Response `201`
Created package
```json
{
"package": {
"id": 1,
"shippingId": 1,
"entityaddressId": 1,
"number": "…",
"type": "…",
"state": "…",
"details": "…"
}
}
```
## Errors
| Status | Description |
|--------|-------------|
| 400 | Validation error or no company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Not authorized — `{"error":"…"}` |
| 404 | Shipping or entity address not found — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

1
package-lock.json generated
View File

@@ -34,6 +34,7 @@
"vm2": "^3.12.2"
},
"devDependencies": {
"acorn": "^8.19.0",
"swagger-jsdoc": "^6.3.0"
}
},

View File

@@ -12,6 +12,7 @@
"manage-users": "node scripts/manageUsers.js",
"wipe-company": "node scripts/wipeCompany.js",
"docs:api": "node scripts/generateApiDocs.js",
"review:functions": "node scripts/extractFunctions.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
@@ -40,6 +41,7 @@
"vm2": "^3.12.2"
},
"devDependencies": {
"acorn": "^8.19.0",
"swagger-jsdoc": "^6.3.0"
},
"allowScripts": {

133
scripts/extractFunctions.js Normal file
View File

@@ -0,0 +1,133 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { parse } from 'acorn';
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
const rootDir = path.resolve(scriptDir, '..');
const outputDir = path.join(rootDir, 'output');
const excludedDirs = new Set(['.git', 'data', 'node_modules', 'output']);
const functions = [];
function collectJavaScriptFiles(dir) {
const files = [];
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
if (entry.isDirectory() && excludedDirs.has(entry.name)) continue;
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
files.push(...collectJavaScriptFiles(fullPath));
} else if (entry.isFile() && entry.name.endsWith('.js')) {
files.push(fullPath);
}
}
return files;
}
function inferredName(node, parent) {
if (node.id?.name) return node.id.name;
if (parent?.type === 'VariableDeclarator' && parent.id.type === 'Identifier') {
return parent.id.name;
}
if (parent?.type === 'AssignmentExpression') {
return parent.left.name ?? parent.left.property?.name ?? 'assigned';
}
if (parent?.type === 'Property' || parent?.type === 'MethodDefinition') {
return parent.key.name ?? parent.key.value ?? 'method';
}
if (parent?.type === 'ExportDefaultDeclaration') return 'default';
return 'anonymous';
}
function visit(node, parent, source, relativePath) {
if (!node || typeof node !== 'object') return;
if (node.type === 'FunctionDeclaration'
|| node.type === 'FunctionExpression'
|| node.type === 'ArrowFunctionExpression') {
const method = (parent?.type === 'MethodDefinition'
|| (parent?.type === 'Property' && parent.method))
? parent
: node;
const start = method.start;
const end = method.end;
const startLine = source.slice(0, start).split('\n').length;
const endLine = source.slice(0, end).split('\n').length;
const name = inferredName(node, parent);
let signature = source.slice(start, node.body.start).replace(/\s+/g, ' ').trim();
if (method === node
&& name !== 'anonymous'
&& node.type !== 'FunctionDeclaration'
&& parent?.type !== 'ExportDefaultDeclaration') {
signature = `${name} = ${signature}`;
}
functions.push({
source: relativePath,
start,
end,
startLine,
endLine,
name,
signature,
code: source.slice(start, end),
});
}
for (const [key, value] of Object.entries(node)) {
if (key === 'start' || key === 'end' || key === 'loc') continue;
if (Array.isArray(value)) {
for (const child of value) visit(child, node, source, relativePath);
} else if (value && typeof value === 'object') {
visit(value, node, source, relativePath);
}
}
}
const sourceFiles = collectJavaScriptFiles(rootDir).sort();
for (const filePath of sourceFiles) {
const source = fs.readFileSync(filePath, 'utf8');
const relativePath = path.relative(rootDir, filePath).split(path.sep).join('/');
let ast;
try {
ast = parse(source, {
ecmaVersion: 'latest',
sourceType: 'module',
allowHashBang: true,
});
} catch (error) {
throw new Error(`Could not parse ${relativePath}: ${error.message}`, { cause: error });
}
visit(ast, null, source, relativePath);
}
fs.mkdirSync(outputDir, { recursive: true });
for (const entry of fs.readdirSync(outputDir, { withFileTypes: true })) {
if (entry.isFile() && /^function-.*\.js$/.test(entry.name)) {
fs.unlinkSync(path.join(outputDir, entry.name));
}
}
functions.sort((a, b) => a.source.localeCompare(b.source) || a.start - b.start);
const nameCounts = new Map();
for (const fn of functions) {
const sourceSlug = fn.source.replace(/\.js$/, '').replace(/[^a-zA-Z0-9_-]+/g, '-');
const nameSlug = fn.name.replace(/[^a-zA-Z0-9_-]+/g, '-');
const baseName = `function-${sourceSlug}-L${fn.startLine}-${nameSlug}`;
const sequence = (nameCounts.get(baseName) ?? 0) + 1;
nameCounts.set(baseName, sequence);
const outputName = `${baseName}-${sequence}.js`;
const contents = [
`// Source: ${fn.source} (lines ${fn.startLine}-${fn.endLine})`,
fn.code,
'',
].join('\n');
fs.writeFileSync(path.join(outputDir, outputName), contents);
}
const index = functions
.map(fn => `${fn.source}:${fn.startLine}\t${fn.signature}`)
.join('\n');
fs.writeFileSync(path.join(outputDir, 'functions.txt'), `${index}\n`);
console.log(`Extracted ${functions.length} functions from ${sourceFiles.length} JavaScript files into ${path.relative(rootDir, outputDir)}/`);

View File

@@ -1,5 +1,5 @@
import env from '../../config/env.js';
export function meJson(res, status, body) {
export function respondWithAuthenticatedUser(res, status, body) {
return res.status(status).json({ ...body, googleClientId: env.googleClientId });
}

View File

@@ -1096,6 +1096,67 @@
* description: Tool response content payload
* answer:
* description: Shorthand answer for ask_user response
* Account:
* type: object
* required: [id, companyId, type, name]
* properties:
* id:
* type: integer
* companyId:
* type: integer
* type:
* type: string
* name:
* type: string
* Payment:
* type: object
* required: [id, accountId, manifestId, type, amount]
* properties:
* id:
* type: integer
* accountId:
* type: integer
* manifestId:
* type: integer
* type:
* type: string
* amount:
* type: number
* Shipping:
* type: object
* required: [id, manifestId, number, type, state]
* properties:
* id:
* type: integer
* manifestId:
* type: integer
* number:
* type: string
* description: Shipping number from the `shipping` number sequence
* type:
* type: string
* state:
* type: string
* Package:
* type: object
* required: [id, shippingId, entityaddressId, number, type, state]
* properties:
* id:
* type: integer
* shippingId:
* type: integer
* entityaddressId:
* type: integer
* number:
* type: string
* description: Package number from the `package` number sequence
* type:
* type: string
* state:
* type: string
* details:
* type: string
* nullable: true
* Printer:
* type: object
* required: [id, company_id, address, type, sort]

213
src/api/routes/account.js Normal file
View File

@@ -0,0 +1,213 @@
import * as db from '../../db/account.js';
function requireSelectedCompany(req) {
const companyId = req.user?.selectedCompanyId;
if (companyId == null) {
return { error: 'No company selected' };
}
return { companyId };
}
function parsePositiveInt(value, field) {
const id = Number(value);
if (!Number.isInteger(id) || id <= 0) {
return { error: `${field} is required` };
}
return { id };
}
function accountErrorStatus(error) {
if (error.message === 'No company selected') return 400;
if (error.message === 'Not authorized') return 403;
if (error.message?.endsWith(' not found')) return 404;
if (error.message?.endsWith(' required')) return 400;
return 500;
}
/**
* Registers account routes for the authenticated user's selected company.
* @param {import('express').Application} app
* @param {string} [basePath='']
*/
export function register(app, basePath = '') {
/**
* @openapi
* /account:
* get:
* tags: [Account]
* summary: List accounts
* description: Lists payment accounts for the selected company.
* security:
* - cookieAuth: []
* responses:
* 200:
* description: List of accounts
* content:
* application/json:
* schema:
* type: object
* properties:
* accounts:
* type: array
* items:
* $ref: '#/components/schemas/Account'
* 400:
* description: No company selected
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 401:
* description: Unauthorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
app.get(`${basePath}/account`, async (req, res) => {
const selected = requireSelectedCompany(req);
if (selected.error) {
return res.status(400).json({ error: selected.error });
}
try {
const accounts = await db.list(req.user.id);
res.status(200).json({ accounts });
} catch (error) {
const status = accountErrorStatus(error);
if (status === 500) console.error('List accounts failed:', error);
res.status(status).json({ error: error.message });
}
});
/**
* @openapi
* /account:
* post:
* tags: [Account]
* summary: Create account
* description: Creates a payment account for the selected company.
* security:
* - cookieAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required: [type, name]
* properties:
* type:
* type: string
* name:
* type: string
* responses:
* 201:
* description: Created account
* content:
* application/json:
* schema:
* type: object
* properties:
* account:
* $ref: '#/components/schemas/Account'
* 400:
* description: Validation error or no company selected
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 401:
* description: Unauthorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 403:
* description: Not authorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
app.post(`${basePath}/account`, async (req, res) => {
const selected = requireSelectedCompany(req);
if (selected.error) {
return res.status(400).json({ error: selected.error });
}
try {
const account = await db.create(req.user.id, req.body ?? {});
res.status(201).json({ account });
} catch (error) {
const status = accountErrorStatus(error);
if (status === 500) console.error('Create account failed:', error);
res.status(status).json({ error: error.message });
}
});
/**
* @openapi
* /account/{id}:
* delete:
* tags: [Account]
* summary: Delete account
* description: Deletes an account of the selected company. Its payments are removed as well.
* security:
* - cookieAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: Account ID
* responses:
* 204:
* description: Account deleted
* 400:
* description: Invalid ID or no company selected
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 401:
* description: Unauthorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 403:
* description: Not authorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 404:
* description: Account not found
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
app.delete(`${basePath}/account/:id`, async (req, res) => {
const selected = requireSelectedCompany(req);
if (selected.error) {
return res.status(400).json({ error: selected.error });
}
const parsedId = parsePositiveInt(req.params.id, 'id');
if (parsedId.error) {
return res.status(400).json({ error: parsedId.error });
}
try {
await db.remove(req.user.id, parsedId.id);
res.status(204).end();
} catch (error) {
const status = accountErrorStatus(error);
if (status === 500) console.error('Delete account failed:', error);
res.status(status).json({ error: error.message });
}
});
}

252
src/api/routes/package.js Normal file
View File

@@ -0,0 +1,252 @@
import * as db from '../../db/package.js';
function requireSelectedCompany(req) {
const companyId = req.user?.selectedCompanyId;
if (companyId == null) {
return { error: 'No company selected' };
}
return { companyId };
}
function parsePositiveInt(value, field) {
const id = Number(value);
if (!Number.isInteger(id) || id <= 0) {
return { error: `${field} is required` };
}
return { id };
}
function packageErrorStatus(error) {
if (error.message === 'No company selected') return 400;
if (error.message === 'Not authorized') return 403;
if (error.message?.endsWith(' not found')) return 404;
if (error.message?.endsWith(' required')) return 400;
return 500;
}
/**
* Registers package routes (packages belong to a shipping and target an entity address).
* @param {import('express').Application} app
* @param {string} [basePath='']
*/
export function register(app, basePath = '') {
/**
* @openapi
* /shipping/{id}/package:
* get:
* tags: [Package]
* summary: List packages of a shipping
* description: Lists packages of the shipping for the selected company.
* security:
* - cookieAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: Shipping ID
* responses:
* 200:
* description: List of packages
* content:
* application/json:
* schema:
* type: object
* properties:
* packages:
* type: array
* items:
* $ref: '#/components/schemas/Package'
* 400:
* description: Invalid ID or no company selected
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 401:
* description: Unauthorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 404:
* description: Shipping not found
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
app.get(`${basePath}/shipping/:id/package`, async (req, res) => {
const selected = requireSelectedCompany(req);
if (selected.error) {
return res.status(400).json({ error: selected.error });
}
const parsedId = parsePositiveInt(req.params.id, 'id');
if (parsedId.error) {
return res.status(400).json({ error: parsedId.error });
}
try {
const packages = await db.listByShippingId(req.user.id, parsedId.id);
res.status(200).json({ packages });
} catch (error) {
const status = packageErrorStatus(error);
if (status === 500) console.error('List packages failed:', error);
res.status(status).json({ error: error.message });
}
});
/**
* @openapi
* /shipping/{id}/package:
* post:
* tags: [Package]
* summary: Create package for a shipping
* description: |
* Creates a package for the shipping. The package number is allocated from the
* `package` number sequence. `entityaddressId` is required (target address).
* security:
* - cookieAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: Shipping ID
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required: [entityaddressId, type, state]
* properties:
* entityaddressId:
* type: integer
* type:
* type: string
* state:
* type: string
* details:
* type: string
* responses:
* 201:
* description: Created package
* content:
* application/json:
* schema:
* type: object
* properties:
* package:
* $ref: '#/components/schemas/Package'
* 400:
* description: Validation error or no company selected
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 401:
* description: Unauthorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 403:
* description: Not authorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 404:
* description: Shipping or entity address not found
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
app.post(`${basePath}/shipping/:id/package`, async (req, res) => {
const selected = requireSelectedCompany(req);
if (selected.error) {
return res.status(400).json({ error: selected.error });
}
const parsedId = parsePositiveInt(req.params.id, 'id');
if (parsedId.error) {
return res.status(400).json({ error: parsedId.error });
}
try {
const pkg = await db.create(req.user.id, {
...(req.body ?? {}),
shippingId: parsedId.id,
});
res.status(201).json({ package: pkg });
} catch (error) {
const status = packageErrorStatus(error);
if (status === 500) console.error('Create package failed:', error);
res.status(status).json({ error: error.message });
}
});
/**
* @openapi
* /package/{id}:
* delete:
* tags: [Package]
* summary: Delete package
* description: Deletes a package of the selected company.
* security:
* - cookieAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: Package ID
* responses:
* 204:
* description: Package deleted
* 400:
* description: Invalid ID or no company selected
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 401:
* description: Unauthorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 403:
* description: Not authorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
app.delete(`${basePath}/package/:id`, async (req, res) => {
const selected = requireSelectedCompany(req);
if (selected.error) {
return res.status(400).json({ error: selected.error });
}
const parsedId = parsePositiveInt(req.params.id, 'id');
if (parsedId.error) {
return res.status(400).json({ error: parsedId.error });
}
try {
await db.remove(req.user.id, parsedId.id);
res.status(204).end();
} catch (error) {
const status = packageErrorStatus(error);
if (status === 500) console.error('Delete package failed:', error);
res.status(status).json({ error: error.message });
}
});
}

249
src/api/routes/payment.js Normal file
View File

@@ -0,0 +1,249 @@
import * as db from '../../db/payment.js';
function requireSelectedCompany(req) {
const companyId = req.user?.selectedCompanyId;
if (companyId == null) {
return { error: 'No company selected' };
}
return { companyId };
}
function parsePositiveInt(value, field) {
const id = Number(value);
if (!Number.isInteger(id) || id <= 0) {
return { error: `${field} is required` };
}
return { id };
}
function paymentErrorStatus(error) {
if (error.message === 'No company selected') return 400;
if (error.message === 'Not authorized') return 403;
if (error.message?.endsWith(' not found')) return 404;
if (error.message?.endsWith(' required')) return 400;
if (error.message === 'Invalid payment amount') return 400;
return 500;
}
/**
* Registers payment routes (payments belong to an order / manifest).
* @param {import('express').Application} app
* @param {string} [basePath='']
*/
export function register(app, basePath = '') {
/**
* @openapi
* /order/{id}/payment:
* get:
* tags: [Payment]
* summary: List payments of an order
* description: Lists payments of the order (manifest) for the selected company.
* security:
* - cookieAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: Order ID
* responses:
* 200:
* description: List of payments
* content:
* application/json:
* schema:
* type: object
* properties:
* payments:
* type: array
* items:
* $ref: '#/components/schemas/Payment'
* 400:
* description: Invalid ID or no company selected
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 401:
* description: Unauthorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 404:
* description: Order not found
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
app.get(`${basePath}/order/:id/payment`, async (req, res) => {
const selected = requireSelectedCompany(req);
if (selected.error) {
return res.status(400).json({ error: selected.error });
}
const parsedId = parsePositiveInt(req.params.id, 'id');
if (parsedId.error) {
return res.status(400).json({ error: parsedId.error });
}
try {
const payments = await db.listByManifestId(req.user.id, parsedId.id);
res.status(200).json({ payments });
} catch (error) {
const status = paymentErrorStatus(error);
if (status === 500) console.error('List payments failed:', error);
res.status(status).json({ error: error.message });
}
});
/**
* @openapi
* /order/{id}/payment:
* post:
* tags: [Payment]
* summary: Add payment to an order
* description: Adds a payment to the order using an account of the selected company.
* security:
* - cookieAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: Order ID
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required: [accountId, type, amount]
* properties:
* accountId:
* type: integer
* type:
* type: string
* amount:
* type: number
* responses:
* 201:
* description: Created payment
* content:
* application/json:
* schema:
* type: object
* properties:
* payment:
* $ref: '#/components/schemas/Payment'
* 400:
* description: Validation error or no company selected
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 401:
* description: Unauthorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 403:
* description: Not authorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 404:
* description: Order or account not found
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
app.post(`${basePath}/order/:id/payment`, async (req, res) => {
const selected = requireSelectedCompany(req);
if (selected.error) {
return res.status(400).json({ error: selected.error });
}
const parsedId = parsePositiveInt(req.params.id, 'id');
if (parsedId.error) {
return res.status(400).json({ error: parsedId.error });
}
try {
const payment = await db.create(req.user.id, {
...(req.body ?? {}),
manifestId: parsedId.id,
});
res.status(201).json({ payment });
} catch (error) {
const status = paymentErrorStatus(error);
if (status === 500) console.error('Create payment failed:', error);
res.status(status).json({ error: error.message });
}
});
/**
* @openapi
* /payment/{id}:
* delete:
* tags: [Payment]
* summary: Delete payment
* description: Deletes a payment of an order in the selected company.
* security:
* - cookieAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: Payment ID
* responses:
* 204:
* description: Payment deleted
* 400:
* description: Invalid ID or no company selected
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 401:
* description: Unauthorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 403:
* description: Not authorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
app.delete(`${basePath}/payment/:id`, async (req, res) => {
const selected = requireSelectedCompany(req);
if (selected.error) {
return res.status(400).json({ error: selected.error });
}
const parsedId = parsePositiveInt(req.params.id, 'id');
if (parsedId.error) {
return res.status(400).json({ error: parsedId.error });
}
try {
await db.remove(req.user.id, parsedId.id);
res.status(204).end();
} catch (error) {
const status = paymentErrorStatus(error);
if (status === 500) console.error('Delete payment failed:', error);
res.status(status).json({ error: error.message });
}
});
}

248
src/api/routes/shipping.js Normal file
View File

@@ -0,0 +1,248 @@
import * as db from '../../db/shipping.js';
function requireSelectedCompany(req) {
const companyId = req.user?.selectedCompanyId;
if (companyId == null) {
return { error: 'No company selected' };
}
return { companyId };
}
function parsePositiveInt(value, field) {
const id = Number(value);
if (!Number.isInteger(id) || id <= 0) {
return { error: `${field} is required` };
}
return { id };
}
function shippingErrorStatus(error) {
if (error.message === 'No company selected') return 400;
if (error.message === 'Not authorized') return 403;
if (error.message?.endsWith(' not found')) return 404;
if (error.message?.endsWith(' required')) return 400;
return 500;
}
/**
* Registers shipping routes (shippings belong to an order / manifest).
* @param {import('express').Application} app
* @param {string} [basePath='']
*/
export function register(app, basePath = '') {
/**
* @openapi
* /order/{id}/shipping:
* get:
* tags: [Shipping]
* summary: List shippings of an order
* description: Lists shippings of the order (manifest) for the selected company.
* security:
* - cookieAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: Order ID
* responses:
* 200:
* description: List of shippings
* content:
* application/json:
* schema:
* type: object
* properties:
* shippings:
* type: array
* items:
* $ref: '#/components/schemas/Shipping'
* 400:
* description: Invalid ID or no company selected
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 401:
* description: Unauthorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 404:
* description: Order not found
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
app.get(`${basePath}/order/:id/shipping`, async (req, res) => {
const selected = requireSelectedCompany(req);
if (selected.error) {
return res.status(400).json({ error: selected.error });
}
const parsedId = parsePositiveInt(req.params.id, 'id');
if (parsedId.error) {
return res.status(400).json({ error: parsedId.error });
}
try {
const shippings = await db.listByManifestId(req.user.id, parsedId.id);
res.status(200).json({ shippings });
} catch (error) {
const status = shippingErrorStatus(error);
if (status === 500) console.error('List shippings failed:', error);
res.status(status).json({ error: error.message });
}
});
/**
* @openapi
* /order/{id}/shipping:
* post:
* tags: [Shipping]
* summary: Create shipping for an order
* description: |
* Creates a shipping for the order. The shipping number is allocated from the
* `shipping` number sequence (prefix and counter from the numbers API).
* security:
* - cookieAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: Order ID
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required: [type, state]
* properties:
* type:
* type: string
* state:
* type: string
* responses:
* 201:
* description: Created shipping
* content:
* application/json:
* schema:
* type: object
* properties:
* shipping:
* $ref: '#/components/schemas/Shipping'
* 400:
* description: Validation error or no company selected
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 401:
* description: Unauthorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 403:
* description: Not authorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 404:
* description: Order not found
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
app.post(`${basePath}/order/:id/shipping`, async (req, res) => {
const selected = requireSelectedCompany(req);
if (selected.error) {
return res.status(400).json({ error: selected.error });
}
const parsedId = parsePositiveInt(req.params.id, 'id');
if (parsedId.error) {
return res.status(400).json({ error: parsedId.error });
}
try {
const shipping = await db.create(req.user.id, {
...(req.body ?? {}),
manifestId: parsedId.id,
});
res.status(201).json({ shipping });
} catch (error) {
const status = shippingErrorStatus(error);
if (status === 500) console.error('Create shipping failed:', error);
res.status(status).json({ error: error.message });
}
});
/**
* @openapi
* /shipping/{id}:
* delete:
* tags: [Shipping]
* summary: Delete shipping
* description: Deletes a shipping of the selected company. Its packages are removed as well.
* security:
* - cookieAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: Shipping ID
* responses:
* 204:
* description: Shipping deleted
* 400:
* description: Invalid ID or no company selected
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 401:
* description: Unauthorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 403:
* description: Not authorized
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
app.delete(`${basePath}/shipping/:id`, async (req, res) => {
const selected = requireSelectedCompany(req);
if (selected.error) {
return res.status(400).json({ error: selected.error });
}
const parsedId = parsePositiveInt(req.params.id, 'id');
if (parsedId.error) {
return res.status(400).json({ error: parsedId.error });
}
try {
await db.remove(req.user.id, parsedId.id);
res.status(204).end();
} catch (error) {
const status = shippingErrorStatus(error);
if (status === 500) console.error('Delete shipping failed:', error);
res.status(status).json({ error: error.message });
}
});
}

16
src/db/account.js Normal file
View File

@@ -0,0 +1,16 @@
import { getStore } from './store.js';
export async function list(googleAuthId) {
const store = await getStore();
return store.account.list(googleAuthId);
}
export async function create(googleAuthId, data) {
const store = await getStore();
return store.account.create(googleAuthId, data);
}
export async function remove(googleAuthId, id) {
const store = await getStore();
return store.account.delete(googleAuthId, id);
}

16
src/db/package.js Normal file
View File

@@ -0,0 +1,16 @@
import { getStore } from './store.js';
export async function listByShippingId(googleAuthId, shippingId) {
const store = await getStore();
return store.package.listByShippingId(googleAuthId, shippingId);
}
export async function create(googleAuthId, data) {
const store = await getStore();
return store.package.create(googleAuthId, data);
}
export async function remove(googleAuthId, id) {
const store = await getStore();
return store.package.delete(googleAuthId, id);
}

16
src/db/payment.js Normal file
View File

@@ -0,0 +1,16 @@
import { getStore } from './store.js';
export async function listByManifestId(googleAuthId, manifestId) {
const store = await getStore();
return store.payment.listByManifestId(googleAuthId, manifestId);
}
export async function create(googleAuthId, data) {
const store = await getStore();
return store.payment.create(googleAuthId, data);
}
export async function remove(googleAuthId, id) {
const store = await getStore();
return store.payment.delete(googleAuthId, id);
}

16
src/db/shipping.js Normal file
View File

@@ -0,0 +1,16 @@
import { getStore } from './store.js';
export async function listByManifestId(googleAuthId, manifestId) {
const store = await getStore();
return store.shipping.listByManifestId(googleAuthId, manifestId);
}
export async function create(googleAuthId, data) {
const store = await getStore();
return store.shipping.create(googleAuthId, data);
}
export async function remove(googleAuthId, id) {
const store = await getStore();
return store.shipping.delete(googleAuthId, id);
}

View File

@@ -48,6 +48,10 @@ const STORE_NAMESPACES = [
'categoryattributevalue',
'modelconversation',
'modelmessage',
'account',
'payment',
'shipping',
'package',
];
/**