u
This commit is contained in:
55
README.md
55
README.md
@@ -12,6 +12,8 @@ const client = await new LibDashStore(pool); // runs initialize() (ensure schema
|
||||
|
||||
SQL lives in `src/sql/<table>.js`. Schema DDL: `src/schema.js` / `import 'libdashstore/schema'`.
|
||||
|
||||
ID fields in the JS API use camelCase `…Id` (DB columns stay snake_case).
|
||||
|
||||
---
|
||||
|
||||
## Schema
|
||||
@@ -27,11 +29,10 @@ SQL lives in `src/sql/<table>.js`. Schema DDL: `src/schema.js` / `import 'libdas
|
||||
|
||||
| Method | Signature | Returns |
|
||||
| --- | --- | --- |
|
||||
| `create` | `({ iso639, name, taxrate, reducedtaxrate })` | `{ id, root_category_id, language_id, taxrate_id, reducedtaxrate_id, taxfreerate_id }` |
|
||||
| `update` | `(id, { iso639, name, taxrate, reducedtaxrate })` | `{ id, root_category_id, language_id, taxrate_id, reducedtaxrate_id, taxfreerate_id }` |
|
||||
| `create` | `({ iso639, name, taxrate, reducedtaxrate })` | `{ id, rootCategoryId, languageId, taxrateId, reducedtaxrateId, taxfreerateId }` |
|
||||
| `update` | `(id, { iso639, name, taxrate, reducedtaxrate })` | `{ id, rootCategoryId, languageId, taxrateId, reducedtaxrateId, taxfreerateId }` |
|
||||
| `delete` | `(companyId)` | `void` |
|
||||
| `list` | `()` | `{ id, root_category_id, language_id, taxrate_id, reducedtaxrate_id, taxfreerate_id }[]` |
|
||||
|
||||
| `list` | `()` | `{ id, rootCategoryId, languageId, taxrateId, reducedtaxrateId, taxfreerateId }[]` |
|
||||
|
||||
### `create` (transaction)
|
||||
|
||||
@@ -39,8 +40,8 @@ For `POST /api/company`:
|
||||
|
||||
1. Fails if `name` is taken (`Company name not available`)
|
||||
2. Ensures language by `iso639` (get or create)
|
||||
3. Ensures tax rows for `taxrate`, `reducedtaxrate`, and `0` → `taxfreerate_id`
|
||||
4. Creates root category (`parent_id = null`)
|
||||
3. Ensures tax rows for `taxrate`, `reducedtaxrate`, and `0` → `taxfreerateId`
|
||||
4. Creates root category (`parentId = null`)
|
||||
5. Inserts company with all FKs set
|
||||
|
||||
### `update` (transaction)
|
||||
@@ -50,7 +51,7 @@ For `PATCH /api/company/:id`:
|
||||
1. Fails if company missing / name taken by another company
|
||||
2. Ensures lang + tax rates
|
||||
3. Updates `name`, `default_language_id`, `taxrate`, `reducedtaxrate`
|
||||
(`taxfreerate_id` / root category unchanged)
|
||||
(`taxfreerateId` / root category unchanged)
|
||||
|
||||
---
|
||||
|
||||
@@ -58,28 +59,16 @@ For `PATCH /api/company/:id`:
|
||||
|
||||
| Method | Signature | Returns |
|
||||
| --- | --- | --- |
|
||||
| `add` | `(email)` | `number` (auth id) |
|
||||
| `getById` | `(id)` | `row \| undefined` |
|
||||
| `getByEmail` | `(email)` | `row \| undefined` |
|
||||
| `add` | `(email)` | `id` |
|
||||
| `getByEmail` | `(email)` | `{ id, email, createdAt, selectedCompanyId } \| undefined` |
|
||||
| `listAllowedEmails` | `()` | `{ id, email, createdAt, selectedCompanyId }[]` |
|
||||
| `removeAllowedEmail` | `(email)` | `void` |
|
||||
| `setSelectedCompany` | `(authId, companyId)` | `void` |
|
||||
| `list` | `()` | `row[]` |
|
||||
| `del` | `(id)` | `void` |
|
||||
|
||||
### Row shape
|
||||
|
||||
```ts
|
||||
{
|
||||
id: number
|
||||
email: string
|
||||
created_at: Date
|
||||
selected_company: number | null
|
||||
}
|
||||
```
|
||||
|
||||
Backend mapping:
|
||||
|
||||
- Login: `getByEmail(email)` ← `getAllowedEmail`
|
||||
- Auth middleware: `getById(id)` ← `getAllowedEmailById`
|
||||
- Login: `getByEmail(email)`
|
||||
- CLI user manager: `listAllowedEmails()` / `removeAllowedEmail(email)` / `add(email)`
|
||||
- `PUT /api/auth/selected-company`: `setSelectedCompany(authId, companyId)`
|
||||
|
||||
---
|
||||
@@ -88,8 +77,8 @@ Backend mapping:
|
||||
|
||||
| Method | Signature | Returns |
|
||||
| --- | --- | --- |
|
||||
| `add` | `(companyId, authId)` | `number` (companyuser id) |
|
||||
| `getByEmail` | `(email)` | `number[]` (companyuser ids) |
|
||||
| `add` | `(companyId, authId)` | `id` |
|
||||
| `getByEmail` | `(email)` | `id[]` |
|
||||
| `delete` | `(id)` | `void` |
|
||||
|
||||
---
|
||||
@@ -98,9 +87,9 @@ Backend mapping:
|
||||
|
||||
| Method | Signature | Returns |
|
||||
| --- | --- | --- |
|
||||
| `create` | `(iso639)` | `number` |
|
||||
| `create` | `(iso639)` | `id` |
|
||||
| `getByIso639` | `(iso639)` | `{ id, iso639 } \| undefined` |
|
||||
| `ensure` | `(iso639, tx?)` | `number` (existing or new id) |
|
||||
| `ensure` | `(iso639, tx?)` | `id` |
|
||||
| `list` | `()` | `{ id, iso639 }[]` |
|
||||
| `delete` | `(id)` | `void` |
|
||||
|
||||
@@ -110,9 +99,9 @@ Backend mapping:
|
||||
|
||||
| Method | Signature | Returns |
|
||||
| --- | --- | --- |
|
||||
| `create` | `(rate)` | `number` |
|
||||
| `create` | `(rate)` | `id` |
|
||||
| `getByRate` | `(rate)` | `{ id, rate } \| undefined` |
|
||||
| `ensure` | `(rate, tx?)` | `number` (existing or new id) |
|
||||
| `ensure` | `(rate, tx?)` | `id` |
|
||||
| `list` | `()` | `{ id, rate }[]` |
|
||||
|
||||
---
|
||||
@@ -121,10 +110,10 @@ Backend mapping:
|
||||
|
||||
| Method | Signature | Returns |
|
||||
| --- | --- | --- |
|
||||
| `create` | `({ parent_id, sort })` | `number` |
|
||||
| `create` | `({ parentId, sort })` | `id` |
|
||||
| `updateParentId` | `(id, parentId)` | `void` |
|
||||
| `delete` | `(id)` | `void` |
|
||||
| `getTree` | `(rootId)` | `{ id, parent_id, sort }[]` (flat CTE result) |
|
||||
| `getTree` | `(rootId)` | `{ id, parentId, sort }[]` |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ const MENU = {
|
||||
category: {
|
||||
label: 'category',
|
||||
items: [
|
||||
{ value: 'category.create', label: 'create', hint: '{parent_id, sort} → id' },
|
||||
{ value: 'category.create', label: 'create', hint: '{parentId, sort} → id' },
|
||||
{ value: 'category.updateParentId', label: 'updateParentId', hint: 'id, parentId' },
|
||||
{ value: 'category.delete', label: 'delete', hint: 'id' },
|
||||
{ value: 'category.getTree', label: 'getTree', hint: 'rootId → rows' },
|
||||
@@ -112,12 +112,11 @@ const MENU = {
|
||||
google_auth: {
|
||||
label: 'google_auth',
|
||||
items: [
|
||||
{ value: 'google_auth.add', label: 'add', hint: 'email → authId' },
|
||||
{ value: 'google_auth.getById', label: 'getById', hint: 'id → row' },
|
||||
{ value: 'google_auth.add', label: 'add', hint: 'email → id' },
|
||||
{ value: 'google_auth.getByEmail', label: 'getByEmail', hint: 'email → row' },
|
||||
{ value: 'google_auth.listAllowedEmails', label: 'listAllowedEmails', hint: '→ rows' },
|
||||
{ value: 'google_auth.removeAllowedEmail', label: 'removeAllowedEmail', hint: 'email' },
|
||||
{ value: 'google_auth.setSelectedCompany', label: 'setSelectedCompany', hint: 'authId, companyId' },
|
||||
{ value: 'google_auth.list', label: 'list', hint: '→ rows' },
|
||||
{ value: 'google_auth.del', label: 'del', hint: 'id' },
|
||||
],
|
||||
},
|
||||
schema: {
|
||||
@@ -186,13 +185,10 @@ async function runMethod(client, action) {
|
||||
reducedtaxrate,
|
||||
});
|
||||
const companyId = created.id;
|
||||
|
||||
const auths = await client.google_auth.list();
|
||||
const existingAuth = auths.find((row) => row.email === email);
|
||||
const authId = existingAuth
|
||||
? existingAuth.id
|
||||
const existing = await client.google_auth.getByEmail(email);
|
||||
const authId = existing
|
||||
? existing.id
|
||||
: await client.google_auth.add(email);
|
||||
|
||||
const companyUserId = await client.companyuser.add(companyId, authId);
|
||||
await client.google_auth.setSelectedCompany(authId, companyId);
|
||||
|
||||
@@ -207,7 +203,6 @@ async function runMethod(client, action) {
|
||||
iso639,
|
||||
taxrate,
|
||||
reducedtaxrate,
|
||||
authCreated: !existingAuth,
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -298,7 +293,7 @@ async function runMethod(client, action) {
|
||||
return ['ok', true];
|
||||
}
|
||||
case 'category.create': {
|
||||
const parentRaw = await askText('parent_id (null as empty)', {
|
||||
const parentRaw = await askText('parentId (null as empty)', {
|
||||
placeholder: '',
|
||||
validate: (v) =>
|
||||
v?.trim() && !/^\d+$/.test(v.trim()) ? 'Integer or empty' : undefined,
|
||||
@@ -306,13 +301,13 @@ async function runMethod(client, action) {
|
||||
if (parentRaw == null) return;
|
||||
const sortRaw = await askInt('sort', '0');
|
||||
if (sortRaw == null) return;
|
||||
const parent_id = parentRaw === '' ? null : Number(parentRaw);
|
||||
return ['id', await client.category.create({ parent_id, sort: Number(sortRaw) })];
|
||||
const parentId = parentRaw === '' ? null : Number(parentRaw);
|
||||
return ['id', await client.category.create({ parentId, sort: Number(sortRaw) })];
|
||||
}
|
||||
case 'category.updateParentId': {
|
||||
const id = await askInt('Category id');
|
||||
if (id == null) return;
|
||||
const parentRaw = await askText('parent_id (null as empty)', {
|
||||
const parentRaw = await askText('parentId (null as empty)', {
|
||||
placeholder: '',
|
||||
validate: (v) =>
|
||||
v?.trim() && !/^\d+$/.test(v.trim()) ? 'Integer or empty' : undefined,
|
||||
@@ -360,12 +355,7 @@ async function runMethod(client, action) {
|
||||
validate: (v) => (!v?.trim() ? 'Required' : undefined),
|
||||
});
|
||||
if (email == null) return;
|
||||
return ['authId', await client.google_auth.add(email)];
|
||||
}
|
||||
case 'google_auth.getById': {
|
||||
const id = await askInt('Auth id');
|
||||
if (id == null) return;
|
||||
return ['row', await client.google_auth.getById(Number(id))];
|
||||
return ['id', await client.google_auth.add(email)];
|
||||
}
|
||||
case 'google_auth.getByEmail': {
|
||||
const email = await askText('Email', {
|
||||
@@ -375,6 +365,18 @@ async function runMethod(client, action) {
|
||||
if (email == null) return;
|
||||
return ['row', await client.google_auth.getByEmail(email)];
|
||||
}
|
||||
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;
|
||||
@@ -383,15 +385,6 @@ async function runMethod(client, action) {
|
||||
await client.google_auth.setSelectedCompany(Number(id), Number(companyId));
|
||||
return ['ok', true];
|
||||
}
|
||||
case 'google_auth.list': {
|
||||
return ['rows', await client.google_auth.list()];
|
||||
}
|
||||
case 'google_auth.del': {
|
||||
const id = await askInt('Auth id');
|
||||
if (id == null) return;
|
||||
await client.google_auth.del(Number(id));
|
||||
return ['ok', true];
|
||||
}
|
||||
case 'initialize': {
|
||||
await client.initialize();
|
||||
return ['ok', true];
|
||||
|
||||
10
src/index.js
10
src/index.js
@@ -19,11 +19,10 @@ import taxGetByRate from './store/tax/getByRate.js';
|
||||
import taxEnsure from './store/tax/ensure.js';
|
||||
import taxList from './store/tax/list.js';
|
||||
import googleAuthAdd from './store/google_auth/add.js';
|
||||
import googleAuthGetById from './store/google_auth/getById.js';
|
||||
import googleAuthGetByEmail from './store/google_auth/getByEmail.js';
|
||||
import googleAuthListAllowedEmails from './store/google_auth/listAllowedEmails.js';
|
||||
import googleAuthRemoveAllowedEmail from './store/google_auth/removeAllowedEmail.js';
|
||||
import googleAuthSetSelectedCompany from './store/google_auth/setSelectedCompany.js';
|
||||
import googleAuthList from './store/google_auth/list.js';
|
||||
import googleAuthDel from './store/google_auth/del.js';
|
||||
import storePurge from './store/purge.js';
|
||||
import storeInitialize from './store/initialize.js';
|
||||
|
||||
@@ -68,11 +67,10 @@ export default class LibDashStore {
|
||||
|
||||
this.google_auth = {
|
||||
add: googleAuthAdd(pool),
|
||||
getById: googleAuthGetById(pool),
|
||||
getByEmail: googleAuthGetByEmail(pool),
|
||||
listAllowedEmails: googleAuthListAllowedEmails(pool),
|
||||
removeAllowedEmail: googleAuthRemoveAllowedEmail(pool),
|
||||
setSelectedCompany: googleAuthSetSelectedCompany(pool),
|
||||
list: googleAuthList(pool),
|
||||
del: googleAuthDel(pool),
|
||||
};
|
||||
|
||||
this.purge = storePurge(pool);
|
||||
|
||||
@@ -10,7 +10,7 @@ WITH tree AS (
|
||||
FROM category c
|
||||
INNER JOIN tree t ON c.parent_id = t.id
|
||||
)
|
||||
SELECT id, parent_id, sort
|
||||
SELECT id, parent_id AS parentId, sort
|
||||
FROM tree
|
||||
ORDER BY sort, id
|
||||
`;
|
||||
|
||||
@@ -43,11 +43,11 @@ WHERE id = @id
|
||||
export const LIST = `
|
||||
SELECT
|
||||
id,
|
||||
root_category_id,
|
||||
default_language_id AS language_id,
|
||||
taxrate AS taxrate_id,
|
||||
reducedtaxrate AS reducedtaxrate_id,
|
||||
taxfreerate AS taxfreerate_id
|
||||
root_category_id AS rootCategoryId,
|
||||
default_language_id AS languageId,
|
||||
taxrate AS taxrateId,
|
||||
reducedtaxrate AS reducedtaxrateId,
|
||||
taxfreerate AS taxfreerateId
|
||||
FROM company
|
||||
`;
|
||||
|
||||
|
||||
@@ -4,27 +4,21 @@ OUTPUT INSERTED.id
|
||||
VALUES (@email)
|
||||
`;
|
||||
|
||||
export const GET_BY_ID = `
|
||||
SELECT id, email, created_at, selected_company
|
||||
FROM google_auth
|
||||
WHERE id = @id
|
||||
`;
|
||||
|
||||
export const GET_BY_EMAIL = `
|
||||
SELECT id, email, created_at, selected_company
|
||||
SELECT id, email, created_at AS createdAt, selected_company AS selectedCompanyId
|
||||
FROM google_auth
|
||||
WHERE email = @email
|
||||
`;
|
||||
|
||||
export const LIST = `
|
||||
SELECT id, email, created_at, selected_company
|
||||
export const LIST_ALLOWED_EMAILS = `
|
||||
SELECT id, email, created_at AS createdAt, selected_company AS selectedCompanyId
|
||||
FROM google_auth
|
||||
ORDER BY id
|
||||
ORDER BY email
|
||||
`;
|
||||
|
||||
export const DEL = `
|
||||
export const REMOVE_ALLOWED_EMAIL = `
|
||||
DELETE FROM google_auth
|
||||
WHERE id = @id
|
||||
WHERE email = @email
|
||||
`;
|
||||
|
||||
export const SET_SELECTED_COMPANY = `
|
||||
|
||||
@@ -2,10 +2,10 @@ import sql from 'mssql';
|
||||
import { CREATE } from '../../sql/category.js';
|
||||
|
||||
export default function create(pool) {
|
||||
return async ({ parent_id, sort }) => {
|
||||
return async ({ parentId, sort }) => {
|
||||
const result = await pool
|
||||
.request()
|
||||
.input('parentId', sql.Int, parent_id)
|
||||
.input('parentId', sql.Int, parentId)
|
||||
.input('sort', sql.Int, sort)
|
||||
.query(CREATE);
|
||||
|
||||
|
||||
@@ -46,11 +46,11 @@ export default function create(pool) {
|
||||
|
||||
return {
|
||||
id: result.recordset[0].id,
|
||||
root_category_id: rootCategoryId,
|
||||
language_id: languageId,
|
||||
taxrate_id: taxrateId,
|
||||
reducedtaxrate_id: reducedtaxrateId,
|
||||
taxfreerate_id: taxfreerateId,
|
||||
rootCategoryId,
|
||||
languageId,
|
||||
taxrateId,
|
||||
reducedtaxrateId,
|
||||
taxfreerateId,
|
||||
};
|
||||
} catch (err) {
|
||||
try {
|
||||
|
||||
@@ -45,11 +45,11 @@ export default function update(pool) {
|
||||
|
||||
return {
|
||||
id,
|
||||
root_category_id: company.root_category_id,
|
||||
language_id: languageId,
|
||||
taxrate_id: taxrateId,
|
||||
reducedtaxrate_id: reducedtaxrateId,
|
||||
taxfreerate_id: company.taxfreerate,
|
||||
rootCategoryId: company.root_category_id,
|
||||
languageId,
|
||||
taxrateId,
|
||||
reducedtaxrateId,
|
||||
taxfreerateId: company.taxfreerate,
|
||||
};
|
||||
} catch (err) {
|
||||
try {
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import sql from 'mssql';
|
||||
import { DEL } from '../../sql/google_auth.js';
|
||||
|
||||
export default function del(pool) {
|
||||
return async (id) => {
|
||||
await pool
|
||||
.request()
|
||||
.input('id', sql.Int, id)
|
||||
.query(DEL);
|
||||
};
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import sql from 'mssql';
|
||||
import { GET_BY_ID } from '../../sql/google_auth.js';
|
||||
|
||||
export default function getById(pool) {
|
||||
return async (id) => {
|
||||
const result = await pool
|
||||
.request()
|
||||
.input('id', sql.Int, id)
|
||||
.query(GET_BY_ID);
|
||||
|
||||
return result.recordset[0];
|
||||
};
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import { LIST } from '../../sql/google_auth.js';
|
||||
|
||||
export default function list(pool) {
|
||||
return async () => {
|
||||
const result = await pool.request().query(LIST);
|
||||
return result.recordset;
|
||||
};
|
||||
}
|
||||
8
src/store/google_auth/listAllowedEmails.js
Normal file
8
src/store/google_auth/listAllowedEmails.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import { LIST_ALLOWED_EMAILS } from '../../sql/google_auth.js';
|
||||
|
||||
export default function listAllowedEmails(pool) {
|
||||
return async () => {
|
||||
const result = await pool.request().query(LIST_ALLOWED_EMAILS);
|
||||
return result.recordset;
|
||||
};
|
||||
}
|
||||
11
src/store/google_auth/removeAllowedEmail.js
Normal file
11
src/store/google_auth/removeAllowedEmail.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import sql from 'mssql';
|
||||
import { REMOVE_ALLOWED_EMAIL } from '../../sql/google_auth.js';
|
||||
|
||||
export default function removeAllowedEmail(pool) {
|
||||
return async (email) => {
|
||||
await pool
|
||||
.request()
|
||||
.input('email', sql.NVarChar(320), email)
|
||||
.query(REMOVE_ALLOWED_EMAIL);
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user