Replace mysql2 with prisma
also I updated packages, and properly typed api input a lot of time was spent, I don't remeber what really I did x3 but everything was related to replacing mysql2 with prisma
This commit is contained in:
parent
be1e3909b6
commit
eebf25198d
39 changed files with 1081 additions and 1292 deletions
|
@ -1,11 +1,10 @@
|
|||
import { defineEventHandler, getQuery } from "h3";
|
||||
import { createError } from "#imports";
|
||||
import { defineEventHandler } from "h3";
|
||||
|
||||
import BaaPagination from "~/server/utils/baaPagination";
|
||||
import { type client } from "~/utils/types/database";
|
||||
|
||||
export const baaWrapper = new BaaPagination<client, "id">("clients", "id");
|
||||
import getPaginatedParameters from "../utils/baaPageParsing";
|
||||
import { database } from "../utils/database";
|
||||
import { prismaToWeb } from "~/server/utils/prismaToWeb";
|
||||
|
||||
export default defineEventHandler((e) => {
|
||||
return baaWrapper.RESTget(e);
|
||||
const pageParameters = getPaginatedParameters(e, 50, 200);
|
||||
return database.client.findPaginated(pageParameters, {}).then(prismaToWeb);
|
||||
});
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import { defineEventHandler, readBody } from "h3";
|
||||
import { defineEventHandler, readBody, setResponseStatus } from "h3";
|
||||
import { type Client } from "@prisma/client";
|
||||
|
||||
import { baaWrapper } from "./clients.get";
|
||||
import { type client } from "~/utils/types/database";
|
||||
import getRequestingUser from "../utils/getRequestingUser";
|
||||
import { database } from "../utils/database";
|
||||
import { prismaToWeb } from "~/server/utils/prismaToWeb";
|
||||
import Snowflake from "~/utils/snowflake";
|
||||
|
||||
import { createError } from "#imports";
|
||||
|
||||
|
@ -12,10 +15,10 @@ const clientKeys: Array<string> = [
|
|||
"email",
|
||||
];
|
||||
|
||||
export function checkIsClient(
|
||||
export function checkIsClient<Patch extends boolean = boolean>(
|
||||
value: any,
|
||||
required = false,
|
||||
): value is Partial<Omit<client, "id">> {
|
||||
patch: Patch,
|
||||
): value is Patch extends true ? Partial<Omit<Client, "id">> : Omit<Client, "id"> {
|
||||
const errors = new Map<string, string>();
|
||||
|
||||
if (typeof value !== "object") {
|
||||
|
@ -25,12 +28,12 @@ export function checkIsClient(
|
|||
});
|
||||
}
|
||||
|
||||
if (!(typeof value.name === "string" || value.name === null || (!required && value.name === undefined))) errors.set("name", "is not string or null");
|
||||
if (!(typeof value.address === "string" || value.address === null || (!required && value.address === undefined))) errors.set("address", "is not string or null");
|
||||
if (!(typeof value.phone === "string" || value.phone === null || (!required && value.phone === undefined))) errors.set("phone", "is not string or null");
|
||||
if (!(typeof value.email === "string" || value.email === null || (!required && value.email === undefined))) errors.set("email", "is not string or null");
|
||||
if (!(typeof value.name === "string" || value.name === null || (!patch && value.name === undefined))) errors.set("name", "is not string or null");
|
||||
if (!(typeof value.address === "string" || value.address === null || (!patch && value.address === undefined))) errors.set("address", "is not string or null");
|
||||
if (!(typeof value.phone === "string" || value.phone === null || (!patch && value.phone === undefined))) errors.set("phone", "is not string or null");
|
||||
if (!(typeof value.email === "string" || value.email === null || (!patch && value.email === undefined))) errors.set("email", "is not string or null");
|
||||
|
||||
for (const i in value as Partial<Omit<client, "id">>)
|
||||
for (const i in value as Partial<Omit<Client, "id">>)
|
||||
if (!clientKeys.includes(i)) errors.set(i, `excessive property`);
|
||||
|
||||
if (errors.size !== 0) {
|
||||
|
@ -50,6 +53,20 @@ export function checkIsClient(
|
|||
return true;
|
||||
}
|
||||
|
||||
export default defineEventHandler((e) => {
|
||||
return baaWrapper.RESTpost(e, clientKeys as Array<keyof Omit<client, "id">>, (o): o is Omit<client, "id"> => checkIsClient(o, true));
|
||||
export default defineEventHandler(async (e) => {
|
||||
const body = await readBody(e);
|
||||
const id = new Snowflake().state;
|
||||
const user = await getRequestingUser(e);
|
||||
|
||||
if (!checkIsClient(body, false)) throw createError({ message: "Invalid body", statusCode: 400 });
|
||||
|
||||
const rvalue = await database.client.create({
|
||||
data: {
|
||||
...body,
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
setResponseStatus(e, 201);
|
||||
return prismaToWeb(rvalue);
|
||||
});
|
||||
|
|
|
@ -1,7 +1,22 @@
|
|||
import { defineEventHandler } from "h3";
|
||||
|
||||
import { baaWrapper } from "../clients.get";
|
||||
import { database } from "~/server/utils/database";
|
||||
|
||||
export default defineEventHandler((e) => {
|
||||
return baaWrapper.RESTdeleteRecord(e);
|
||||
import { createError } from "#imports";
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const id = e.context.params?.id as string;
|
||||
|
||||
try {
|
||||
await database.client.delete({
|
||||
where: {
|
||||
id: BigInt(id),
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
// FIXME: should be 500 on errors other than "RecordNotFound"
|
||||
throw createError({ statusCode: 404 });
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
|
|
@ -1,7 +1,18 @@
|
|||
import { defineEventHandler } from "h3";
|
||||
|
||||
import { baaWrapper } from "../clients.get";
|
||||
import { database } from "~/server/utils/database";
|
||||
import { prismaToWeb } from "~/server/utils/prismaToWeb";
|
||||
|
||||
export default defineEventHandler((e) => {
|
||||
return baaWrapper.RESTgetRecord(e);
|
||||
import { createError } from "#imports";
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const key = e.context.params?.id as string;
|
||||
const rvalue = await database.client.findUnique({
|
||||
where: {
|
||||
id: BigInt(key),
|
||||
},
|
||||
});
|
||||
|
||||
if (!rvalue) throw createError({ statusCode: 404 });
|
||||
return prismaToWeb(rvalue);
|
||||
});
|
||||
|
|
|
@ -1,8 +1,23 @@
|
|||
import { defineEventHandler } from "h3";
|
||||
import { defineEventHandler, readBody } from "h3";
|
||||
|
||||
import { checkIsClient } from "../clients.post";
|
||||
import { baaWrapper } from "../clients.get";
|
||||
import { database } from "~/server/utils/database";
|
||||
import { prismaToWeb } from "~/server/utils/prismaToWeb";
|
||||
|
||||
export default defineEventHandler((e) => {
|
||||
return baaWrapper.RESTpatchRecord(e, checkIsClient);
|
||||
import { createError } from "#imports";
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const body = await readBody(e);
|
||||
const id = e.context.params?.id as string;
|
||||
|
||||
if (!checkIsClient(body, true)) throw createError({ message: "Invalid body", statusCode: 400 });
|
||||
|
||||
const rvalue = await database.client.update({
|
||||
where: {
|
||||
id: BigInt(id),
|
||||
},
|
||||
data: body,
|
||||
});
|
||||
|
||||
return prismaToWeb(rvalue);
|
||||
});
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
import { defineEventHandler } from "h3";
|
||||
|
||||
import { baaWrapper } from "~/server/api/orders.get";
|
||||
import { getOrders } from "~/server/api/orders.get";
|
||||
import { prismaToWeb } from "~/server/utils/prismaToWeb";
|
||||
import getPaginatedParameters from "~/server/utils/baaPageParsing";
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const baa = await baaWrapper.RESTget(e, 50, 200, "`client` = ?", [e.context.params?.id]);
|
||||
console.log(baa);
|
||||
return baa;
|
||||
export default defineEventHandler((e) => {
|
||||
const pageParameters = getPaginatedParameters(e, 50, 200);
|
||||
const clientId = e.context.params?.id as string;
|
||||
return getOrders(
|
||||
pageParameters,
|
||||
{
|
||||
clientId: BigInt(clientId),
|
||||
},
|
||||
).then(prismaToWeb);
|
||||
});
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { defineEventHandler } from "h3";
|
||||
|
||||
import { baaWrapper } from "../clients.get";
|
||||
import { database } from "~/server/utils/database";
|
||||
|
||||
export default defineEventHandler((e) => {
|
||||
return baaWrapper.RESTrecordCount(e);
|
||||
export default defineEventHandler(async (e) => {
|
||||
return {
|
||||
count: await database.client.count({}),
|
||||
};
|
||||
});
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import { defineEventHandler } from "h3";
|
||||
import { type data, database } from "../utils/database";
|
||||
import { database } from "../utils/database";
|
||||
|
||||
export async function isFirstRun() {
|
||||
const [tables] = await database.query({ sql: "SHOW TABLES", rowsAsArray: true }, []) as data<[string]>;
|
||||
if (tables.length === 0) return true;
|
||||
if (!tables.find(a => a[0] === "users")) return true;
|
||||
const [[users]] = await database.query("SELECT COUNT(*) as `count` FROM `users`") as data<{count: number}>;
|
||||
if (users.count === 0) return true;
|
||||
return false;
|
||||
try {
|
||||
const numberOfUsers = await database.user.count();
|
||||
return numberOfUsers === 0;
|
||||
} catch {
|
||||
// We could fall here if the database is not initialized
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export default defineEventHandler((e) => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import fs from "node:fs/promises";
|
||||
import { execSync } from "node:child_process";
|
||||
import { defineEventHandler, setResponseStatus, readBody } from "h3";
|
||||
|
||||
import { database as db } from "../utils/database";
|
||||
import { database } from "../utils/database";
|
||||
import { isFirstRun } from "./firstRun.get";
|
||||
import { getPasswordHash } from "./login.post";
|
||||
import Snowflake from "~/utils/snowflake";
|
||||
|
@ -11,7 +11,7 @@ import { createError } from "#imports";
|
|||
export default defineEventHandler(async (e) => {
|
||||
if (!isFirstRun()) {
|
||||
setResponseStatus(e, 404);
|
||||
return "";
|
||||
return null;
|
||||
}
|
||||
|
||||
const body = await readBody(e);
|
||||
|
@ -23,12 +23,14 @@ export default defineEventHandler(async (e) => {
|
|||
const email = body.email;
|
||||
if (typeof email !== "string") throw createError({ message: "email is not string", statusCode: 400 });
|
||||
|
||||
const sql = await fs.readFile("./schemaModel.sql", "utf-8");
|
||||
|
||||
const database = await db.new({ multipleStatements: true });
|
||||
await database.query(sql);
|
||||
await database.execute(
|
||||
"INSERT INTO `users` (`id`, `username`, `password`, `email`) VALUES (?, ?, ?, ?)",
|
||||
[new Snowflake().toString(), username, getPasswordHash(password), email]);
|
||||
return "";
|
||||
execSync("npx prisma db push --force-reset");
|
||||
database.user.create({
|
||||
data: {
|
||||
id: new Snowflake().state,
|
||||
username,
|
||||
email,
|
||||
password: getPasswordHash(password),
|
||||
},
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import crypto from "crypto";
|
||||
import { defineEventHandler, getCookie, setCookie, readBody } from "h3";
|
||||
|
||||
import { database, type data } from "../utils/database";
|
||||
import { database } from "../utils/database";
|
||||
import { isString } from "../utils/isString";
|
||||
import { cookieSettings } from "../utils/rootUtils";
|
||||
import Snowflake from "~/utils/snowflake";
|
||||
|
@ -28,19 +28,26 @@ export default defineEventHandler(async (e) => {
|
|||
|
||||
const hashedPassword = getPasswordHash(password);
|
||||
|
||||
const [account] = await database.query(
|
||||
"SELECT CONVERT(`id`, CHAR(32)) AS `id` from `users` WHERE `username` = ? AND `password` = ? LIMIT 1",
|
||||
[login, hashedPassword],
|
||||
)as unknown as data<{id: string}>;
|
||||
const account = await database.user.findUnique({
|
||||
where: {
|
||||
username: login,
|
||||
password: hashedPassword,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (account.length === 0) throw createError({ statusCode: 400, message: "Invalid username or password." });
|
||||
if (account === null) throw createError({ statusCode: 400, message: "Invalid username or password." });
|
||||
|
||||
const sessionId = new Snowflake().toString();
|
||||
const sessionId = new Snowflake();
|
||||
|
||||
await database.query(
|
||||
"INSERT INTO `sessions` (`id`, `user`) VALUES ( ? , ? )",
|
||||
[sessionId, account[0].id],
|
||||
);
|
||||
setCookie(e, "token", sessionId, cookieSettings);
|
||||
return { message: "Login successful", token: sessionId };
|
||||
await database.session.create({
|
||||
data: {
|
||||
id: sessionId.state,
|
||||
userId: account.id,
|
||||
},
|
||||
});
|
||||
setCookie(e, "token", sessionId.toString(), cookieSettings);
|
||||
return { message: "Login successful", token: sessionId.toString() };
|
||||
});
|
||||
|
|
|
@ -23,9 +23,10 @@ export default defineEventHandler(async (e) => {
|
|||
});
|
||||
}
|
||||
|
||||
database.query(
|
||||
"DELETE FROM `sessions` WHERE `id` = ?",
|
||||
[token],
|
||||
);
|
||||
database.session.delete({
|
||||
where: {
|
||||
id: BigInt(token),
|
||||
},
|
||||
});
|
||||
return { message: "Logged out" };
|
||||
});
|
||||
|
|
|
@ -1,37 +1,65 @@
|
|||
import { defineEventHandler } from "h3";
|
||||
import { type Order, type Client, Prisma } from "@prisma/client";
|
||||
|
||||
import BaaPagination from "../utils/baaPagination";
|
||||
import { type data, database } from "../utils/database";
|
||||
import { type client, type orderSummary } from "~/utils/types/database";
|
||||
import getPaginatedParameters, { type pageData } from "../utils/baaPageParsing";
|
||||
import { database } from "../utils/database";
|
||||
import { prismaToWeb } from "~/server/utils/prismaToWeb";
|
||||
|
||||
export const baaWrapper = new BaaPagination<orderSummary, "id">(
|
||||
"orderSummaries",
|
||||
"id",
|
||||
"*, CONVERT(`client`, CHAR) AS `client`, CONVERT(`user`, CHAR) as `user`",
|
||||
);
|
||||
type orderSummary = Omit<Order, "clientId"> & {
|
||||
client: Client;
|
||||
value: number;
|
||||
imported_products_count: number;
|
||||
work_count: number;
|
||||
};
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const orders = await baaWrapper.RESTget(e, 50, 200);
|
||||
export async function getOrders(
|
||||
pageParameters: pageData,
|
||||
where?: Prisma.OrderWhereInput,
|
||||
) {
|
||||
const data = await database.order.findPaginated(
|
||||
pageParameters,
|
||||
{
|
||||
select: {
|
||||
id: true,
|
||||
client: true,
|
||||
userId: true,
|
||||
draft: true,
|
||||
imported_products: {
|
||||
select: {
|
||||
price: true,
|
||||
},
|
||||
},
|
||||
work: {
|
||||
select: {
|
||||
price: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
where,
|
||||
},
|
||||
);
|
||||
|
||||
const uniqueClients: Array<string> = [];
|
||||
for (const i of orders) {
|
||||
if (!uniqueClients.includes(i.client))
|
||||
uniqueClients.push(database.escape(i.client));
|
||||
const rvalue = new Array<orderSummary>();
|
||||
|
||||
for (const i of data) {
|
||||
const importedProductsPriceSum = i.imported_products.reduce((pv, cv) => pv + cv.price.toNumber(), 0);
|
||||
const workPriceSum = i.work.reduce((pv, cv) => pv + cv.price.toNumber(), 0);
|
||||
|
||||
rvalue.push({
|
||||
id: i.id,
|
||||
client: i.client,
|
||||
draft: i.draft,
|
||||
imported_products_count: i.imported_products.length,
|
||||
userId: i.userId,
|
||||
value: importedProductsPriceSum + workPriceSum,
|
||||
work_count: i.work.length,
|
||||
});
|
||||
}
|
||||
|
||||
const [clients] = await database.query(
|
||||
["SELECT",
|
||||
"*,",
|
||||
"CONVERT(`id`, CHAR) AS `id`",
|
||||
"FROM `clients`",
|
||||
"WHERE `id` IN",
|
||||
`(${uniqueClients.join(', ')})`,
|
||||
].join(" "),
|
||||
) as data<client>;
|
||||
|
||||
const rvalue: Array<Omit<typeof orders, "client"> | { client?: client }> = [];
|
||||
|
||||
for (const i of orders)
|
||||
rvalue.push({ ...i, client: clients.find(e => i.client === e.id) });
|
||||
return rvalue;
|
||||
}
|
||||
|
||||
export default defineEventHandler((e) => {
|
||||
const pageParameters = getPaginatedParameters(e, 50, 200);
|
||||
return getOrders(pageParameters, {}).then(prismaToWeb);
|
||||
});
|
||||
|
|
|
@ -1,39 +1,44 @@
|
|||
import { defineEventHandler, readBody, setResponseStatus } from "h3";
|
||||
import * as Prisma from "@prisma/client";
|
||||
|
||||
import { createValidationError, handleRecursedValidationError } from "../utils/validation";
|
||||
import { database as db } from "../utils/database";
|
||||
import { database } from "../utils/database";
|
||||
import getRequestingUser from "../utils/getRequestingUser";
|
||||
import { getOrder } from "./orders/[id].get";
|
||||
import { prismaToWeb } from "~/server/utils/prismaToWeb";
|
||||
import Snowflake from "~/utils/snowflake";
|
||||
|
||||
import { createError } from "#imports";
|
||||
|
||||
type importedProduct = {
|
||||
type importedProduct<inOrder extends boolean = boolean> = {
|
||||
orderId: inOrder extends true ? never : string,
|
||||
name: string | null,
|
||||
link: string,
|
||||
price_imported: number,
|
||||
price: number,
|
||||
}
|
||||
|
||||
type work = {
|
||||
offer: string,
|
||||
type work<inOrder extends boolean = boolean> = {
|
||||
orderId: inOrder extends true ? never : string,
|
||||
offerId: string,
|
||||
price: number,
|
||||
notes: string | null,
|
||||
is_fulfilled: boolean | 0 | 1,
|
||||
fulfilled: boolean,
|
||||
}
|
||||
|
||||
type order = {
|
||||
client: string,
|
||||
// user: string,
|
||||
is_draft: boolean | 0 | 1,
|
||||
imported_products: Array<importedProduct>,
|
||||
work: Array<work>,
|
||||
clientId: string,
|
||||
// userId: string,
|
||||
draft: boolean,
|
||||
imported_products: Array<importedProduct<true>>,
|
||||
work: Array<work<true>>,
|
||||
};
|
||||
|
||||
export function checkIsWork<Patch extends boolean = boolean>(
|
||||
export function checkIsWork<Patch extends boolean = boolean, inOrder extends boolean = boolean>(
|
||||
value: any,
|
||||
patch: Patch,
|
||||
): value is Patch extends true ? Partial<work> : work {
|
||||
needsOrderId: inOrder,
|
||||
): value is Patch extends true ? Partial<work<inOrder>> : work<inOrder> {
|
||||
const errors = new Map<string, string>();
|
||||
|
||||
if (typeof value !== "object") {
|
||||
|
@ -43,19 +48,24 @@ export function checkIsWork<Patch extends boolean = boolean>(
|
|||
});
|
||||
}
|
||||
|
||||
if (!(typeof value.offer === "string" || (patch && value.offer === undefined))) errors.set("offer", "is not string");
|
||||
if (!(typeof value.orderId === "string" || (patch && value.orderId === undefined) || !needsOrderId)) errors.set("orderId", "is not string");
|
||||
if (!(typeof value.offerId === "string" || (patch && value.offerId === undefined))) errors.set("offerId", "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 (!(typeof value.is_fulfilled === "boolean" || (patch && value.is_fulfilled === undefined))) errors.set("is_fulfilled", "is not boolean");
|
||||
|
||||
// TODO: Excessive property checking
|
||||
// Excessive properties should be checked and an error should be thrown if there is one
|
||||
|
||||
if (errors.size !== 0) throw createValidationError(errors);
|
||||
return true;
|
||||
}
|
||||
|
||||
export function checkIsImportedProduct<Patch extends boolean = boolean>(
|
||||
export function checkIsImportedProduct<Patch extends boolean = boolean, inOrder extends boolean = boolean>(
|
||||
value: any,
|
||||
patch: Patch,
|
||||
): value is Patch extends true ? Partial<importedProduct> : importedProduct {
|
||||
needsOrderId: inOrder,
|
||||
): value is Patch extends true ? Partial<importedProduct<inOrder>> : importedProduct<inOrder> {
|
||||
const errors = new Map<string, string>();
|
||||
|
||||
if (typeof value !== "object") {
|
||||
|
@ -65,10 +75,14 @@ export function checkIsImportedProduct<Patch extends boolean = boolean>(
|
|||
});
|
||||
}
|
||||
|
||||
if (!(typeof value.orderId === "string" || (patch && value.orderId === undefined) || !needsOrderId)) errors.set("orderId", "is not string");
|
||||
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 (!(typeof value.price === "number" || (patch && value.price === undefined))) errors.set("price", "is not number");
|
||||
|
||||
// TODO: Excessive property checking
|
||||
// Excessive properties should be checked and an error should be thrown if there is one
|
||||
|
||||
if (errors.size !== 0) throw createValidationError(errors);
|
||||
|
||||
|
@ -78,7 +92,7 @@ export function checkIsImportedProduct<Patch extends boolean = boolean>(
|
|||
export function checkIsOrder<Patch extends boolean = boolean>(
|
||||
value: any,
|
||||
patch: Patch,
|
||||
): value is Patch extends true ? Partial<Pick<order, "client" | "is_draft">> : order {
|
||||
): value is Patch extends true ? Partial<Omit<order, "imported_products" | "work">> : order {
|
||||
const errors = new Map<string, string>();
|
||||
|
||||
if (typeof value !== "object") {
|
||||
|
@ -88,19 +102,22 @@ export function checkIsOrder<Patch extends boolean = boolean>(
|
|||
});
|
||||
}
|
||||
|
||||
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 (!(typeof value.clientId === "string" || (patch && value.clientId === undefined))) errors.set("clientId", "is not string");
|
||||
if (!(typeof value.draft === "boolean" || (patch && value.is_draft === undefined))) errors.set("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");
|
||||
|
||||
// TODO: Excessive property checking
|
||||
// Excessive properties should be checked and an error should be thrown if there is one
|
||||
|
||||
if (!patch) {
|
||||
const importedProducts = value.imported_products;
|
||||
if (importedProducts instanceof Array) {
|
||||
for (const i in importedProducts) {
|
||||
try {
|
||||
checkIsImportedProduct(importedProducts[i], patch);
|
||||
checkIsImportedProduct(importedProducts[i], patch, false);
|
||||
} catch (e) {
|
||||
handleRecursedValidationError(e, errors, `imported_products[${i}]`);
|
||||
}
|
||||
|
@ -111,7 +128,7 @@ export function checkIsOrder<Patch extends boolean = boolean>(
|
|||
if (work instanceof Array) {
|
||||
for (const i in work) {
|
||||
try {
|
||||
checkIsWork(work[i], patch);
|
||||
checkIsWork(work[i], patch, false);
|
||||
} catch (e) {
|
||||
handleRecursedValidationError(e, errors, `work[${i}]`);
|
||||
}
|
||||
|
@ -126,49 +143,49 @@ export function checkIsOrder<Patch extends boolean = boolean>(
|
|||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const body = await readBody(e);
|
||||
const id = new Snowflake().toString();
|
||||
const id = new Snowflake().state;
|
||||
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<Promise<any>> = [];
|
||||
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();
|
||||
await database.order.create({
|
||||
data: {
|
||||
clientId: BigInt(body.clientId),
|
||||
draft: body.draft,
|
||||
imported_products: {
|
||||
createMany: {
|
||||
data: body.imported_products.reduce(
|
||||
(pV, cV) => {
|
||||
pV.push({
|
||||
...cV,
|
||||
id: new Snowflake().state,
|
||||
});
|
||||
return pV;
|
||||
},
|
||||
[] as Array<Omit<Prisma.Prisma.ImportedProductCreateManyOrderInput, "orderId">>,
|
||||
),
|
||||
},
|
||||
},
|
||||
work: {
|
||||
createMany: {
|
||||
data: body.work.reduce(
|
||||
(pV, cV) => {
|
||||
pV.push({
|
||||
...cV,
|
||||
id: new Snowflake().state,
|
||||
offerId: BigInt(cV.offerId),
|
||||
});
|
||||
return pV;
|
||||
},
|
||||
[] as Array<Omit<Prisma.Prisma.WorkCreateManyOrderInput, "orderId">>,
|
||||
),
|
||||
},
|
||||
},
|
||||
id,
|
||||
userId: user.id,
|
||||
},
|
||||
});
|
||||
|
||||
setResponseStatus(e, 201);
|
||||
return getOrder(id);
|
||||
return getOrder(id).then(prismaToWeb);
|
||||
});
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
import { defineEventHandler } from "h3";
|
||||
import { type ResultSetHeader } from "mysql2";
|
||||
|
||||
import { database } from "~/server/utils/database";
|
||||
|
||||
import { createError } from "#imports";
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const id = e.context.params?.id;
|
||||
const id = e.context.params?.id as string;
|
||||
|
||||
const [result] = await database.query(
|
||||
"DELETE FROM `orders` WHERE `id` = ?",
|
||||
[id],
|
||||
) as unknown as [ResultSetHeader];
|
||||
|
||||
if (result.affectedRows === 0) throw createError({ statusCode: 404 });
|
||||
try {
|
||||
await database.order.delete({
|
||||
where: {
|
||||
id: BigInt(id),
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
// FIXME: should be 500 on errors other than "RecordNotFound"
|
||||
throw createError({ statusCode: 404 });
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
|
|
@ -1,109 +1,38 @@
|
|||
import { defineEventHandler } from "h3";
|
||||
import { createError } from "#imports";
|
||||
|
||||
import { type offer as offerType, type order } from "~/utils/types/database";
|
||||
import { database, type data } from "~/server/utils/database";
|
||||
import { database } from "~/server/utils/database";
|
||||
import { prismaToWeb } from "~/server/utils/prismaToWeb";
|
||||
|
||||
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 orderExists(id: bigint) {
|
||||
const exists = await database.order.findUnique({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
return exists !== null;
|
||||
}
|
||||
|
||||
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<offerType>;
|
||||
|
||||
// @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<order> {
|
||||
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,
|
||||
}>;
|
||||
export async function getOrder(id: bigint) {
|
||||
const order = await database.order.findUnique({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
include: {
|
||||
imported_products: true,
|
||||
work: {
|
||||
include: {
|
||||
offer: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!order) throw createError({ statusCode: 404 });
|
||||
|
||||
const importedProducts = await getImportedProducts(id);
|
||||
const work = await getWork(id);
|
||||
|
||||
return { ...order, imported_products: importedProducts, work };
|
||||
return order;
|
||||
}
|
||||
|
||||
export default defineEventHandler((e) => {
|
||||
const key = e.context.params?.id;
|
||||
return getOrder(key as string);
|
||||
const key = e.context.params?.id as string;
|
||||
return getOrder(BigInt(key)).then(prismaToWeb);
|
||||
});
|
||||
|
|
|
@ -1,19 +1,27 @@
|
|||
import { defineEventHandler, readBody } from "h3";
|
||||
|
||||
import { checkIsOrder } from "../orders.post";
|
||||
import { database as db } from "~/server/utils/database";
|
||||
import { getOrder } from "./[id].get";
|
||||
import { database } from "~/server/utils/database";
|
||||
import { prismaToWeb } from "~/server/utils/prismaToWeb";
|
||||
|
||||
import { createError } from "#imports";
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const body = await readBody(e);
|
||||
const id = e.context.params?.id;
|
||||
const id = e.context.params?.id as string;
|
||||
|
||||
if (!checkIsOrder(e, true)) throw createError({ message: "Invalid body", statusCode: 400 });
|
||||
if (!checkIsOrder(body, true)) throw createError({ message: "Invalid body", statusCode: 400 });
|
||||
|
||||
const database = await db.new();
|
||||
await database.beginTransaction();
|
||||
await database.order.update({
|
||||
where: {
|
||||
id: BigInt(id),
|
||||
},
|
||||
data: {
|
||||
clientId: body.clientId ? BigInt(body.clientId) : undefined,
|
||||
draft: body.draft,
|
||||
},
|
||||
});
|
||||
|
||||
for (const [k, v] of Object.entries(body))
|
||||
database.query(`UPDATE TABLE \`orders\` SET \`${k}\` = ? WHERE \`id\` = ?`, [v, id]);
|
||||
return getOrder(BigInt(id)).then(prismaToWeb);
|
||||
});
|
||||
|
|
|
@ -1,14 +1,27 @@
|
|||
import { defineEventHandler } from "h3";
|
||||
|
||||
import { orderExists, getImportedProducts } from "../[id].get";
|
||||
import { orderExists } from "../[id].get";
|
||||
import { database } from "~/server/utils/database";
|
||||
import { prismaToWeb } from "~/server/utils/prismaToWeb";
|
||||
|
||||
import { createError } from "#imports";
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const id = e.context.params?.id as string;
|
||||
const orderId = e.context.params?.id as string;
|
||||
|
||||
if (!orderExists(id)) throw createError({ statusCode: 404 });
|
||||
if (!(await orderExists(BigInt(orderId)))) throw createError({ statusCode: 404 });
|
||||
|
||||
const importedProducts = await getImportedProducts(id);
|
||||
return importedProducts;
|
||||
return database.importedProduct.findMany({
|
||||
where: {
|
||||
orderId: BigInt(orderId),
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
link: true,
|
||||
name: true,
|
||||
orderId: true,
|
||||
price: true,
|
||||
price_imported: true,
|
||||
},
|
||||
}).then(prismaToWeb);
|
||||
});
|
||||
|
|
|
@ -1,29 +1,33 @@
|
|||
import { defineEventHandler, readBody, setResponseStatus } from "h3";
|
||||
|
||||
import { checkIsImportedProduct } from "../../orders.post";
|
||||
import { getImportedProducts, orderExists } from "../[id].get";
|
||||
import { orderExists } from "../[id].get";
|
||||
import Snowflake from "~/utils/snowflake";
|
||||
import { database } from "~/server/utils/database";
|
||||
import { prismaToWeb } from "~/server/utils/prismaToWeb";
|
||||
|
||||
import { createError } from "#imports";
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const body = await readBody(e);
|
||||
const idOrder = e.context.params?.id as string;
|
||||
const idImportedProducts = new Snowflake().toString();
|
||||
const idImportedProduct = new Snowflake().state;
|
||||
|
||||
if (!orderExists(idOrder)) throw createError({ statusCode: 404 });
|
||||
if (!checkIsImportedProduct(body, false)) throw createError({ message: "Invalid body", statusCode: 400 });
|
||||
if (!await orderExists(BigInt(idOrder))) throw createError({ statusCode: 404 });
|
||||
if (!checkIsImportedProduct(body, false, 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],
|
||||
);
|
||||
const rvalue = await database.importedProduct.create({
|
||||
data: {
|
||||
id: idImportedProduct,
|
||||
link: body.link,
|
||||
name: body.name,
|
||||
orderId: BigInt(idOrder),
|
||||
price: body.price,
|
||||
price_imported: body.price_imported,
|
||||
},
|
||||
});
|
||||
|
||||
setResponseStatus(e, 201);
|
||||
return getImportedProducts(idOrder);
|
||||
|
||||
return prismaToWeb(rvalue);
|
||||
});
|
||||
|
|
|
@ -1,14 +1,31 @@
|
|||
import { defineEventHandler } from "h3";
|
||||
|
||||
import { orderExists, getWork } from "../[id].get";
|
||||
import { orderExists } from "../[id].get";
|
||||
import { database } from "~/server/utils/database";
|
||||
import { prismaToWeb } from "~/server/utils/prismaToWeb";
|
||||
|
||||
import { createError } from "#imports";
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const id = e.context.params?.id as string;
|
||||
const orderId = e.context.params?.id as string;
|
||||
|
||||
if (!orderExists(id)) throw createError({ statusCode: 404 });
|
||||
if (!await orderExists(BigInt(orderId))) throw createError({ statusCode: 404 });
|
||||
|
||||
const work = await getWork(id);
|
||||
return work;
|
||||
const data = await database.work.findMany({
|
||||
where: {
|
||||
orderId: BigInt(orderId),
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
fulfilled: true,
|
||||
notes: true,
|
||||
offer: true,
|
||||
orderId: true,
|
||||
price: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!data) throw createError({ statusCode: 404 });
|
||||
|
||||
return prismaToWeb(data);
|
||||
});
|
||||
|
|
|
@ -1,30 +1,34 @@
|
|||
import { defineEventHandler, readBody, setResponseStatus } from "h3";
|
||||
import { Decimal } from "@prisma/client/runtime/library";
|
||||
|
||||
import { checkIsWork } from "../../orders.post";
|
||||
import { getWork, orderExists } from "../[id].get";
|
||||
import { orderExists } from "../[id].get";
|
||||
import Snowflake from "~/utils/snowflake";
|
||||
import { database } from "~/server/utils/database";
|
||||
import { prismaToWeb } from "~/server/utils/prismaToWeb";
|
||||
|
||||
import { createError } from "#imports";
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const body = await readBody(e);
|
||||
const idOrder = e.context.params?.id as string;
|
||||
const idWork = new Snowflake().toString();
|
||||
const idWork = new Snowflake().state;
|
||||
|
||||
if (!orderExists(idOrder)) throw createError({ statusCode: 404 });
|
||||
if (!checkIsWork(body, false)) throw createError({ message: "Invalid body", statusCode: 400 });
|
||||
if (!orderExists(BigInt(idOrder))) throw createError({ statusCode: 404 });
|
||||
if (!checkIsWork(body, false, 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],
|
||||
);
|
||||
const rvalue = await database.work.create({
|
||||
data: {
|
||||
id: BigInt(idWork),
|
||||
fulfilled: body.fulfilled,
|
||||
notes: body.notes,
|
||||
offerId: BigInt(body.offerId),
|
||||
orderId: BigInt(body.orderId),
|
||||
price: new Decimal(body.price),
|
||||
},
|
||||
});
|
||||
|
||||
setResponseStatus(e, 201);
|
||||
|
||||
return getWork(idWork);
|
||||
return prismaToWeb(rvalue);
|
||||
});
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import { defineEventHandler } from "h3";
|
||||
|
||||
import { type ResultSetHeader } from "mysql2";
|
||||
import { orderExists } from "../../[id].get";
|
||||
import { database } from "~/server/utils/database";
|
||||
|
||||
import { createError } from "#imports";
|
||||
|
@ -10,13 +8,17 @@ 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 });
|
||||
try {
|
||||
await database.work.delete({
|
||||
where: {
|
||||
id: BigInt(idWork),
|
||||
orderId: BigInt(idOrder),
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
// FIXME: should be 500 on errors other than "RecordNotFound"
|
||||
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;
|
||||
});
|
||||
|
|
|
@ -1,13 +1,32 @@
|
|||
import { defineEventHandler } from "h3";
|
||||
|
||||
import { orderExists, getWork } from "../../[id].get";
|
||||
import { orderExists } from "../../[id].get";
|
||||
import { database } from "~/server/utils/database";
|
||||
import { prismaToWeb } from "~/server/utils/prismaToWeb";
|
||||
|
||||
import { createError } from "#imports";
|
||||
|
||||
export default defineEventHandler((e) => {
|
||||
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 });
|
||||
return getWork(idWork);
|
||||
if (!await orderExists(BigInt(idOrder))) throw createError({ statusCode: 404 });
|
||||
const data = await database.work.findUnique({
|
||||
where: {
|
||||
orderId: BigInt(idOrder),
|
||||
id: BigInt(idWork),
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
fulfilled: true,
|
||||
notes: true,
|
||||
offer: true,
|
||||
orderId: true,
|
||||
price: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!data) throw createError({ statusCode: 404 });
|
||||
|
||||
return prismaToWeb(data);
|
||||
});
|
||||
|
|
|
@ -1,15 +1,8 @@
|
|||
/* global defineEventHandler, getCookie */
|
||||
import { defineEventHandler, getCookie } from "h3";
|
||||
import { defineEventHandler } from "h3";
|
||||
|
||||
import { database, type data } from "~/server/utils/database";
|
||||
import { type user } from "~/utils/types/database";
|
||||
import getRequestingUser from "~/server/utils/getRequestingUser";
|
||||
import { prismaToWeb } from "~/server/utils/prismaToWeb";
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const token = getCookie(e, "token");
|
||||
const [[userData]] = await database.query(
|
||||
"SELECT CONVERT(`users`.`id`, CHAR(32)) as `id`, `users`.`username` as `username`, `users`.`email` as `email`, `users`.`display_name` as `display_name` FROM `sessions` LEFT JOIN `users` ON `sessions`.`user` = `users`.`id` WHERE `sessions`.`id` = ?",
|
||||
[token],
|
||||
) as unknown as data<user>;
|
||||
|
||||
return userData;
|
||||
export default defineEventHandler((e) => {
|
||||
return getRequestingUser(e).then(prismaToWeb);
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue