Compare commits
No commits in common. "90932a49c8b4af7a8f09cfe6e77f75cb42167d7b" and "bbe0c91d7eec37f05beea67dea0ac997230226ae" have entirely different histories.
90932a49c8
...
bbe0c91d7e
8 changed files with 9 additions and 304 deletions
|
@ -1,8 +0,0 @@
|
|||
import { defineNuxtRouteMiddleware, navigateTo, useFetch } from "nuxt/app";
|
||||
|
||||
export default defineNuxtRouteMiddleware(async (to, from) => {
|
||||
const firstRun = await useFetch("/api/firstRun");
|
||||
|
||||
if (firstRun.data.value)
|
||||
return navigateTo({ path: "/firstRun" });
|
||||
});
|
|
@ -1,62 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
/* global $fetch */
|
||||
import { ref } from 'vue';
|
||||
import { NuxtError, navigateTo, useFetch } from 'nuxt/app';
|
||||
import { definePageMeta } from '~/.nuxt/imports';
|
||||
|
||||
import EntryEditor, { fieldDefinition } from '~/components/entryEditor.vue';
|
||||
import Alerts, { AlertData } from '~/components/alerts.vue';
|
||||
|
||||
const editorFields: Array<fieldDefinition> = [
|
||||
{ key: "username", type: "text", label: "Username", optional: false },
|
||||
{ key: "password", type: "text", label: "Password", optional: false },
|
||||
{ key: "email", type: "text", label: "email", optional: false },
|
||||
];
|
||||
|
||||
const formValue = ref<any>({});
|
||||
const alerts = ref<Array<AlertData>>([]);
|
||||
|
||||
definePageMeta({
|
||||
layout: false,
|
||||
});
|
||||
|
||||
async function submit() {
|
||||
try {
|
||||
await $fetch("/api/firstRun", {
|
||||
body: formValue.value,
|
||||
method: "POST",
|
||||
});
|
||||
await navigateTo("/login");
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
alerts.value.push({ text: (e as NuxtError).data.message });
|
||||
}
|
||||
}
|
||||
|
||||
if (!(await useFetch("/api/firstRun")).data.value)
|
||||
await navigateTo("/login");
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Alerts :alerts="alerts" />
|
||||
<VCard max-width="450px" class="mx-auto mt-16" variant="outlined">
|
||||
<template #title>
|
||||
Initial setup
|
||||
</template>
|
||||
<template #text>
|
||||
<p>
|
||||
It looks like you've run the server with an empty or uninitialized database or with database without any users.<br>
|
||||
Below you can initialize the database register your first user and.
|
||||
</p><br>
|
||||
<EntryEditor
|
||||
:fields="editorFields"
|
||||
@update-sub-model-value="(k, v) => { formValue[k] = v }"
|
||||
/>
|
||||
</template>
|
||||
<template #actions>
|
||||
<VBtn color="primary" @click="submit">
|
||||
Initialize
|
||||
</VBtn>
|
||||
</template>
|
||||
</VCard>
|
||||
</template>
|
|
@ -19,7 +19,6 @@ const redirectTo = ref(route.redirectedFrom);
|
|||
|
||||
definePageMeta({
|
||||
layout: false,
|
||||
middleware: ["first-run"],
|
||||
});
|
||||
|
||||
async function submit() {
|
||||
|
|
167
schemaModel.sql
167
schemaModel.sql
|
@ -1,167 +0,0 @@
|
|||
-- Server version 8.0.32
|
||||
|
||||
--
|
||||
-- Table structure for table `users`
|
||||
--
|
||||
|
||||
CREATE TABLE `users` (
|
||||
`id` bigint unsigned NOT NULL DEFAULT (((unix_timestamp() * 1000 * pow(2,22)) + floor((rand() * pow(2,12))))),
|
||||
`username` varchar(30) NOT NULL,
|
||||
`email` varchar(128) NOT NULL,
|
||||
`password` binary(64) NOT NULL,
|
||||
`display_name` varchar(30) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `idusers_UNIQUE` (`id`),
|
||||
UNIQUE KEY `username_UNIQUE` (`username`),
|
||||
UNIQUE KEY `email_UNIQUE` (`email`)
|
||||
);
|
||||
|
||||
--
|
||||
-- Table structure for table `clients`
|
||||
--
|
||||
|
||||
CREATE TABLE `clients` (
|
||||
`id` bigint unsigned NOT NULL DEFAULT (((unix_timestamp() * 1000 * pow(2,22)) + floor((rand() * pow(2,12))))),
|
||||
`name` varchar(128) DEFAULT NULL,
|
||||
`address` varchar(128) DEFAULT NULL,
|
||||
`phone` varchar(16) DEFAULT NULL,
|
||||
`email` varchar(128) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
);
|
||||
|
||||
--
|
||||
-- Table structure for table `orders`
|
||||
--
|
||||
|
||||
CREATE TABLE `orders` (
|
||||
`id` bigint unsigned NOT NULL DEFAULT (((unix_timestamp() * 1000 * pow(2,22)) + floor((rand() * pow(2,12))))),
|
||||
`client` bigint unsigned NOT NULL,
|
||||
`user` bigint unsigned NOT NULL,
|
||||
`is_draft` tinyint NOT NULL DEFAULT '1',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `user_idx` (`user`),
|
||||
KEY `client_idx` (`client`),
|
||||
CONSTRAINT `client` FOREIGN KEY (`client`) REFERENCES `clients` (`id`),
|
||||
CONSTRAINT `user` FOREIGN KEY (`user`) REFERENCES `users` (`id`)
|
||||
);
|
||||
|
||||
--
|
||||
-- Table structure for table `imported_products`
|
||||
--
|
||||
|
||||
CREATE TABLE `imported_products` (
|
||||
`id` bigint unsigned NOT NULL DEFAULT (((unix_timestamp() * 1000 * pow(2,22)) + floor((rand() * pow(2,12))))),
|
||||
`order` bigint unsigned NOT NULL,
|
||||
`name` varchar(128) DEFAULT NULL,
|
||||
`link` varchar(1024) NOT NULL,
|
||||
`price_imported` decimal(10,2) NOT NULL DEFAULT '0.00',
|
||||
`price` decimal(10,2) NOT NULL DEFAULT '0.00',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `order_idx` (`order`),
|
||||
CONSTRAINT `order2` FOREIGN KEY (`order`) REFERENCES `orders` (`id`)
|
||||
);
|
||||
|
||||
--
|
||||
-- Table structure for table `offer`
|
||||
--
|
||||
|
||||
CREATE TABLE `offer` (
|
||||
`id` bigint unsigned NOT NULL DEFAULT (((unix_timestamp() * 1000 * pow(2,22)) + floor((rand() * pow(2,12))))),
|
||||
`name` varchar(45) NOT NULL,
|
||||
`description` text,
|
||||
`recommended_price` decimal(10,2) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
);
|
||||
|
||||
--
|
||||
-- Table structure for table `order_templates`
|
||||
--
|
||||
|
||||
CREATE TABLE `order_templates` (
|
||||
`id` bigint unsigned NOT NULL DEFAULT (((unix_timestamp() * 1000 * pow(2,22)) + floor((rand() * pow(2,12))))),
|
||||
`name` varchar(45) NOT NULL,
|
||||
`description` text,
|
||||
PRIMARY KEY (`id`)
|
||||
);
|
||||
|
||||
--
|
||||
-- Table structure for table `sessions`
|
||||
--
|
||||
|
||||
CREATE TABLE `sessions` (
|
||||
`id` bigint unsigned NOT NULL DEFAULT (((unix_timestamp() * 1000 * pow(2,22)) + floor((rand() * pow(2,12))))),
|
||||
`user` bigint unsigned NOT NULL,
|
||||
`expiry_date` timestamp NULL DEFAULT ((now() + interval 30 day)),
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `user_idx` (`user`),
|
||||
CONSTRAINT `user_session` FOREIGN KEY (`user`) REFERENCES `users` (`id`)
|
||||
);
|
||||
|
||||
--
|
||||
-- Table structure for table `work`
|
||||
--
|
||||
|
||||
CREATE TABLE `work` (
|
||||
`id` bigint unsigned NOT NULL DEFAULT (((unix_timestamp() * 1000 * pow(2,22)) + floor((rand() * pow(2,12))))),
|
||||
`order` bigint unsigned NOT NULL,
|
||||
`offer` bigint unsigned NOT NULL,
|
||||
`price` decimal(10,2) NOT NULL,
|
||||
`notes` text,
|
||||
`is_fulfilled` tinyint NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `order_idx` (`order`),
|
||||
KEY `offer_idx` (`offer`),
|
||||
CONSTRAINT `offer` FOREIGN KEY (`offer`) REFERENCES `offer` (`id`),
|
||||
CONSTRAINT `order` FOREIGN KEY (`order`) REFERENCES `orders` (`id`)
|
||||
);
|
||||
|
||||
--
|
||||
-- Table structure for table `work_templates`
|
||||
--
|
||||
|
||||
CREATE TABLE `work_templates` (
|
||||
`id` bigint unsigned NOT NULL DEFAULT (((unix_timestamp() * 1000 * pow(2,22)) + floor((rand() * pow(2,12))))),
|
||||
`order_template` bigint unsigned NOT NULL,
|
||||
`offer` bigint unsigned NOT NULL,
|
||||
`price` decimal(10,2) NOT NULL DEFAULT '0.00',
|
||||
`notes` text,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `order_template_idx` (`order_template`),
|
||||
KEY `offer_idx` (`offer`),
|
||||
CONSTRAINT `offer2` FOREIGN KEY (`offer`) REFERENCES `offer` (`id`),
|
||||
CONSTRAINT `order_template` FOREIGN KEY (`order_template`) REFERENCES `order_templates` (`id`)
|
||||
);
|
||||
|
||||
--
|
||||
-- Final view structure for view `orderSummaries`
|
||||
--
|
||||
|
||||
CREATE VIEW `orderSummaries` AS
|
||||
SELECT
|
||||
`id`,
|
||||
`client`,
|
||||
`user`,
|
||||
`is_draft`,
|
||||
(COALESCE(`imported_products`.`price`, 0) + COALESCE(`work`.`price`, 0)) AS `value`,
|
||||
COALESCE(`imported_products`.`count`, 0) as `imported_products_count`,
|
||||
COALESCE(`work`.`count`, 0) as `work_count`
|
||||
FROM
|
||||
`orders`
|
||||
LEFT JOIN
|
||||
(
|
||||
SELECT
|
||||
`order`,
|
||||
SUM(`price`) as `price`,
|
||||
COUNT(*) AS `count`
|
||||
FROM `imported_products`
|
||||
GROUP BY `order`
|
||||
) as `imported_products` ON `orders`.`id` = `imported_products`.`order`
|
||||
LEFT JOIN
|
||||
(
|
||||
SELECT
|
||||
`order`,
|
||||
SUM(`price`) AS `price`,
|
||||
COUNT(*) AS `count`
|
||||
FROM `work`
|
||||
GROUP BY `work`.`order`
|
||||
) AS `work` ON `work`.`order` = `orders`.`id`;
|
|
@ -1,15 +0,0 @@
|
|||
/* global defineEventHandler */
|
||||
import { data, 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;
|
||||
}
|
||||
|
||||
export default defineEventHandler((e) => {
|
||||
return isFirstRun();
|
||||
});
|
|
@ -1,33 +0,0 @@
|
|||
/* global defineEventHandler, setResponseStatus, readBody, createError */
|
||||
|
||||
import fs from "node:fs/promises";
|
||||
|
||||
import { database as db } from "../utils/database";
|
||||
import { isFirstRun } from "./firstRun.get";
|
||||
import { getPasswordHash } from "./login.post";
|
||||
import Snowflake from "~/utils/snowflake";
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
if (!isFirstRun()) {
|
||||
setResponseStatus(e, 404);
|
||||
return "";
|
||||
}
|
||||
|
||||
const body = await readBody(e);
|
||||
if (typeof body !== "object") throw createError({ message: "Invalid body", statusCode: 400 });
|
||||
const username = body.username;
|
||||
if (typeof username !== "string") throw createError({ message: "username is not string", statusCode: 400 });
|
||||
const password = body.password;
|
||||
if (typeof password !== "string") throw createError({ message: "password is not string", statusCode: 400 });
|
||||
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 "";
|
||||
});
|
|
@ -6,12 +6,6 @@ import { isString } from "../utils/isString";
|
|||
import { cookieSettings } from "../utils/rootUtils";
|
||||
import Snowflake from "~/utils/snowflake";
|
||||
|
||||
export function getPasswordHash(password: string) {
|
||||
return crypto.createHmac("sha512", "42")
|
||||
.update(password)
|
||||
.digest();
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
if (getCookie(e, "token"))
|
||||
throw createError({ statusCode: 501, message: "Case not implemented: logging in while cookie is set" });
|
||||
|
@ -24,10 +18,12 @@ export default defineEventHandler(async (e) => {
|
|||
if (!isString(login)) throw createError({ statusCode: 400, message: "Login is not string." });
|
||||
if (!isString(password)) throw createError({ statusCode: 400, message: "Password is not string." });
|
||||
|
||||
const hashedPassword = getPasswordHash(password);
|
||||
const hashedPassword = crypto.createHmac("sha512", "42")
|
||||
.update(password)
|
||||
.digest("hex");
|
||||
|
||||
const [account] = await database.query(
|
||||
"SELECT CONVERT(`id`, CHAR(32)) AS `id` from `users` WHERE `username` = ? AND `password` = ? LIMIT 1",
|
||||
"SELECT CONVERT(`id`, CHAR(32)) AS `id` from `users` WHERE `username` = ? AND LOWER(HEX(`password`)) = ? LIMIT 1",
|
||||
[login, hashedPassword],
|
||||
)as unknown as data<{id: string}>;
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ const endpointsWithoutAuth: string[] = [
|
|||
"/hi",
|
||||
"/login",
|
||||
"/logout",
|
||||
"/firstRun",
|
||||
];
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
|
@ -32,14 +31,10 @@ export default defineEventHandler(async (e) => {
|
|||
*/
|
||||
export async function isAuthorised(token: string | undefined): Promise<boolean> {
|
||||
if (!token) return false;
|
||||
try {
|
||||
const [[session]] = await database.query(
|
||||
"SELECT EXISTS(SELECT `id` FROM `sessions` WHERE `id` = ? AND `expiry_date` >= NOW()) as `logged_in`",
|
||||
[token],
|
||||
) as unknown as data<{logged_in: number}>;
|
||||
|
||||
return session.logged_in === 1;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue