import { readFile } from 'node:fs/promises'; import 'dotenv/config'; import sql from 'mssql'; import chalk from 'chalk'; import * as p from '@clack/prompts'; import LibDashStore from '../src/index.js'; const config = { server: process.env.MSSQL_SERVER, database: process.env.MSSQL_DATABASE, user: process.env.MSSQL_USER, password: process.env.MSSQL_PASSWORD, options: { encrypt: false, trustServerCertificate: true, }, }; function printResult(label, value) { p.log.step(chalk.cyan(label)); const text = value === undefined ? chalk.dim('undefined') : typeof value === 'string' ? value : JSON.stringify(value, null, 2); console.log(chalk.white(text)); console.log(); } async function askText(message, { placeholder, validate } = {}) { const value = await p.text({ message, placeholder, validate }); if (p.isCancel(value)) return null; return value.trim(); } async function askInt(message, placeholder = '1') { return askText(message, { placeholder, validate: (v) => (!/^\d+$/.test(v?.trim() ?? '') ? 'Integer required' : undefined), }); } async function askNumber(message, placeholder = '19') { const value = await askText(message, { placeholder, validate: (v) => v?.trim() === '' || Number.isNaN(Number(v)) ? 'Number required' : undefined, }); if (value == null) return null; return Number(value); } async function shutdown(pool, code = 0) { try { await pool.close(); } catch { // ignore } process.exit(code); } const MENU = { setup: { label: chalk.cyan('setup'), hint: 'guided flows', items: [ { value: 'setup.company', label: 'company', hint: 'email + name + lang' }, ], }, company: { label: 'company', items: [ { value: 'company.create', label: 'create', hint: 'googleAuthId, iso639, name, taxes → result' }, { value: 'company.update', label: 'update', hint: 'id + fields → result' }, { value: 'company.list', label: 'list', hint: 'googleAuthId → result[]' }, { value: 'company.delete', label: 'delete', hint: 'companyId' }, ], }, lang: { label: 'lang', items: [ { value: 'lang.create', label: 'create', hint: 'iso639 → id' }, { value: 'lang.list', label: 'list', hint: '→ rows' }, { value: 'lang.delete', label: 'delete', hint: 'id' }, ], }, tax: { label: 'tax', items: [ { value: 'tax.create', label: 'create', hint: 'rate → id' }, { value: 'tax.list', label: 'list', hint: '→ rows' }, { value: 'tax.ensure', label: 'ensure', hint: 'rate → id' }, ], }, unit: { label: 'unit', items: [ { value: 'unit.list', label: 'list', hint: 'googleAuthId, companyId → rows', }, { value: 'unit.upsert', label: 'upsert', hint: 'googleAuthId, companyId, JSON unit → row', }, { value: 'unit.delete', label: 'delete', hint: 'googleAuthId, companyId, si', }, ], }, companysetting: { label: 'companysetting', items: [ { value: 'companysetting.list', label: 'list', hint: 'googleAuthId, companyId, frontendOnly? → rows', }, { value: 'companysetting.get', label: 'get', hint: 'googleAuthId, companyId, key → row', }, { value: 'companysetting.set', label: 'set', hint: 'googleAuthId, companyId, JSON setting → row', }, { value: 'companysetting.delete', label: 'delete', hint: 'googleAuthId, companyId, key', }, ], }, brand: { label: 'brand', items: [ { value: 'brand.list', label: 'list', hint: 'googleAuthId, companyId → rows', }, { value: 'brand.upsert', label: 'upsert', hint: 'googleAuthId, companyId, JSON brand → row', }, { value: 'brand.delete', label: 'delete', hint: 'googleAuthId, companyId, id', }, ], }, category: { label: 'category', items: [ { value: 'category.createUnderParent', label: 'createUnderParent', hint: 'googleAuthId, parentId, name → row' }, { value: 'category.rename', label: 'rename', hint: 'googleAuthId, id, name → row' }, { value: 'category.move', label: 'move', hint: 'googleAuthId, id, parentId → row' }, { value: 'category.order', label: 'order', hint: 'googleAuthId, parentId, JSON categoryIds' }, { value: 'category.delete', label: 'delete', hint: 'googleAuthId, id' }, { value: 'category.getTreeForCompany', label: 'getTreeForCompany', hint: 'googleAuthId, companyId → rows' }, { value: 'category.getCategoryArticles', label: 'getCategoryArticles', hint: 'googleAuthId, categoryId → rows' }, ], }, article: { label: 'article', items: [ { value: 'article.createUnderParent', label: 'createUnderParent', hint: 'googleAuthId, parentId, name → row', }, { value: 'article.getBySku', label: 'getBySku', hint: 'googleAuthId, sku → row', }, { value: 'article.getHistoryBySku', label: 'getHistoryBySku', hint: 'googleAuthId, sku → history[]', }, { value: 'article.updateBySku', label: 'updateBySku', hint: 'googleAuthId, sku, name/unitamount/unitSi → row', }, { value: 'article.setPricesBySku', label: 'setPricesBySku', hint: 'googleAuthId, sku, JSON prices → row', }, { value: 'article.setEansBySku', label: 'setEansBySku', hint: 'googleAuthId, sku, JSON eans → row', }, { value: 'article.setBrandIdBySku', label: 'setBrandIdBySku', hint: 'googleAuthId, sku, brandId|null → row', }, { value: 'article.setWeightBySku', label: 'setWeightBySku', hint: 'googleAuthId, sku, weight → row', }, { value: 'article.setCategoryIdsBySku', label: 'setCategoryIdsBySku', hint: 'googleAuthId, sku, JSON categoryIds → row', }, { value: 'article.addImage', label: 'addImage', hint: 'googleAuthId, sku, avif file path → sizes', }, { value: 'article.deleteImage', label: 'deleteImage', hint: 'googleAuthId, sku, fullHash', }, { value: 'article.orderImages', label: 'orderImages', hint: 'googleAuthId, sku, comma fullHashes', }, { value: 'article.delete', label: 'delete', hint: 'googleAuthId, sku', }, ], }, pic: { label: 'pic', items: [ { value: 'pic.getImageByHash', label: 'getImageByHash', hint: 'googleAuthId, fullHash → meta', }, { value: 'pic.getThumbByHash', label: 'getThumbByHash', hint: 'googleAuthId, fullHash → meta', }, ], }, companyuser: { label: 'companyuser', items: [ { value: 'companyuser.add', label: 'add', hint: 'companyId, googleAuthId → id' }, { value: 'companyuser.delete', label: 'delete', hint: 'id' }, { value: 'companyuser.getByEmail', label: 'getByEmail', hint: 'email → ids' }, ], }, google_auth: { label: 'google_auth', items: [ { value: 'google_auth.add', label: 'add', hint: 'email → id' }, { value: 'google_auth.getByEmail', label: 'getByEmail', hint: 'email → row' }, { value: 'google_auth.getById', label: 'getById', hint: 'googleAuthId → row' }, { value: 'google_auth.listAllowedEmails', label: 'listAllowedEmails', hint: '→ rows' }, { value: 'google_auth.removeAllowedEmail', label: 'removeAllowedEmail', hint: 'email' }, { value: 'google_auth.setSelectedCompany', label: 'setSelectedCompany', hint: 'googleAuthId, companyId' }, { value: 'google_auth.setProfile', label: 'setProfile', hint: 'googleAuthId, picture, sub' }, ], }, schema: { label: 'schema', items: [ { value: 'initialize', label: 'initialize', hint: 'ensure schema (no purge)' }, { value: 'purge', label: chalk.red('purge'), hint: 'drop all · exit' }, ], }, }; async function pickAction() { const group = await p.select({ message: 'Group', options: [ ...Object.entries(MENU).map(([value, group]) => ({ value, label: group.label, hint: group.hint, })), { value: 'exit', label: 'exit', hint: 'quit without purge' }, ], }); if (p.isCancel(group) || group === 'exit') return 'exit'; const action = await p.select({ message: MENU[group].label, options: [ ...MENU[group].items, { value: 'back', label: chalk.dim('← back') }, ], }); if (p.isCancel(action) || action === 'back') return null; return action; } async function runMethod(client, action) { switch (action) { case 'setup.company': { const email = await askText('Email', { placeholder: 'user@example.com', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (email == null) return; const name = await askText('Company name', { placeholder: 'Acme', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (name == null) return; const iso639 = await askText('Default language (iso639)', { placeholder: 'de', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (iso639 == null) return; const taxrate = await askNumber('Tax rate', '19'); if (taxrate == null) return; const reducedtaxrate = await askNumber('Reduced tax rate', '7'); if (reducedtaxrate == null) return; const existing = await client.google_auth.getByEmail(email); const googleAuthId = existing ? existing.id : await client.google_auth.add(email); const created = await client.company.create({ iso639, name, taxrate, reducedtaxrate, googleAuthId, }); const companyId = created.id; await client.google_auth.setSelectedCompany(googleAuthId, companyId); return [ 'created', { ...created, googleAuthId, email, name, iso639, taxrate, reducedtaxrate, }, ]; } case 'company.create': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const name = await askText('Name', { placeholder: 'Acme', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (name == null) return; const iso639 = await askText('iso639', { placeholder: 'de', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (iso639 == null) return; const taxrate = await askNumber('taxrate', '19'); if (taxrate == null) return; const reducedtaxrate = await askNumber('reducedtaxrate', '7'); if (reducedtaxrate == null) return; return [ 'result', await client.company.create({ iso639, name, taxrate, reducedtaxrate, googleAuthId: Number(googleAuthId), }), ]; } case 'company.update': { const id = await askInt('Company id'); if (id == null) return; const name = await askText('Name', { placeholder: 'Acme', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (name == null) return; const iso639 = await askText('iso639', { placeholder: 'de', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (iso639 == null) return; const taxrate = await askNumber('taxrate', '19'); if (taxrate == null) return; const reducedtaxrate = await askNumber('reducedtaxrate', '7'); if (reducedtaxrate == null) return; return [ 'result', await client.company.update(Number(id), { iso639, name, taxrate, reducedtaxrate, }), ]; } case 'company.list': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; return ['result[]', await client.company.list(Number(googleAuthId))]; } case 'company.delete': { const companyId = await askInt('Company id'); if (companyId == null) return; await client.company.delete(Number(companyId)); return ['ok', true]; } case 'tax.create': { const rate = await askNumber('rate', '19'); if (rate == null) return; return ['id', await client.tax.create(rate)]; } case 'tax.list': { return ['rows', await client.tax.list()]; } case 'tax.ensure': { const rate = await askNumber('rate', '19'); if (rate == null) return; return ['id', await client.tax.ensure(rate)]; } case 'unit.list': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const companyId = await askInt('company id'); if (companyId == null) return; return [ 'result[]', await client.unit.list(Number(googleAuthId), Number(companyId)), ]; } case 'unit.upsert': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const companyId = await askInt('company id'); if (companyId == null) return; const unitJson = await askText('unit JSON', { placeholder: '{"si":"ml","basesi":"l","basesifactor":0.001,"symbol":"ml","name":"Milliliter"}', validate: (v) => { if (!v?.trim()) return 'Required'; try { const parsed = JSON.parse(v); if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { return 'Must be a JSON object'; } } catch { return 'Invalid JSON'; } }, }); if (unitJson == null) return; return [ 'result', await client.unit.upsert( Number(googleAuthId), Number(companyId), JSON.parse(unitJson), ), ]; } case 'unit.delete': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const companyId = await askInt('company id'); if (companyId == null) return; const si = await askText('si', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (si == null) return; await client.unit.delete(Number(googleAuthId), Number(companyId), si); return ['ok', true]; } case 'companysetting.list': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const companyId = await askInt('company id'); if (companyId == null) return; const frontendOnly = await askText('frontendOnly (true/false)', { placeholder: 'false', }); if (frontendOnly == null) return; return [ 'result[]', await client.companysetting.list(Number(googleAuthId), Number(companyId), { frontendOnly: frontendOnly.trim().toLowerCase() === 'true', }), ]; } case 'companysetting.get': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const companyId = await askInt('company id'); if (companyId == null) return; const key = await askText('key', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (key == null) return; const frontendOnly = await askText('frontendOnly (true/false)', { placeholder: 'false', }); if (frontendOnly == null) return; return [ 'result', await client.companysetting.get( Number(googleAuthId), Number(companyId), key, { frontendOnly: frontendOnly.trim().toLowerCase() === 'true' }, ), ]; } case 'companysetting.set': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const companyId = await askInt('company id'); if (companyId == null) return; const settingJson = await askText('setting JSON', { placeholder: '{"key":"theme","value":"dark","frontend":true}', validate: (v) => { if (!v?.trim()) return 'Required'; try { const parsed = JSON.parse(v); if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { return 'Must be a JSON object'; } } catch { return 'Invalid JSON'; } }, }); if (settingJson == null) return; return [ 'result', await client.companysetting.set( Number(googleAuthId), Number(companyId), JSON.parse(settingJson), ), ]; } case 'companysetting.delete': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const companyId = await askInt('company id'); if (companyId == null) return; const key = await askText('key', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (key == null) return; await client.companysetting.delete( Number(googleAuthId), Number(companyId), key, ); return ['ok', true]; } case 'brand.list': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const companyId = await askInt('company id'); if (companyId == null) return; return [ 'result[]', await client.brand.list(Number(googleAuthId), Number(companyId)), ]; } case 'brand.upsert': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const companyId = await askInt('company id'); if (companyId == null) return; const brandJson = await askText('brand JSON', { placeholder: '{"name":"Nike","gsprpinfo":"..."}', validate: (v) => { if (!v?.trim()) return 'Required'; try { const parsed = JSON.parse(v); if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { return 'Must be a JSON object'; } } catch { return 'Invalid JSON'; } }, }); if (brandJson == null) return; return [ 'result', await client.brand.upsert( Number(googleAuthId), Number(companyId), JSON.parse(brandJson), ), ]; } case 'brand.delete': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const companyId = await askInt('company id'); if (companyId == null) return; const id = await askInt('brand id'); if (id == null) return; await client.brand.delete(Number(googleAuthId), Number(companyId), id); return ['ok', true]; } case 'lang.create': { const iso639 = await askText('ISO 639', { placeholder: 'de', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (iso639 == null) return; return ['id', await client.lang.create(iso639)]; } case 'lang.list': { return ['rows', await client.lang.list()]; } case 'lang.delete': { const id = await askInt('Language id'); if (id == null) return; await client.lang.delete(Number(id)); return ['ok', true]; } case 'category.createUnderParent': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const parentId = await askInt('parent category id'); if (parentId == null) return; const name = await askText('name', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (name == null) return; return [ 'result', await client.category.createUnderParent({ googleAuthId: Number(googleAuthId), parentId: Number(parentId), name, }), ]; } case 'category.rename': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const id = await askInt('Category id'); if (id == null) return; const name = await askText('name', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (name == null) return; return [ 'row', await client.category.rename({ googleAuthId: Number(googleAuthId), id: Number(id), name, }), ]; } case 'category.move': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const id = await askInt('Category id'); if (id == null) return; const parentId = await askInt('new parent id'); if (parentId == null) return; return [ 'row', await client.category.move({ googleAuthId: Number(googleAuthId), id: Number(id), parentId: Number(parentId), }), ]; } case 'category.order': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const parentId = await askInt('parent id'); if (parentId == null) return; const categoryIdsJson = await askText('categoryIds JSON', { placeholder: '[2,4,3,5]', validate: (v) => { if (!v?.trim()) return 'Required'; try { const parsed = JSON.parse(v); if (!Array.isArray(parsed)) return 'Must be a JSON array'; } catch { return 'Invalid JSON'; } }, }); if (categoryIdsJson == null) return; return [ 'result', await client.category.order({ googleAuthId: Number(googleAuthId), parentId: Number(parentId), categoryIds: JSON.parse(categoryIdsJson), }), ]; } case 'category.delete': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const id = await askInt('Category id'); if (id == null) return; await client.category.delete({ googleAuthId: Number(googleAuthId), id: Number(id), }); return ['ok', true]; } case 'category.getTreeForCompany': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const companyId = await askInt('company id'); if (companyId == null) return; return [ 'rows', await client.category.getTreeForCompany(Number(googleAuthId), Number(companyId)), ]; } case 'category.getCategoryArticles': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const categoryId = await askInt('category id'); if (categoryId == null) return; return [ 'rows', await client.category.getCategoryArticles( Number(googleAuthId), Number(categoryId), ), ]; } case 'article.createUnderParent': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const parentId = await askInt('category id'); if (parentId == null) return; const name = await askText('name', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (name == null) return; return [ 'result', await client.article.createUnderParent({ googleAuthId: Number(googleAuthId), parentId: Number(parentId), name, }), ]; } case 'article.getBySku': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; return [ 'result', await client.article.getBySku(Number(googleAuthId), sku), ]; } case 'article.getHistoryBySku': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; return [ 'result', await client.article.getHistoryBySku(Number(googleAuthId), sku), ]; } case 'article.updateBySku': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; const patchJson = await askText('patch JSON', { placeholder: '{"name":"Tea","unitamount":0.24,"unitSi":"l"}', validate: (v) => { if (!v?.trim()) return 'Required'; try { const parsed = JSON.parse(v); if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { return 'Must be a JSON object'; } } catch { return 'Invalid JSON'; } }, }); if (patchJson == null) return; return [ 'result', await client.article.updateBySku( Number(googleAuthId), sku, JSON.parse(patchJson), ), ]; } case 'article.setPricesBySku': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; const pricesJson = await askText('prices JSON', { placeholder: '[{"price":9.99,"tax":19}]', validate: (v) => { if (!v?.trim()) return 'Required'; try { const parsed = JSON.parse(v); if (!Array.isArray(parsed)) return 'Must be a JSON array'; } catch { return 'Invalid JSON'; } }, }); if (pricesJson == null) return; return [ 'result', await client.article.setPricesBySku( Number(googleAuthId), sku, JSON.parse(pricesJson), ), ]; } case 'article.setEansBySku': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; const eansJson = await askText('eans JSON', { placeholder: '["4006381333931","4012345678901"]', validate: (v) => { if (!v?.trim()) return 'Required'; try { const parsed = JSON.parse(v); if (!Array.isArray(parsed)) return 'Must be a JSON array'; } catch { return 'Invalid JSON'; } }, }); if (eansJson == null) return; return [ 'result', await client.article.setEansBySku( Number(googleAuthId), sku, JSON.parse(eansJson), ), ]; } case 'article.setBrandIdBySku': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; const raw = await askText('brandId (or "null")', { validate: (v) => { if (v === null || v === undefined || v.trim() === '') return 'Required'; const t = v.trim(); if (t === 'null') return undefined; if (!Number.isFinite(Number(t))) return 'Must be a number or null'; }, }); if (raw == null) return; const brandId = raw.trim() === 'null' ? null : Number(raw.trim()); return [ 'result', await client.article.setBrandIdBySku( Number(googleAuthId), sku, brandId, ), ]; } case 'article.setWeightBySku': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; const weight = await askInt('weight'); if (weight == null) return; return [ 'result', await client.article.setWeightBySku( Number(googleAuthId), sku, weight, ), ]; } case 'article.setCategoryIdsBySku': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; const categoryIdsJson = await askText('categoryIds JSON', { placeholder: '[10,23,42]', validate: (v) => { if (!v?.trim()) return 'Required'; try { const parsed = JSON.parse(v); if (!Array.isArray(parsed)) return 'Must be a JSON array'; } catch { return 'Invalid JSON'; } }, }); if (categoryIdsJson == null) return; return [ 'result', await client.article.setCategoryIdsBySku( Number(googleAuthId), sku, JSON.parse(categoryIdsJson), ), ]; } case 'article.addImage': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; const filePath = await askText('AVIF file path', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (filePath == null) return; const image = await readFile(filePath.trim()); return [ 'result', await client.article.addImage(Number(googleAuthId), sku, image), ]; } case 'article.deleteImage': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; const fullHash = await askText('fullHash', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (fullHash == null) return; await client.article.deleteImage( Number(googleAuthId), sku, fullHash.trim(), ); return ['ok', true]; } case 'article.orderImages': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; const hashes = await askText('fullHashes (comma-separated)', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (hashes == null) return; const fullHashes = hashes .split(',') .map((h) => h.trim()) .filter(Boolean); return [ 'result', await client.article.orderImages(Number(googleAuthId), sku, { fullHashes, }), ]; } case 'article.delete': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; await client.article.delete(Number(googleAuthId), sku); return ['ok', true]; } case 'pic.getImageByHash': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const fullHash = await askText('fullHash', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (fullHash == null) return; const image = await client.pic.getImageByHash( Number(googleAuthId), fullHash.trim(), ); return [ 'result', { w: image.w, h: image.h, size: image.size, contentType: image.contentType, bufferLength: image.buffer.length, }, ]; } case 'pic.getThumbByHash': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const fullHash = await askText('fullHash', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (fullHash == null) return; const image = await client.pic.getThumbByHash( Number(googleAuthId), fullHash.trim(), ); return [ 'result', { w: image.w, h: image.h, contentType: image.contentType, bufferLength: image.buffer.length, }, ]; } case 'companyuser.add': { const companyId = await askInt('Company id'); if (companyId == null) return; const googleAuthId = await askInt('Auth id'); if (googleAuthId == null) return; return ['id', await client.companyuser.add(Number(companyId), Number(googleAuthId))]; } case 'companyuser.delete': { const id = await askInt('Company user id'); if (id == null) return; await client.companyuser.delete(Number(id)); return ['ok', true]; } case 'companyuser.getByEmail': { const email = await askText('Email', { placeholder: 'user@example.com', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (email == null) return; return ['ids', await client.companyuser.getByEmail(email)]; } case 'google_auth.add': { const email = await askText('Email', { placeholder: 'user@example.com', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (email == null) return; return ['id', await client.google_auth.add(email)]; } case 'google_auth.getByEmail': { const email = await askText('Email', { placeholder: 'user@example.com', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (email == null) return; return ['row', await client.google_auth.getByEmail(email)]; } case 'google_auth.getById': { const id = await askInt('Auth id'); if (id == null) return; return ['row', await client.google_auth.getById(Number(id))]; } case 'google_auth.listAllowedEmails': { return ['rows', await client.google_auth.listAllowedEmails()]; } case 'google_auth.removeAllowedEmail': { const email = await askText('Email', { placeholder: 'user@example.com', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (email == null) return; await client.google_auth.removeAllowedEmail(email); return ['ok', true]; } case 'google_auth.setSelectedCompany': { const id = await askInt('Auth id'); if (id == null) return; const companyId = await askInt('Company id'); if (companyId == null) return; await client.google_auth.setSelectedCompany(Number(id), Number(companyId)); return ['ok', true]; } case 'google_auth.setProfile': { const id = await askInt('Auth id'); if (id == null) return; const picture = await askText('picture URL', { placeholder: '(empty = null)' }); if (picture == null) return; const sub = await askText('Google sub', { placeholder: '(empty = null)' }); if (sub == null) return; await client.google_auth.setProfile(Number(id), { picture: picture.trim() || null, sub: sub.trim() || null, }); return ['ok', true]; } case 'initialize': { await client.initialize(); return ['ok', true]; } default: return null; } } async function main() { console.clear(); p.intro(chalk.bgMagenta.black(' libdashstore ') + chalk.dim(' api tester')); const spin = p.spinner(); spin.start('Connecting to MSSQL…'); let pool; try { pool = await sql.connect(config); } catch (err) { spin.stop(chalk.red('Connection failed')); p.log.error(err.message); p.outro(chalk.red('Aborted')); process.exit(1); } spin.message('Initializing schema…'); let client; try { client = await new LibDashStore(pool); spin.stop(chalk.green('Ready')); } catch (err) { spin.stop(chalk.red('Initialize failed')); p.log.error(err.message); await shutdown(pool, 1); } p.note( [ chalk.dim(`${config.server} / ${config.database}`), chalk.dim('initialize = ensure schema · purge ends session'), ].join('\n'), 'Connected', ); while (true) { const action = await pickAction(); if (action === 'exit') { p.outro(chalk.dim('Bye')); await shutdown(pool, 0); } if (action == null) continue; if (action === 'purge') { const confirm1 = await p.confirm({ message: chalk.red('Drop ALL tables?'), initialValue: false, }); if (p.isCancel(confirm1) || !confirm1) { p.log.warn('Purge cancelled'); continue; } const confirm2 = await p.confirm({ message: chalk.red('Really purge? This cannot be undone.'), initialValue: false, }); if (p.isCancel(confirm2) || !confirm2) { p.log.warn('Purge cancelled'); continue; } const run = p.spinner(); run.start('Purging…'); await client.purge(); run.stop(chalk.green('purge')); p.outro(chalk.red('Database purged — exiting')); await shutdown(pool, 0); } try { const result = await runMethod(client, action); if (result) { p.log.success(action); printResult(result[0], result[1]); } } catch (err) { p.log.error(err.message); console.log(chalk.dim(err.stack?.split('\n').slice(1, 4).join('\n') ?? '')); console.log(); } } } main().catch(async (err) => { console.error(chalk.red(err.stack ?? err.message)); process.exit(1); });