u
This commit is contained in:
@@ -9,16 +9,16 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body).
|
||||
|
||||
### Reusable types
|
||||
|
||||
- `T1` = `{ id: int, number: str, type: str, entityId: int, date: str, lines: T18[], externalNumber?: str|null, createdAt?: str, shipping?: T20|null, shippings?: T20[], payment?: T29|null, payments?: T29[], paid?: bool, shipped?: bool, delivered?: bool, orderTotal?: num, paidTotal?: num, orderedQuantity?: num, shippedQuantity?: num, shipments?: T2[], files?: T9[] }`
|
||||
- `T2` = `{ id: int, number: str, type: str, date: str, lines: T18[], shippings: T20[], entityId?: int|null, externalNumber?: str|null, createdAt?: str|null, lineCount?: int, packageCount?: int, files?: T9[], state?: str }`
|
||||
- `T3` = `{ id: int, number: str, type: str, date: str, lines: T18[], entityId?: int|null, externalNumber?: str|null, createdAt?: str, positions?: int, grossTotal?: num, totalQuantity?: num, customerName?: str|null, files?: T9[] }`
|
||||
- `T4` = `{ id: int, number: str, type: str, supplierId: int, date: str, lines: T21[], files: T9[], externalNumber?: str|null, createdAt?: str, ledger?: T36 }`
|
||||
- `T1` = `{ id: int, number: str, type: str, entityId: int, date: str, lines: T18[], externalNumber?: str|null, createdAt?: str, shipping?: T20|null, shippings?: T20[], payment?: T29|null, payments?: T29[], paid?: bool, shipped?: bool, delivered?: bool, orderTotal?: num, paidTotal?: num, orderedQuantity?: num, shippedQuantity?: num, shipments?: T2[], files?: T10[] }`
|
||||
- `T2` = `{ id: int, number: str, type: str, date: str, lines: T18[], shippings: T20[], entityId?: int|null, externalNumber?: str|null, createdAt?: str|null, lineCount?: int, packageCount?: int, files?: T10[], state?: str }`
|
||||
- `T3` = `{ id: int, number: str, type: str, date: str, lines: T18[], entityId?: int|null, externalNumber?: str|null, createdAt?: str, positions?: int, grossTotal?: num, totalQuantity?: num, customerName?: str|null, files?: T10[] }`
|
||||
- `T4` = `{ id: int, number: str, type: str, supplierId: int, date: str, lines: T21[], files: T10[], externalNumber?: str|null, createdAt?: str, ledger?: T36 }`
|
||||
- `T5` = `{ name: str, path: str, totalSizeBytes: int, timestamp: str|null, manifest?: T6|null }`
|
||||
- `T6` = `{ id: str, timestamp: str, dateKey: str, monthKey: str, dbProvider: str, files: T11, totalSizeBytes: int, durationMs: int }`
|
||||
- `T7` = `{ id: int, articleId: int, entityId: int, pu: num, sort: int, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T38|null, incoming?: { date: str, amount: num }[] }`
|
||||
- `T8` = `{ id: int, name: str, width: num, height: num, dpi: int, type: str, content: { objects: T22[] }, thumb?: str|null }`
|
||||
- `T9` = `{ id: int, manifestId: int, fileId: int, sort: int, hash: str, mimetype: str, name: str, size: int, created?: str, modified?: str, skeledash?: str|null, thumbx?: int|null, thumby?: int|null, meta?: { [k]: str }|null }`
|
||||
- `T10` = `{ id: int, name: str, pairingpin?: str|null, token?: str|null, pairedAt?: str|null, clientserial?: str|null, rootCategoryId?: int|null, syncRootArticles?: bool, lastseenAt?: str|null, updatedAt?: str|null, categoryIds?: int[] }`
|
||||
- `T7` = `{ id: int, name: str, pairingpin?: str|null, token?: str|null, pairedAt?: str|null, clientserial?: str|null, rootCategoryId?: int|null, syncRootArticles?: bool, sort?: int, enabled?: bool, clientversion?: str|null, lastseenAt?: str|null, updatedAt?: str|null, categoryIds?: int[] }`
|
||||
- `T8` = `{ id: int, articleId: int, entityId: int, pu: num, sort: int, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T38|null, incoming?: { date: str, amount: num }[] }`
|
||||
- `T9` = `{ id: int, name: str, width: num, height: num, dpi: int, type: str, content: { objects: T22[] }, thumb?: str|null }`
|
||||
- `T10` = `{ id: int, manifestId: int, fileId: int, sort: int, hash: str, mimetype: str, name: str, size: int, created?: str, modified?: str, skeledash?: str|null, thumbx?: int|null, thumby?: int|null, meta?: { [k]: str }|null }`
|
||||
- `T11` = `{ vfs: T37, storage: T37, database: T31 }`
|
||||
- `T12` = `{ id: int, entityId: int, addressType: str, firstName?: str|null, name?: str|null, street?: str|null, streetNumber?: str|null, zip?: str|null, city?: str|null, county?: str|null }`
|
||||
- `T13` = `{ id: int, company_id: int, prompt: str, max_itr: int, cron: str, created_at: str, desc?: str|null, skills?: str|null, lastagentnote?: str|null, lastrun_at?: str|null }`
|
||||
@@ -88,7 +88,7 @@ _none_
|
||||
- Returns: `204 void`
|
||||
#### Get article by SKU — `GET /article/{sku}`
|
||||
- Params: `sku: str`
|
||||
- Returns: `200 { article: { id: int, sku: str, name: str, slug: str, desc: str|null, seo: str|null, track_stock: bool, unitamount: num|null, weight: num|null, unit: T26, brand: T25|null, categoryIds: int[], photos: { sort: int, fullHash: str, fullW: int, fullH: int, fullSize: int, thumbW: int, thumbH: int, thumbHash: str }[], eans: str[], prices: { minmultiplier: num, net: num, taxRate: num, customergroupId: int }[], suppliers: T7[], undeliveredOrders: { orderId: int, orderNumber: str, date: str, amount: num, customerName?: str|null }[] } }`
|
||||
- Returns: `200 { article: { id: int, sku: str, name: str, slug: str, desc: str|null, seo: str|null, track_stock: bool, unitamount: num|null, weight: num|null, unit: T26, brand: T25|null, categoryIds: int[], photos: { sort: int, fullHash: str, fullW: int, fullH: int, fullSize: int, thumbW: int, thumbH: int, thumbHash: str }[], eans: str[], prices: { minmultiplier: num, net: num, taxRate: num, customergroupId: int }[], suppliers: T8[], undeliveredOrders: { orderId: int, orderNumber: str, date: str, amount: num, customerName?: str|null }[] } }`
|
||||
#### Update article by SKU — `PATCH /article/{sku}`
|
||||
- Params: `sku: str, body: { name?: str, desc?: str, seo?: str, unitamount?: num, unitSi?: str, track_stock?: bool }`
|
||||
- Returns: `204 void`
|
||||
@@ -163,10 +163,10 @@ _none_
|
||||
- Returns: `200 { ok: bool }`
|
||||
#### List article suppliers — `GET /article/{sku}/supplier`
|
||||
- Params: `sku: str`
|
||||
- Returns: `200 { suppliers: T7[] }`
|
||||
- Returns: `200 { suppliers: T8[] }`
|
||||
#### Assign supplier to article — `POST /article/{sku}/supplier`
|
||||
- Params: `sku: str, body: { entityId: int, sku?: str|null, pu?: num, stock?: num|null, sort?: int|null, netPrice?: num|null, taxId?: int|null, taxRate?: num|null }`
|
||||
- Returns: `201 { supplier: T7 }`
|
||||
- Returns: `201 { supplier: T8 }`
|
||||
#### Reorder article suppliers — `PUT /article/{sku}/supplier/order`
|
||||
- Params: `sku: str, body: { ids: int[] }`
|
||||
- Returns: `200 { ids: int[] }`
|
||||
@@ -175,7 +175,7 @@ _none_
|
||||
- Returns: `204 void`
|
||||
#### Update article supplier assignment — `PATCH /article/{sku}/supplier/{id}`
|
||||
- Params: `sku: str, id: int, body: { sku?: str|null, pu?: num, stock?: num|null, sort?: int, netPrice?: num|null, taxId?: int|null, taxRate?: num|null }`
|
||||
- Returns: `200 { supplier: T7 }`
|
||||
- Returns: `200 { supplier: T8 }`
|
||||
#### Set article track_stock flag by SKU — `PUT /article/{sku}/track_stock`
|
||||
- Params: `sku: str, body: { track_stock: bool }`
|
||||
- Returns: `204 void`
|
||||
@@ -424,7 +424,7 @@ _none_
|
||||
- Returns: `200 str`
|
||||
#### Generate and attach delivery note PDF (goods receipt) — `POST /delivery/{id}/pdf`
|
||||
- Params: `id: int`
|
||||
- Returns: `201 { attachment: T9, fileUrl: str }`
|
||||
- Returns: `201 { attachment: T10, fileUrl: str }`
|
||||
|
||||
### Section: /entity
|
||||
|
||||
@@ -518,19 +518,19 @@ _none_
|
||||
- Returns: `200 { labeltemplates: { id: int, name: str, width: num, height: num, dpi: int, type: str, thumb?: str|null }[] }`
|
||||
#### Create label template — `POST /labeltemplate`
|
||||
- Params: `body: { name: str, width: num, height: num, dpi: int, type: str, content: { objects: T22[] } }`
|
||||
- Returns: `201 { labeltemplate: T8 }`
|
||||
- Returns: `201 { labeltemplate: T9 }`
|
||||
#### Delete label template — `DELETE /labeltemplate/{id}`
|
||||
- Params: `id: int`
|
||||
- Returns: `204 void`
|
||||
#### Get label template by ID — `GET /labeltemplate/{id}`
|
||||
- Params: `id: int`
|
||||
- Returns: `200 { labeltemplate: T8 }`
|
||||
- Returns: `200 { labeltemplate: T9 }`
|
||||
#### Update label template (partial) — `PATCH /labeltemplate/{id}`
|
||||
- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: { objects: T22[] } }`
|
||||
- Returns: `200 { labeltemplate: T8 }`
|
||||
- Returns: `200 { labeltemplate: T9 }`
|
||||
#### Update label template — `PUT /labeltemplate/{id}`
|
||||
- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: { objects: T22[] } }`
|
||||
- Returns: `200 { labeltemplate: T8 }`
|
||||
- Returns: `200 { labeltemplate: T9 }`
|
||||
|
||||
### Section: /ledger
|
||||
|
||||
@@ -650,7 +650,7 @@ _none_
|
||||
- Returns: `201 { payment: T29 }`
|
||||
#### Generate and attach order PDF — `POST /order/{id}/pdf`
|
||||
- Params: `id: int`
|
||||
- Returns: `201 { attachment: T9, fileUrl: str }`
|
||||
- Returns: `201 { attachment: T10, fileUrl: str }`
|
||||
#### List shipments of an order — `GET /order/{id}/shipment`
|
||||
- Params: `id: int`
|
||||
- Returns: `200 { shipments: T2[] }`
|
||||
@@ -680,16 +680,16 @@ _none_
|
||||
|
||||
#### List POS terminals — `GET /posterminal`
|
||||
_none_
|
||||
- Returns: `200 { posterminals: T10[] }`
|
||||
- Returns: `200 { posterminals: T7[] }`
|
||||
#### Create POS terminal — `PUT /posterminal`
|
||||
- Params: `body: { name: str, rootCategoryId?: int|null, syncRootArticles?: bool }`
|
||||
- Returns: `200 { posterminal: T10 }`
|
||||
- Params: `body: { name: str, rootCategoryId?: int|null, syncRootArticles?: bool, sort?: int, enabled?: bool }`
|
||||
- Returns: `200 { posterminal: T7 }`
|
||||
#### Delete POS terminal — `DELETE /posterminal/{id}`
|
||||
- Params: `id: int`
|
||||
- Returns: `204 void`
|
||||
#### Update POS terminal — `PATCH /posterminal/{id}`
|
||||
- Params: `id: int, body: { name?: str, rootCategoryId?: int|null, syncRootArticles?: bool, pairingpin?: str|null }`
|
||||
- Returns: `200 { posterminal: T10 }`
|
||||
- Params: `id: int, body: { name?: str, rootCategoryId?: int|null, syncRootArticles?: bool, sort?: int, enabled?: bool, clientversion?: str|null, pairingpin?: str|null }`
|
||||
- Returns: `200 { posterminal: T7 }`
|
||||
#### Disable all categories on terminal — `DELETE /posterminal/{id}/all`
|
||||
- Params: `id: int`
|
||||
- Returns: `200 { id: int, categoryIds: int[] }`
|
||||
@@ -788,7 +788,7 @@ _none_
|
||||
- Returns: `201 { order: T1 }`
|
||||
#### Generate and attach quote PDF — `POST /quote/{id}/pdf`
|
||||
- Params: `id: int`
|
||||
- Returns: `201 { attachment: T9, fileUrl: str }`
|
||||
- Returns: `201 { attachment: T10, fileUrl: str }`
|
||||
|
||||
### Section: /retoure
|
||||
|
||||
@@ -875,7 +875,7 @@ _none_
|
||||
- Returns: `200 str`
|
||||
#### Generate and attach shipment PDF (delivery note) — `POST /shipment/{id}/pdf`
|
||||
- Params: `id: int`
|
||||
- Returns: `201 { attachment: T9, fileUrl: str }`
|
||||
- Returns: `201 { attachment: T10, fileUrl: str }`
|
||||
#### List packages of a shipment — `GET /shipment/{id}/shipping`
|
||||
- Params: `id: int`
|
||||
- Returns: `200 { shippings: T20[] }`
|
||||
@@ -982,7 +982,7 @@ _none_
|
||||
- Params: `query: path: str, size?: int, w?: int, h?: int, format?: str`
|
||||
- Returns: `200 jpeg`
|
||||
#### POS discovery and pairing — `GET /v1/client`
|
||||
- Params: `query: authCode: str, name?: str`
|
||||
- Params: `query: authCode: str, name?: str, clientversion?: str`
|
||||
- Returns: `200 { authCode: str|null, authToken: str|null, certificateFingerprint: str, certificateSerialNumber: str, mandantId: str, mandantName: str|null, mandantDatabase: str|null, serverFingerprint: str|null, name: str|null, serverTimestamp: str }`
|
||||
#### JTL-POS customer catalog synchronization — `GET /v1/customer`
|
||||
- Params: `query: lastChangedCustomer?: str, limit?: int`
|
||||
|
||||
@@ -15,6 +15,7 @@ Special API for POS bind and sync.
|
||||
|-------|----------|------|-------------|
|
||||
| `authCode` | yes | string | Discovery code (1-4 digits) or pairing PIN (6 digits) |
|
||||
| `name` | no | string | Client device name or serial identifier (default 'JTL-POS') |
|
||||
| `clientversion` | no | string | Optional client app version (for future POS clients during pairing) |
|
||||
|
||||
## Response `200`
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ Posterminal list
|
||||
"clientserial": "…",
|
||||
"rootCategoryId": 1,
|
||||
"syncRootArticles": true,
|
||||
"sort": 1,
|
||||
"enabled": true,
|
||||
"clientversion": "…",
|
||||
"lastseenAt": "…",
|
||||
"updatedAt": "…",
|
||||
"categoryIds": [
|
||||
|
||||
@@ -19,6 +19,9 @@ Updates a POS terminal configuration (name, root category, sync settings, or pai
|
||||
| `name` | no | string | |
|
||||
| `rootCategoryId` | no | integer | |
|
||||
| `syncRootArticles` | no | boolean | |
|
||||
| `sort` | no | integer | |
|
||||
| `enabled` | no | boolean | |
|
||||
| `clientversion` | no | string | |
|
||||
| `pairingpin` | no | string | |
|
||||
|
||||
```json
|
||||
@@ -44,6 +47,9 @@ Updated posterminal
|
||||
"clientserial": "…",
|
||||
"rootCategoryId": 1,
|
||||
"syncRootArticles": true,
|
||||
"sort": 1,
|
||||
"enabled": true,
|
||||
"clientversion": "…",
|
||||
"lastseenAt": "…",
|
||||
"updatedAt": "…",
|
||||
"categoryIds": [
|
||||
|
||||
@@ -15,6 +15,8 @@ The pairingpin is randomly generated server-side and returned in the response.
|
||||
| `name` | yes | string | |
|
||||
| `rootCategoryId` | no | integer | |
|
||||
| `syncRootArticles` | no | boolean | |
|
||||
| `sort` | no | integer | |
|
||||
| `enabled` | no | boolean | |
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -37,6 +39,9 @@ Created posterminal with generated pairingpin
|
||||
"clientserial": "…",
|
||||
"rootCategoryId": 1,
|
||||
"syncRootArticles": true,
|
||||
"sort": 1,
|
||||
"enabled": true,
|
||||
"clientversion": "…",
|
||||
"lastseenAt": "…",
|
||||
"updatedAt": "…",
|
||||
"categoryIds": [
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"dev": "cross-env DEV_BACKEND_MONITOR=true node --watch-path=./src --watch-path=./.env --watch-path=../dashdbschema/src --watch-path=../llmorbiter/src src/index.js",
|
||||
"manage-users": "node scripts/manageUsers.js",
|
||||
"wipe-company": "node scripts/wipeCompany.js",
|
||||
"downgrade": "node scripts/downgradeSchema.js",
|
||||
"docs:api": "node scripts/generateApiDocs.js",
|
||||
"api-graph": "node generate-api-graph.js",
|
||||
"review:functions": "node scripts/extractFunctions.js",
|
||||
|
||||
30
scripts/downgradeSchema.js
Normal file
30
scripts/downgradeSchema.js
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Downgrades the configured database (from `.env`) to a target schema version.
|
||||
*
|
||||
* npm run downgrade -- <targetVersion>
|
||||
*
|
||||
* Uses libdashstore's `src/providers/<provider>/downgrade/<from>_to_<to>.js`.
|
||||
* Stop the API first; starting it again upgrades back to the latest version.
|
||||
*/
|
||||
import { createProvider } from '../src/db/provider.js';
|
||||
import env from '../src/config/env.js';
|
||||
|
||||
const [rawTarget] = process.argv.slice(2);
|
||||
const target = Number(rawTarget);
|
||||
if (!rawTarget || !Number.isInteger(target) || target < 1) {
|
||||
console.error('Usage: npm run downgrade -- <targetVersion>');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const provider = createProvider();
|
||||
try {
|
||||
const before = await provider.getSchemaVersion();
|
||||
const steps = await provider.downgradeSchema(target);
|
||||
console.log(`Schema ${env.dbProvider}: ${before} -> ${target} (${steps} step(s))`);
|
||||
} catch (err) {
|
||||
console.error(`Downgrade failed: ${err.message}`);
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
await provider.close();
|
||||
}
|
||||
|
||||
@@ -1693,6 +1693,18 @@
|
||||
* nullable: true
|
||||
* syncRootArticles:
|
||||
* type: boolean
|
||||
* sort:
|
||||
* type: integer
|
||||
* description: Display order (ascending)
|
||||
* default: 0
|
||||
* enabled:
|
||||
* type: boolean
|
||||
* description: Whether terminal is active/allowed to sync
|
||||
* default: true
|
||||
* clientversion:
|
||||
* type: string
|
||||
* nullable: true
|
||||
* description: Client software version reported by POS terminal
|
||||
* lastseenAt:
|
||||
* type: string
|
||||
* nullable: true
|
||||
|
||||
@@ -132,6 +132,12 @@ export function register(app, basePath = '') {
|
||||
* syncRootArticles:
|
||||
* type: boolean
|
||||
* default: true
|
||||
* sort:
|
||||
* type: integer
|
||||
* default: 0
|
||||
* enabled:
|
||||
* type: boolean
|
||||
* default: true
|
||||
* example:
|
||||
* name: "Kassa 1"
|
||||
* responses:
|
||||
@@ -190,6 +196,11 @@ export function register(app, basePath = '') {
|
||||
const syncRootArticles = req.body.syncRootArticles !== undefined
|
||||
? req.body.syncRootArticles
|
||||
: req.body.sync_root_articles;
|
||||
const sort = req.body.sort !== undefined ? Number(req.body.sort) : undefined;
|
||||
if (sort !== undefined && !Number.isInteger(sort)) {
|
||||
return res.status(400).json({ error: 'sort must be an integer' });
|
||||
}
|
||||
const enabled = req.body.enabled !== undefined ? Boolean(req.body.enabled) : undefined;
|
||||
|
||||
try {
|
||||
const posterminal = await db.upsert(req.user.id, {
|
||||
@@ -197,6 +208,8 @@ export function register(app, basePath = '') {
|
||||
pairingpin,
|
||||
rootCategoryId,
|
||||
syncRootArticles,
|
||||
sort,
|
||||
enabled,
|
||||
});
|
||||
res.status(200).json({ posterminal });
|
||||
} catch (error) {
|
||||
@@ -239,6 +252,13 @@ export function register(app, basePath = '') {
|
||||
* nullable: true
|
||||
* syncRootArticles:
|
||||
* type: boolean
|
||||
* sort:
|
||||
* type: integer
|
||||
* enabled:
|
||||
* type: boolean
|
||||
* clientversion:
|
||||
* type: string
|
||||
* nullable: true
|
||||
* pairingpin:
|
||||
* type: string
|
||||
* nullable: true
|
||||
@@ -312,6 +332,22 @@ export function register(app, basePath = '') {
|
||||
payload.syncRootArticles = Boolean(rawSync);
|
||||
}
|
||||
|
||||
if (req.body.sort !== undefined) {
|
||||
const rawSort = Number(req.body.sort);
|
||||
if (!Number.isInteger(rawSort)) {
|
||||
return res.status(400).json({ error: 'sort must be an integer' });
|
||||
}
|
||||
payload.sort = rawSort;
|
||||
}
|
||||
|
||||
if (req.body.enabled !== undefined) {
|
||||
payload.enabled = Boolean(req.body.enabled);
|
||||
}
|
||||
|
||||
if (req.body.clientversion !== undefined) {
|
||||
payload.clientversion = req.body.clientversion ? String(req.body.clientversion).trim() : null;
|
||||
}
|
||||
|
||||
if (req.body.pairingpin !== undefined) {
|
||||
payload.pairingpin = req.body.pairingpin ? String(req.body.pairingpin).trim() : null;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,11 @@ export async function touchLastseen(id) {
|
||||
return store.posterminal.touchLastseen(id);
|
||||
}
|
||||
|
||||
export async function setClientversion(id, clientversion) {
|
||||
const store = await getStore();
|
||||
return store.posterminal.setClientversion(id, clientversion);
|
||||
}
|
||||
|
||||
export async function pair(id, data) {
|
||||
const store = await getStore();
|
||||
return store.posterminal.pair(id, data);
|
||||
|
||||
41
src/db/provider.js
Normal file
41
src/db/provider.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import { MssqlProvider } from 'libdashstore/providers/mssql';
|
||||
import { BetterSqlite3Provider } from 'libdashstore/providers/better-sqlite3';
|
||||
import env from '../config/env.js';
|
||||
|
||||
function getMssqlConfig() {
|
||||
const { server, database, user, password, port, encrypt, trustServerCertificate } = env.mssql;
|
||||
|
||||
if (!server || !database || !user || !password) {
|
||||
throw new Error('MSSQL is not configured (MSSQL_SERVER, MSSQL_DATABASE, MSSQL_USER, MSSQL_PASSWORD required)');
|
||||
}
|
||||
|
||||
return {
|
||||
server,
|
||||
database,
|
||||
user,
|
||||
password,
|
||||
port,
|
||||
storageDir: env.storageDir,
|
||||
pool: {
|
||||
max: 10,
|
||||
min: 0,
|
||||
idleTimeoutMillis: 30_000,
|
||||
},
|
||||
options: {
|
||||
encrypt,
|
||||
trustServerCertificate,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the configured (uninitialized) libdashstore provider from `.env`.
|
||||
* @returns {MssqlProvider | BetterSqlite3Provider}
|
||||
*/
|
||||
export function createProvider() {
|
||||
if (env.dbProvider === 'sqlite' || env.dbProvider === 'better-sqlite3') {
|
||||
return new BetterSqlite3Provider(env.sqlite);
|
||||
}
|
||||
return new MssqlProvider(getMssqlConfig(), { storageDir: env.storageDir });
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import LibDashStore from 'libdashstore';
|
||||
import { MssqlProvider } from 'libdashstore/providers/mssql';
|
||||
import { BetterSqlite3Provider } from 'libdashstore/providers/better-sqlite3';
|
||||
import env from '../config/env.js';
|
||||
import { createProvider } from './provider.js';
|
||||
import { createLogger, chalk } from '../util/logger.js';
|
||||
import { clearCachedCompanyUsers, clearCachedUser } from '../api/middleware/auth.js';
|
||||
import { clearDeletedCompany, setUserCompany } from '../api/sse.js';
|
||||
@@ -134,34 +132,10 @@ function wrapStoreWithTiming(rawStore) {
|
||||
return rawStore;
|
||||
}
|
||||
|
||||
function getMssqlConfig() {
|
||||
const { server, database, user, password, port, encrypt, trustServerCertificate } = env.mssql;
|
||||
|
||||
if (!server || !database || !user || !password) {
|
||||
throw new Error('MSSQL is not configured (MSSQL_SERVER, MSSQL_DATABASE, MSSQL_USER, MSSQL_PASSWORD required)');
|
||||
}
|
||||
|
||||
return {
|
||||
server,
|
||||
database,
|
||||
user,
|
||||
password,
|
||||
port,
|
||||
storageDir: env.storageDir,
|
||||
pool: {
|
||||
max: 10,
|
||||
min: 0,
|
||||
idleTimeoutMillis: 30_000,
|
||||
},
|
||||
options: {
|
||||
encrypt,
|
||||
trustServerCertificate,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the initialized LibDashStore singleton.
|
||||
* `provider.initialize()` applies pending schema upgrades (libdashstore
|
||||
* `src/providers/<provider>/upgrade/`) before the store is used.
|
||||
* @returns {Promise<InstanceType<typeof LibDashStore>>}
|
||||
*/
|
||||
export async function getStore() {
|
||||
@@ -171,11 +145,7 @@ export async function getStore() {
|
||||
|
||||
if (!connecting) {
|
||||
connecting = (async () => {
|
||||
if (env.dbProvider === 'sqlite' || env.dbProvider === 'better-sqlite3') {
|
||||
provider = new BetterSqlite3Provider(env.sqlite);
|
||||
} else {
|
||||
provider = new MssqlProvider(getMssqlConfig(), { storageDir: env.storageDir });
|
||||
}
|
||||
provider = createProvider();
|
||||
await provider.initialize();
|
||||
store = wrapStoreWithTiming(await new LibDashStore(provider));
|
||||
return store;
|
||||
|
||||
@@ -57,6 +57,11 @@ export async function posAuthMiddleware(req, res, next) {
|
||||
return res.status(401).json({ Message: 'Authorization has been denied for this request.' });
|
||||
}
|
||||
|
||||
if (terminal.enabled === 0 || terminal.enabled === false) {
|
||||
console.log(chalk.yellow(` [posAuth] Terminal "${terminal.id}" is disabled`));
|
||||
return res.status(403).json({ Message: 'Authorization has been denied for this request.' });
|
||||
}
|
||||
|
||||
req.posterminal = terminal;
|
||||
db.touchLastseen(terminal.id).catch((err) => {
|
||||
console.error('Failed to touch lastseen for posterminal:', err);
|
||||
|
||||
@@ -83,6 +83,13 @@ export function buildClientStep2(authCode, config) {
|
||||
* type: string
|
||||
* description: Client device name or serial identifier (default 'JTL-POS')
|
||||
* example: "Kasse 1"
|
||||
* - in: query
|
||||
* name: clientversion
|
||||
* required: false
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Optional client app version (for future POS clients during pairing)
|
||||
* example: "1.10.12.0"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Discovery or pairing successful
|
||||
@@ -160,11 +167,13 @@ export async function handleClient(req, res) {
|
||||
const token = terminal.token;
|
||||
const strToken = String(token || crypto.randomBytes(16).toString('hex')).trim();
|
||||
const pairedAt = serverTimestamp();
|
||||
const clientversion = req.query.clientversion || req.query.version || req.query.clientVersion || null;
|
||||
|
||||
await db.pair(terminal.id, {
|
||||
token: strToken,
|
||||
clientserial: name,
|
||||
pairedAt,
|
||||
clientversion: clientversion ? String(clientversion).trim() : null,
|
||||
});
|
||||
|
||||
if (terminal.companyId) {
|
||||
|
||||
Reference in New Issue
Block a user