142 lines
5.2 KiB
JavaScript
142 lines
5.2 KiB
JavaScript
'use strict';
|
|
|
|
const { serverTimestamp } = require('./seed');
|
|
|
|
function readBody(req) {
|
|
return new Promise((resolve, reject) => {
|
|
const chunks = [];
|
|
req.on('data', (chunk) => chunks.push(chunk));
|
|
req.on('end', () => resolve(Buffer.concat(chunks)));
|
|
req.on('error', reject);
|
|
});
|
|
}
|
|
|
|
function sendJson(res, statusCode, body) {
|
|
const responseBody = JSON.stringify(body);
|
|
res.writeHead(statusCode, {
|
|
'Content-Type': 'application/json; charset=utf-8',
|
|
'Content-Length': Buffer.byteLength(responseBody),
|
|
});
|
|
res.end(responseBody);
|
|
}
|
|
|
|
function createJtlPosServer(store, config = {}) {
|
|
const { authToken = process.env.AUTH_TOKEN || '9a2e3036ed9c47e389741d9dbb7590e9' } = config;
|
|
const {
|
|
certificateFingerprint = 'BC2114CF407A42724BEEF417960F76DCBF9DE879',
|
|
certificateSerialNumber = '00BFC8BEACDB981B165210EF111CB9D3',
|
|
serverFingerprint = '39-6D-BD-DE-F3-5C-5A-EA-C2-19-CF-EB-A7-A9-58-2F-20-3F-20-F7-3D-E6-CA-8E-AE-FD-28-30-37-A6-45-AE',
|
|
mandantId = '1',
|
|
mandantName = 'eB-Standard',
|
|
mandantDatabase = 'eazybusiness',
|
|
} = config;
|
|
|
|
function buildClientStep1() {
|
|
return {
|
|
authCode: null,
|
|
authToken,
|
|
certificateFingerprint,
|
|
certificateSerialNumber,
|
|
mandantId,
|
|
mandantName: null,
|
|
mandantDatabase: null,
|
|
serverFingerprint,
|
|
name: null,
|
|
serverTimestamp: serverTimestamp(),
|
|
};
|
|
}
|
|
|
|
function buildClientStep2(authCode) {
|
|
return {
|
|
authCode,
|
|
authToken,
|
|
certificateFingerprint,
|
|
certificateSerialNumber,
|
|
mandantId,
|
|
mandantName,
|
|
mandantDatabase,
|
|
serverFingerprint: null,
|
|
name: null,
|
|
serverTimestamp: serverTimestamp(),
|
|
};
|
|
}
|
|
|
|
async function handle(req, res) {
|
|
const url = new URL(req.url, 'https://localhost');
|
|
|
|
const route = (method, pathname) => method === req.method && url.pathname === pathname;
|
|
|
|
if (route('GET', '/api/v1/client')) {
|
|
const authCode = url.searchParams.get('authCode') || '';
|
|
const name = url.searchParams.get('name') || 'JTL-POS';
|
|
const state = store.getState();
|
|
|
|
// Step 1 — the first 4 digits of a pre-registered 6-digit pairing code.
|
|
if (authCode.length <= 4 && authCode.length > 0) {
|
|
return sendJson(res, 200, buildClientStep1());
|
|
}
|
|
|
|
// Step 2 — full 6-digit code must exist in the pending authCodes map.
|
|
if (authCode.length === 6) {
|
|
if (state.authCodes[authCode]) {
|
|
store.dispatch({ type: 'REVOKE_PAIRING_CODE', payload: { code: authCode } });
|
|
store.dispatch({ type: 'REGISTER_DEVICE', payload: { token: authToken, name } });
|
|
return sendJson(res, 200, buildClientStep2(authCode));
|
|
}
|
|
return sendJson(res, 400, { Message: 'Der Authentifizierungscode ist falsch.' });
|
|
}
|
|
|
|
return sendJson(res, 400, { Message: 'Keinen passenden Authentifizierungscode gefunden.' });
|
|
}
|
|
|
|
if (route('GET', '/api/v1/init')) {
|
|
const params = {
|
|
lastChangedCategory: url.searchParams.get('lastChangedCategory') || '0',
|
|
lastChangedCustomer: url.searchParams.get('lastChangedCustomer') || '0',
|
|
lastChangedCustomerGroup: url.searchParams.get('lastChangedCustomerGroup') || '0',
|
|
lastChangedProduct: url.searchParams.get('lastChangedProduct') || '0',
|
|
lastChangedConfigurationGroup: url.searchParams.get('lastChangedConfigurationGroup') || '0',
|
|
lastChangedConfigurationItem: url.searchParams.get('lastChangedConfigurationItem') || '0',
|
|
lastChangedCompositeProduct: url.searchParams.get('lastChangedCompositeProduct') || '0',
|
|
lastChangedDeletedEntity: url.searchParams.get('lastChangedDeletedEntity') || '0',
|
|
};
|
|
return sendJson(res, 200, store.getInitCounts(params));
|
|
}
|
|
|
|
const entityEndpoints = {
|
|
'/api/v1/customergroup': { collection: 'customerGroups', param: 'lastChangedCustomerGroup', limit: '200' },
|
|
'/api/v1/deletedentity': { collection: 'deletedEntities', param: 'lastChangedDeletedEntity', limit: '200' },
|
|
'/api/v1/category': { collection: 'categories', param: 'lastChangedCategory', limit: '20' },
|
|
'/api/v1/customer': { collection: 'customers', param: 'lastChangedCustomer', limit: '20' },
|
|
'/api/v1/product': { collection: 'products', param: 'lastChangedProduct', limit: '20' },
|
|
'/api/v1/productcomposite': { collection: 'productComposites', param: 'lastChangedCompositeProduct', limit: '20' },
|
|
};
|
|
|
|
for (const [pathname, { collection, param, limit: defaultLimit }] of Object.entries(entityEndpoints)) {
|
|
if (route('GET', pathname)) {
|
|
const cursor = url.searchParams.get(param) || '0';
|
|
const limit = url.searchParams.get('limit') || defaultLimit;
|
|
const response = store.selectFilteredListFromCollection(collection, cursor, limit);
|
|
return sendJson(res, 200, response);
|
|
}
|
|
}
|
|
|
|
return sendJson(res, 404, {
|
|
Message: `No HTTP resource was found that matches the request URI '${url}'.`,
|
|
});
|
|
}
|
|
|
|
return async function requestListener(req, res) {
|
|
const body = await readBody(req);
|
|
// Attach raw body for optional logging by the wrapper
|
|
req.rawBody = body;
|
|
try {
|
|
await handle(req, res);
|
|
} catch (err) {
|
|
sendJson(res, 500, { Message: err.message });
|
|
}
|
|
};
|
|
}
|
|
|
|
module.exports = { createJtlPosServer };
|