diff --git a/README.md b/README.md deleted file mode 100644 index c1f2342..0000000 --- a/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# WorkshopTasker - -Allows for managing order tasks and inported products for a small work shop. - -*A college database project* - -# Running - -The project was tested with the nodejs version v18.16.0. It may not work with newer versions of nodejs. - -Dev dependencies are required. `npm install -D` - -Project uses Oracle MySQL as a database store. It uses environment variables for the connection settigns. All provided variables below are required. - -| Environment variable | Description | -|----------------------|-------------------| -| `DB_HOST` | Database host | -| `DB_PORT` | Database port | -| `DB_USER` | Database user | -| `DB_PASSWORD` | Database password | -| `DB_SCHEMA` | Database schema | - -After setting variables, you can run the project using `npx nuxi dev` or `npx nuxi preview` diff --git a/components/alerts.vue b/components/alerts.vue deleted file mode 100644 index 1301eab..0000000 --- a/components/alerts.vue +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - diff --git a/components/entryEditor.vue b/components/entryEditor.vue deleted file mode 100644 index 49a0651..0000000 --- a/components/entryEditor.vue +++ /dev/null @@ -1,51 +0,0 @@ - - - - updateModel(i.key, v)" - /> - diff --git a/components/navigation/navigation.vue b/components/navigation/navigation.vue index c72c67d..02ee1f2 100644 --- a/components/navigation/navigation.vue +++ b/components/navigation/navigation.vue @@ -13,20 +13,18 @@ const navOpen = ref(!mobile.value); Database Project - + - - - + diff --git a/components/orderView.vue b/components/orderView.vue deleted file mode 100644 index aa540b7..0000000 --- a/components/orderView.vue +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - Works - - - - fulfilled - offer - price - - - - - {{ i.is_fulfilled }} - - {{ i.offer.name }} - - {{ i.price }} PLN - - - - - - Imported products - - - - Name - link - price - - - - - {{ i.name }} - {{ i.link }} - {{ i.price }} PLN - - - - - - diff --git a/components/pagedList.vue b/components/pagedList.vue index 24dc5ed..952a2c7 100644 --- a/components/pagedList.vue +++ b/components/pagedList.vue @@ -2,7 +2,6 @@ const props = defineProps<{ records: Array, recordKey: string, - recordValue?: string, variant?: "default" | "inset" | "accordion" | "popout", modelValue?: any, }>(); @@ -23,7 +22,6 @@ defineEmits<{ v-for="record in records" :key="record[recordKey]" :variant="props.variant ?? 'default'" - :value="recordValue !== undefined ? record[recordValue] : undefined" > diff --git a/components/pagedTable.vue b/components/pagedTable.vue index 5c9df94..b0045f1 100644 --- a/components/pagedTable.vue +++ b/components/pagedTable.vue @@ -53,8 +53,4 @@ defineEmits<{ flex-direction: row; flex-wrap: nowrap; } - - tr:hover { - background-color: rgba(var(--v-theme-on-background), calc(var(--v-hover-opacity) * var(--v-theme-overlay-multiplier))); - } diff --git a/pages/client/[id].vue b/pages/client/[id].vue index 13f2504..380683a 100644 --- a/pages/client/[id].vue +++ b/pages/client/[id].vue @@ -1,14 +1,9 @@ - - - - Edit client - - - { formData[k] = v; }" - /> - - - - Submit - - - - - + - - - {{ client.phone }} - - - - - - edit - - + + {{ client.phone }} + + + - Created {{ getCreationDate() }} - - - {{ new Date(Number(new Snowflake(BigInt(((i.record) as orderSummary).id)).timestamp)).toLocaleDateString() }} - {{ ((i.record) as orderSummary).value }} PLN - - {{ ((i.record) as orderSummary).imported_products_count }} - products, - {{ ((i.record) as orderSummary).work_count }} - works - - - - - + {{ i }} + + + {{ i }} diff --git a/pages/clients.vue b/pages/clients.vue index 365602e..adf945c 100644 --- a/pages/clients.vue +++ b/pages/clients.vue @@ -1,19 +1,12 @@ - - - - - Create client - - - { formData[k] = v; }" - /> - - - - Submit - - - - + + owowowowowowowowo + + @@ -146,11 +66,6 @@ async function handleSubmit() { There are {{ count?.count }} clients in the database. - - Create - diff --git a/pages/index.vue b/pages/index.vue index 4fba044..3939812 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -3,8 +3,7 @@ import Test from '~/components/test.vue'; - Hi mom! - + Hi mom! diff --git a/pages/login.vue b/pages/login.vue index 5761adf..df89aba 100644 --- a/pages/login.vue +++ b/pages/login.vue @@ -124,13 +124,6 @@ updateUserInfo(); > Logout - - go to clients - diff --git a/schemaModel.mysqlWorkbench.mwb b/schemaModel.mysqlWorkbench.mwb deleted file mode 100644 index bd78215..0000000 Binary files a/schemaModel.mysqlWorkbench.mwb and /dev/null differ diff --git a/server/api/clients/[id]/orders.get.ts b/server/api/clients/[id]/orders.get.ts deleted file mode 100644 index bd9903b..0000000 --- a/server/api/clients/[id]/orders.get.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* global defineEventHandler */ - -import { baaWrapper } from "~/server/api/orders.get"; - -export default defineEventHandler(async (e) => { - const baa = await baaWrapper.RESTget(e, 50, 200, "`client` = ?", [e.context.params?.id]); - console.log(baa); - return baa; -}); diff --git a/server/api/login.post.ts b/server/api/login.post.ts index b011aaf..e993cab 100644 --- a/server/api/login.post.ts +++ b/server/api/login.post.ts @@ -3,8 +3,8 @@ import crypto from "crypto"; import { database, data } from "../utils/database"; import { isString } from "../utils/isString"; +import Snowflake from "../utils/snowflake"; import { cookieSettings } from "../utils/rootUtils"; -import Snowflake from "~/utils/snowflake"; export default defineEventHandler(async (e) => { if (getCookie(e, "token")) diff --git a/server/api/orders.get.ts b/server/api/orders.get.ts deleted file mode 100644 index 34f7816..0000000 --- a/server/api/orders.get.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* global defineEventHandler */ - -import BaaPagination from "../utils/baaPagination"; -import { data, database } from "../utils/database"; -import { client, orderSummary } from "~/utils/types/database"; - -export const baaWrapper = new BaaPagination( - "orderSummaries", - "id", - "*, CONVERT(`client`, CHAR) AS `client`, CONVERT(`user`, CHAR) as `user`", -); - -export default defineEventHandler(async (e) => { - const orders = await baaWrapper.RESTget(e, 50, 200); - - const uniqueClients: Array = []; - for (const i of orders) { - if (!uniqueClients.includes(i.client)) - uniqueClients.push(database.escape(i.client)); - } - - const [clients] = await database.query( - ["SELECT", - "*,", - "CONVERT(`id`, CHAR) AS `id`", - "FROM `clients`", - "WHERE `id` IN", - `(${uniqueClients.join(', ')})`, - ].join(" "), - ) as data; - - const rvalue: Array | { client?: client }> = []; - - for (const i of orders) - rvalue.push({ ...i, client: clients.find(e => i.client === e.id) }); - return rvalue; -}); diff --git a/server/api/orders.post.ts b/server/api/orders.post.ts deleted file mode 100644 index ed31ada..0000000 --- a/server/api/orders.post.ts +++ /dev/null @@ -1,172 +0,0 @@ -/* global defineEventHandler, createError, readBody, setResponseStatus */ - -import { createValidationError, handleRecursedValidationError } from "../utils/validation"; -import { database as db } from "../utils/database"; -import getRequestingUser from "../utils/getRequestingUser"; -import { getOrder } from "./orders/[id].get"; -import Snowflake from "~/utils/snowflake"; - -type importedProduct = { - name: string | null, - link: string, - price_imported: number, - price: number, -} - -type work = { - offer: string, - price: number, - notes: string | null, - is_fulfilled: boolean | 0 | 1, -} - -type order = { - client: string, - // user: string, - is_draft: boolean | 0 | 1, - imported_products: Array, - work: Array, -}; - -export function checkIsWork( - value: any, - patch: Patch, -): value is Patch extends true ? Partial : work { - const errors = new Map(); - - if (typeof value !== "object") { - throw createError({ - message: "Invalid body", - statusCode: 400, - }); - } - - if (!(typeof value.offer === "string" || (patch && value.offer === undefined))) errors.set("offer", "is not string"); - if (!(typeof value.price === "number" || (patch && value.price === undefined))) errors.set("price", "is not price"); - if (!(typeof value.notes === "string" || value.notes === null || (patch && value.notes === undefined))) errors.set("notes", "is not string or null"); - if (!(typeof value.is_fulfilled === "boolean" || value.is_fulfilled === 0 || value.is_fulfilled === 1 || (patch && value.is_fulfilled === undefined))) errors.set("is_fulfilled", "is not boolean"); - - if (errors.size !== 0) throw createValidationError(errors); - return true; -} - -export function checkIsImportedProduct( - value: any, - patch: Patch, -): value is Patch extends true ? Partial : importedProduct { - const errors = new Map(); - - if (typeof value !== "object") { - throw createError({ - message: "Invalid body", - statusCode: 400, - }); - } - - if (!(typeof value.name === "string" || value.name === null || (patch && value.name === undefined))) errors.set("name", "is not string or null"); - if (!(typeof value.link === "string" || (patch && value.name === undefined))) errors.set("link", "is not string"); - if (!(typeof value.price_imported === "number" || (patch && value.name === undefined))) errors.set("price_imported", "is not number"); - if (!(typeof value.price || (patch && value.price === undefined))) errors.set("price", "is not number"); - - if (errors.size !== 0) throw createValidationError(errors); - - return true; -} - -export function checkIsOrder( - value: any, - patch: Patch, -): value is Patch extends true ? Partial> : order { - const errors = new Map(); - - if (typeof value !== "object") { - throw createError({ - message: "Invalid body", - statusCode: 400, - }); - } - - if (!(typeof value.client === "string" || (patch && value.client === undefined))) errors.set("client", "is not string"); - if (!(typeof value.is_draft === "boolean" || value.is_draft === 0 || value.is_draft === 1 || (patch && value.is_draft === undefined))) errors.set("is_draft", "is not boolean"); - if (!(value.imported_products instanceof Array)) errors.set("imported_products", "is not array"); - else if (patch && value.imported_products !== undefined) errors.set("imported_products", "cannot patch from order"); - if (!(value.work instanceof Array)) errors.set("work", "is not array"); - else if (patch && value.work !== undefined) errors.set("work", "cannot patch from order"); - - if (!patch) { - const importedProducts = value.imported_products; - if (importedProducts instanceof Array) { - for (const i in importedProducts) { - try { - checkIsImportedProduct(importedProducts[i], patch); - } catch (e) { - handleRecursedValidationError(e, errors, `imported_products[${i}]`); - } - } - } - - const work = value.work; - if (work instanceof Array) { - for (const i in work) { - try { - checkIsWork(work[i], patch); - } catch (e) { - handleRecursedValidationError(e, errors, `work[${i}]`); - } - } - } - } - - if (errors.size !== 0) throw createValidationError(errors); - - return true; -} - -export default defineEventHandler(async (e) => { - const body = await readBody(e); - const id = new Snowflake().toString(); - const user = await getRequestingUser(e); - - if (!checkIsOrder(body, false)) throw createError({ message: "Invalid body", statusCode: 400 }); - - const database = await db.new(); - await database.beginTransaction(); - - await database.query( - ["INSERT INTO", - "`orders`", - "VALUES", - "(?, ?, ?, ?)", - ].join(" "), - [id, body.client, user.id, body.is_draft], - ); - - const promises: Array> = []; - for (const i of body.imported_products) { - promises.push(database.query( - ["INSERT INTO", - "`imported_products`", - "VALUES", - "(?, ?, ?, ?, ?, ?)", - ].join(" "), - [new Snowflake().toString(), id, i.name, i.link, i.price_imported, i.price], - )); - } - - for (const i of body.work) { - promises.push(database.query( - ["INSERT INTO", - "`work`", - "VALUES", - "(?, ?, ?, ?, ?, ?)", - ].join(" "), - [new Snowflake().toString(), id, i.offer, i.price, i.notes, i.is_fulfilled], - )); - } - - await Promise.all(promises); - await database.commit(); - - setResponseStatus(e, 201); - return getOrder(id); -}); diff --git a/server/api/orders/[id].delete.ts b/server/api/orders/[id].delete.ts deleted file mode 100644 index 0f660d0..0000000 --- a/server/api/orders/[id].delete.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* global defineEventHandler, createError */ -import { ResultSetHeader } from "mysql2"; - -import { database } from "~/server/utils/database"; - -export default defineEventHandler(async (e) => { - const id = e.context.params?.id; - - const [result] = await database.query( - "DELETE FROM `orders` WHERE `id` = ?", - [id], - ) as unknown as [ResultSetHeader]; - - if (result.affectedRows === 0) throw createError({ statusCode: 404 }); - - return null; -}); diff --git a/server/api/orders/[id].get.ts b/server/api/orders/[id].get.ts deleted file mode 100644 index 9832f2f..0000000 --- a/server/api/orders/[id].get.ts +++ /dev/null @@ -1,108 +0,0 @@ -/* global defineEventHandler, createError */ - -import { offer as offerType, order } from "~/utils/types/database"; -import { database, data } from "~/server/utils/database"; - -export async function orderExists(id: string) { - const [[exists]] = await database.query( - "SELECT EXISTS(*) AS `exists` FROM `orders` WHERE `id` = ?", - [id], - ) as data<{exists: 0 | 1}>; - - return exists.exists === 1; -} - -export async function getImportedProducts(id: string) { - const [importedProducts] = await database.query( - ["SELECT", - "CONVERT(`id`, CHAR) AS `id`,", - "`name`,", - "`link`,", - "`price`,", - "`price_imported`", - "FROM `imported_products`", - "WHERE `order` = ?", - ].join(" "), - [id], - ) as data<{ - id: string, - name: string | null, - link: string, - price: string, - price_imported: string - }>; - - return importedProducts; -} - -export async function getWork(id: string) { - const [work] = await database.query( - ["SELECT", - "CONVERT(`id`, CHAR) AS `id`,", - "CONVERT(`offer`, CHAR) AS `offer`,", - "`price`,", - "`notes`,", - "`is_fulfilled`", - "FROM `work`", - "WHERE `order` = ?", - ].join(" "), - [id], - ) as data<{ - id: string, - offer: offerType, - price: number, - notes: string | null, - is_fulfilled: 0 | 1, - }>; - - const [offer] = await database.query( - ["SELECT", - "CONVERT(`offer`.`id`, CHAR) AS `id`,", - "`offer`.`name`,", - "`offer`.`description`,", - "`offer`.`recommended_price`", - "FROM", - "`work`", - "LEFT JOIN `offer` ON `work`.`offer` = `offer`.`id`", - "WHERE `work`.`order` = ?", - ].join(" "), - [id], - ) as data; - - // @ts-ignore i.offer is string, but it needs to be an offer object - for (const i of work) i.offer = offer.find(e => e.id === i.offer) as offerType; - return work; -} - -export async function getOrder(id: string): Promise { - const [[order]] = await database.query( - ["SELECT", - "CONVERT(`id`, CHAR) AS `id`,", - "CONVERT(`client`, CHAR) AS `client`,", - "CONVERT(`user`, CHAR) AS `user`, ", - "`is_draft`,", - "`value`", - "FROM `orderSummaries`", - "WHERE `id` = ?", - ].join(" "), - [id], - ) as data<{ - id: string, - client: string, - user: string, - is_draft: 0 | 1, - value: number, - }>; - - if (!order) throw createError({ statusCode: 404 }); - - const importedProducts = await getImportedProducts(id); - const work = await getWork(id); - - return { ...order, imported_products: importedProducts, work }; -} - -export default defineEventHandler((e) => { - const key = e.context.params?.id; - return getOrder(key as string); -}); diff --git a/server/api/orders/[id].patch.ts b/server/api/orders/[id].patch.ts deleted file mode 100644 index 87aa718..0000000 --- a/server/api/orders/[id].patch.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* global defineEventHandler, readBody, createError */ - -import { checkIsOrder } from "../orders.post"; -import { database as db } from "~/server/utils/database"; - -export default defineEventHandler(async (e) => { - const body = await readBody(e); - const id = e.context.params?.id; - - if (!checkIsOrder(e, true)) throw createError({ message: "Invalid body", statusCode: 400 }); - - const database = await db.new(); - await database.beginTransaction(); - - for (const [k, v] of Object.entries(body)) - database.query(`UPDATE TABLE \`orders\` SET \`${k}\` = ? WHERE \`id\` = ?`, [v, id]); -}); diff --git a/server/api/orders/[id]/imported_products.get.ts b/server/api/orders/[id]/imported_products.get.ts deleted file mode 100644 index ad7d9e0..0000000 --- a/server/api/orders/[id]/imported_products.get.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* global defineEventHandler, createError */ - -import { orderExists, getImportedProducts } from "../[id].get"; - -export default defineEventHandler(async (e) => { - const id = e.context.params?.id as string; - - if (!orderExists(id)) throw createError({ statusCode: 404 }); - - const importedProducts = await getImportedProducts(id); - return importedProducts; -}); diff --git a/server/api/orders/[id]/imported_products.post.ts b/server/api/orders/[id]/imported_products.post.ts deleted file mode 100644 index f441432..0000000 --- a/server/api/orders/[id]/imported_products.post.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* global defineEventHandler, readBody, createError, setResponseStatus */ - -import { checkIsImportedProduct } from "../../orders.post"; -import { getImportedProducts, orderExists } from "../[id].get"; -import Snowflake from "~/utils/snowflake"; -import { database } from "~/server/utils/database"; - -export default defineEventHandler(async (e) => { - const body = await readBody(e); - const idOrder = e.context.params?.id as string; - const idImportedProducts = new Snowflake().toString(); - - if (!orderExists(idOrder)) throw createError({ statusCode: 404 }); - if (!checkIsImportedProduct(body, false)) throw createError({ message: "Invalid body", statusCode: 400 }); - - await database.query( - ["INSERT INTO", - "`imported_products`", - "VALUES", - "(?, ?, ?, ?, ?, ?)", - ].join(" "), - [idImportedProducts, idOrder, body.name, body.link, body.price_imported, body.price], - ); - - setResponseStatus(e, 201); - return getImportedProducts(idOrder); -}); diff --git a/server/api/orders/[id]/work.get.ts b/server/api/orders/[id]/work.get.ts deleted file mode 100644 index 5399cb8..0000000 --- a/server/api/orders/[id]/work.get.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* global defineEventHandler, createError */ - -import { orderExists, getWork } from "../[id].get"; - -export default defineEventHandler(async (e) => { - const id = e.context.params?.id as string; - - if (!orderExists(id)) throw createError({ statusCode: 404 }); - - const work = await getWork(id); - return work; -}); diff --git a/server/api/orders/[id]/work.post.ts b/server/api/orders/[id]/work.post.ts deleted file mode 100644 index 98e04bd..0000000 --- a/server/api/orders/[id]/work.post.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* global defineEventHandler, readBody, createError, setResponseStatus */ - -import { checkIsWork } from "../../orders.post"; -import { getWork, orderExists } from "../[id].get"; -import Snowflake from "~/utils/snowflake"; -import { database } from "~/server/utils/database"; - -export default defineEventHandler(async (e) => { - const body = await readBody(e); - const idOrder = e.context.params?.id as string; - const idWork = new Snowflake().toString(); - - if (!orderExists(idOrder)) throw createError({ statusCode: 404 }); - if (!checkIsWork(body, false)) throw createError({ message: "Invalid body", statusCode: 400 }); - - await database.query( - ["INSERT INTO", - "`work`", - "VALUES", - "(?, ?, ?, ?, ?, ?)", - ].join(" "), - [idWork, idOrder, body.offer, body.price, body.notes, body.is_fulfilled], - ); - - setResponseStatus(e, 201); - - return getWork(idWork); -}); diff --git a/server/api/orders/[id]/work/[idWork].delete.ts b/server/api/orders/[id]/work/[idWork].delete.ts deleted file mode 100644 index d95f1c3..0000000 --- a/server/api/orders/[id]/work/[idWork].delete.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* global defineEventHandler, createError */ - -import { ResultSetHeader } from "mysql2"; -import { orderExists } from "../../[id].get"; -import { database } from "~/server/utils/database"; - -export default defineEventHandler(async (e) => { - const idOrder = e.context.params?.id as string; - const idWork = e.context.params?.idWork as string; - - if (!orderExists(idOrder)) throw createError({ statusCode: 404 }); - - const [response] = await database.query( - "DELETE FROM `work` WHERE `id` = ?", - [idWork], - ) as unknown as [ResultSetHeader]; - - if (response.affectedRows === 0) throw createError({ statusCode: 404 }); - return null; -}); diff --git a/server/api/orders/[id]/work/[idWork].get.ts b/server/api/orders/[id]/work/[idWork].get.ts deleted file mode 100644 index 5a8c06c..0000000 --- a/server/api/orders/[id]/work/[idWork].get.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* global defineEventHandler, createError */ - -import { orderExists, getWork } from "../../[id].get"; - -export default defineEventHandler((e) => { - const idOrder = e.context.params?.id as string; - const idWork = e.context.params?.idWork as string; - - if (!orderExists(idOrder)) throw createError({ statusCode: 404 }); - return getWork(idWork); -}); diff --git a/server/utils/baaPagination.ts b/server/utils/baaPagination.ts index 96e4853..6920cc4 100644 --- a/server/utils/baaPagination.ts +++ b/server/utils/baaPagination.ts @@ -1,11 +1,11 @@ -/* global defineEventHandler, getQuery, createError, readBody, setResponseStatus */ +/* global defineEventHandler, getQuery, createError, readBody */ import { QueryObject } from "ufo"; import { H3Event } from "h3"; import { ResultSetHeader } from "mysql2/promise"; import { data, database } from "./database"; import { isString } from "./isString"; -import Snowflake from "~/utils/snowflake"; +import Snowflake from "./snowflake"; import { client } from "~/utils/types/database"; type queryType = { @@ -21,12 +21,6 @@ type queryType = { export default class BaaPagination { readonly table: string; readonly key: keyType; - readonly select: string; - readonly groupBy: string; - - private get sqlGroupBy() { - return this.groupBy !== "" ? `GROUP BY ${this.groupBy}` : ""; - } /** * Gets queryType for a given query with a value @@ -69,40 +63,36 @@ export default class BaaPagination = [], ) { - const sqlwhere = where !== "" ? `AND (${where})` : ""; switch (queryType.type) { case "before": { const [data] = await database.query( - `SELECT ${this.select}, CONVERT(\`${this.key}\`, CHAR) AS \`${this.key}\` FROM \`${this.table}\` WHERE \`${this.key}\` < ? ${sqlwhere} ORDER BY \`${this.key}\` DESC ${this.sqlGroupBy} LIMIT ?`, - [queryType.id, ...bind, limit], + `SELECT *, CONVERT(\`${this.key}\`, CHAR) AS \`${this.key}\` FROM \`${this.table}\` WHERE \`${this.key}\` < ? ORDER BY \`${this.key}\` DESC LIMIT ?`, + [queryType.id, limit], ) as unknown as data; return data; } case "after": { const [data] = await database.query( - `SELECT ${this.select}, CONVERT(\`${this.key}\`, CHAR) AS \`${this.key}\` FROM \`${this.table}\` WHERE \`${this.key}\` > ? ${sqlwhere} ORDER BY \`${this.key}\` DESC ${this.sqlGroupBy} LIMIT ?`, - [queryType.id, ...bind, limit], + `SELECT *, CONVERT(\`${this.key}\`, CHAR) AS \`${this.key}\` FROM \`${this.table}\` WHERE \`$this.key\` > ? ORDER BY \`${this.key}\` DESC LIMIT ?`, + [queryType.id, limit], ) as unknown as data; return data; } case "around": { const [data] = await database.query( - ` SELECT ${this.select}, CONVERT(\`${this.key}\`, CHAR) AS \`${this.key}\` FROM (\n` + - `(SELECT * FROM \`${this.table}\` WHERE \`${this.key}\` >= ? ${sqlwhere} ORDER BY \`${this.key}\` ${this.sqlGroupBy} ASC LIMIT ?)\n` + + `(SELECT *, CONVERT(\`${this.key}\`, CHAR) AS \`${this.key}\` FROM \`${this.table}\` WHERE \`${this.key}\` >= ? ORDER BY \`${this.key}\` ASC LIMIT ?)\n` + "UNION ALL\n" + - `(SELECT ${this.select} FROM \`${this.table}\` WHERE \`${this.key}\` < ? ${sqlwhere} ORDER BY \`${this.key}\` DESC ${this.sqlGroupBy} LIMIT ?)\n` + - `) as \`x\` ORDER BY \`${this.key}\` DESC`, - [queryType.id, ...bind, Math.ceil(limit / 2), queryType.id, ...bind, Math.floor(limit / 2)], + `(SELECT *, CONVERT(\`${this.key}\`, CHAR) AS \`${this.key}\` FROM \`${this.table}\` WHERE \`${this.key}\` < ? ORDER BY \`${this.key}\` DESC LIMIT ?)\n` + + "ORDER BY `id` DESC", + [queryType.id, Math.ceil(limit / 2), queryType.id, Math.floor(limit / 2)], ) as unknown as data; return data; } case null: { const [data] = await database.query( - `SELECT ${this.select}, CONVERT(\`${this.key}\`, CHAR) AS \`${this.key}\` FROM \`${this.table}\` WHERE TRUE ${sqlwhere} ORDER BY \`${this.key}\` DESC ${this.sqlGroupBy} LIMIT ?`, - [...bind, limit], + `SELECT *, CONVERT(\`${this.key}\`, CHAR) AS \`${this.key}\` FROM \`${this.table}\` ORDER BY \`${this.key}\` DESC LIMIT ?`, + [limit], ) as unknown as data; return data; } @@ -111,13 +101,7 @@ export default class BaaPagination = [], - ) { + RESTget(e: H3Event, defaultLimit = 50, limitLimit = 200) { const query = getQuery(e); let limit = defaultLimit; @@ -137,7 +121,7 @@ export default class BaaPagination>( @@ -154,7 +138,7 @@ export default class BaaPagination body[field])); await database.query( - `INSERT INTO \`${this.table}\` ` + + "INSERT INTO `clients` " + `(\`${this.key}\`,\`${fields.join("`, `")}\`) ` + "VALUES (" + "?, ".repeat(fields.length) + @@ -162,8 +146,6 @@ export default class BaaPagination; @@ -212,7 +194,7 @@ export default class BaaPagination; @@ -232,30 +214,17 @@ export default class BaaPagination = [], - ) { - const sqlwhere = where !== "" ? `WHERE ${where}` : ""; + async RESTrecordCount(e :H3Event) { const [[data]] = await database.query( - `SELECT COUNT(*) as \`count\` FROM \`${this.table}\` ${sqlwhere} ${this.sqlGroupBy}`, - bind, + `SELECT COUNT(*) as \`count\` FROM \`${this.table}\``, ) as data<{count: number}>; if (!data) throw createError("Database returned no rows"); return data; } - constructor( - table: string, - key: keyType, - select = "*", - groupBy = "", - ) { + constructor(table: string, key: keyType) { this.table = table; this.key = key; - this.select = select; - this.groupBy = groupBy; } } diff --git a/server/utils/database.ts b/server/utils/database.ts index d956d52..7510f9e 100644 --- a/server/utils/database.ts +++ b/server/utils/database.ts @@ -1,17 +1,11 @@ -import mysql, { Connection } from "mysql2/promise"; +import mysql from "mysql2/promise"; -const connectionOptions: mysql.ConnectionOptions = { +export const database = await mysql.createConnection({ host: process.env.DB_HOST, port: Number(process.env.DB_PORT), user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_SCHEMA, - decimalNumbers: true, - supportBigNumbers: true, -}; - -export const database = - await mysql.createConnection(connectionOptions) as Connection & { new: () => Promise }; -database.new = () => { return mysql.createConnection(connectionOptions); }; +}); export type data = [T[], mysql.FieldPacket[]]; diff --git a/server/utils/getRequestingUser.ts b/server/utils/getRequestingUser.ts deleted file mode 100644 index 45a0037..0000000 --- a/server/utils/getRequestingUser.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* global getCookie, createError */ -import { H3Event } from "h3"; - -import { database, data } from "./database"; -import { user } from "~/utils/types/database"; - -export default async function getRequestingUser(e: H3Event) { - const cookie = getCookie(e, "token"); - const [[user]] = await database.query( - ["SELECT", - "CONVERT(`users`.`id`, CHAR) as `id`,", - "`users`.`username`,", - "`users`.`email`,", - "`users`.`display_name`", - "FROM", - "`sessions`", - "LEFT JOIN `users` ON `sessions`.`user` = `users`.`id`", - "WHERE `sessions`.`id` = ?", - ].join(" "), - [cookie], - ) as data; - - if (!user) throw createError("User not found"); - return user; -} diff --git a/utils/snowflake.ts b/server/utils/snowflake.ts similarity index 86% rename from utils/snowflake.ts rename to server/utils/snowflake.ts index b91bddc..829884c 100644 --- a/utils/snowflake.ts +++ b/server/utils/snowflake.ts @@ -31,11 +31,7 @@ export default class Snowflake { this.state = state + (value << 0n); } - constructor(value?: bigint) { - if (value) { - this.state = BigInt.asUintN(64, value); - return; - } + constructor() { this.set_timestamp(Date.now()); this.set_processid(1); this.set_increment(Snowflake.increment()); @@ -44,8 +40,4 @@ export default class Snowflake { public toString() { return this.state.toString(); } - - public get timestamp() { - return BigInt.asUintN(64 - 22, this.state >> 22n); - } } diff --git a/server/utils/validation.ts b/server/utils/validation.ts deleted file mode 100644 index 300a55d..0000000 --- a/server/utils/validation.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* global createError */ - -export function createValidationError(errors: Map) { - let message = "Invalid parameters: "; - for (const i in errors) - message += i + ", "; - message = message.slice(0, -2); - return createError({ - statusCode: 400, - message, - data: { - errors: Object.fromEntries(errors), - }, - }); -} - -export function handleRecursedValidationError(e: unknown, errors: Map, element: string) { - if (typeof e !== "object") throw e; - if (!e) throw e; - if (!(e as any).data || !(e as any).message) throw e; - const upstreamErrors = (e as any).data.errors as any; - const upstreamMessage = (e as any).message; - if (upstreamErrors) { - for (const j in upstreamErrors) - errors.set(`${element}.${j}`, String(upstreamErrors[j])); - } else if (upstreamMessage) { - errors.set(`${element}`, String(upstreamMessage)); - } else { - throw e; - } -} diff --git a/utils/types/database.ts b/utils/types/database.ts index 28a4ddc..5978975 100644 --- a/utils/types/database.ts +++ b/utils/types/database.ts @@ -1,99 +1,68 @@ export interface client { - id: string; - name: string | null; - address: string | null; - phone: string | null; - email: string | null; + id: string, + name: string | null, + address: string | null, + phone: string | null, + email: string | null, } export interface user { - id: string; - username: string; - email: string; - display_name?: string; + id: string, + username: string, + email: string, + display_name?: string, } export interface session { - id: string; - user: string; - expiry_date: string; + id: string, + user: string, + expiry_date: string, } export interface imported_product { - id: string; + id: string, // order: string, - name?: string; - link: string; - price_imported: string; - price: string; + name?: string, + link: string, + price_imported: string, + price: string, } export interface offer { - id: string; - name: string; - description?: string; - recommended_price?: string; + id: string, + name: string, + description?: string, + recommended_price?: string, } export interface work { - id: string; + id: string, // order: string, - offer: string | offer; - price: string; - notes: string; - is_fulfilled: boolean; + offer: string|offer, + price: string, + notes: string, + is_fulfilled: boolean, } export interface order { - imported_products: Array<{ - id: string; - name: string | null; - link: string; - price: string; - price_imported: string; - }>; - work: { - id: string; - offer: offer; - price: number; - notes: string | null; - is_fulfilled: 0 | 1; - }[]; - id: string; - client: string; - user: string; - is_draft: 0 | 1; - value: number; -} - -export interface orderSummary { - id: string; - client: string; - user: string; - is_draft: 0 | 1; - value: string; - imported_products_count: number; - work_count: number; + id: string, + client: client|string, + user: user|string, + is_draft: boolean, + imported_products: imported_product[], + work: work[], } export interface work_template { - id: string; + id: string, // order_template: string, - offer: string | offer; - price: string; - notes?: string; + offer: string|offer, + price: string, + notes?: string, } export interface order_template { - id: string; - name: string; - description?: string; + id: string, + name: string, + description?: string, } - -// 1 is true, 0 is false -export type Dboolean = - | boolean - | 0 // false - | 1; // true - -export type Dnumber = number | `${number}`;