Genesis
This commit is contained in:
141
lib/src/jtl-server.js
Normal file
141
lib/src/jtl-server.js
Normal file
@@ -0,0 +1,141 @@
|
||||
'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 };
|
||||
Reference in New Issue
Block a user