Add create file table, insert first user, fully port to tailwind
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { AttachmentId, PieceId, RequestId, SessionId, Sha256, UserId } from "common";
|
||||
import { ColumnType, CompiledQuery, CreateTableBuilder, Insertable, Kysely, Selectable, sql, Updateable } from "kysely";
|
||||
import { BunSqliteDialect } from "kysely-bun-sqlite";
|
||||
import { Database as BunSqliteDatabase } from "bun:sqlite";
|
||||
import { AttachmentId, PieceId, RequestId, SessionId, Sha256, UserId } from "common";
|
||||
import { ColumnType, CompiledQuery, CreateTableBuilder, Kysely } from "kysely";
|
||||
import { BunSqliteDialect } from "kysely-bun-sqlite";
|
||||
|
||||
export function generateSessionId(byteLength: number = 32): SessionId {
|
||||
const array = new Uint8Array(byteLength);
|
||||
@@ -112,6 +112,13 @@ export async function initDatabase(filename: string = "db.sqlite3"): Promise<Kys
|
||||
.column("timestamp")
|
||||
.execute();
|
||||
|
||||
await db.schema
|
||||
.createTable("File")
|
||||
.ifNotExists()
|
||||
.addColumn("sha256", "blob", (c) => c.notNull().primaryKey())
|
||||
.addColumn("data", "blob", (c) => c.notNull())
|
||||
.execute();
|
||||
|
||||
await db.schema
|
||||
.createTable("User")
|
||||
.ifNotExists()
|
||||
@@ -152,11 +159,28 @@ export async function initDatabase(filename: string = "db.sqlite3"): Promise<Kys
|
||||
.ifNotExists()
|
||||
.addColumn("attachmentId", "text", (c) => c.notNull().primaryKey())
|
||||
.addColumn("pieceId", "text", (c) => c.notNull().references("Piece.pieceId").onDelete("cascade").onUpdate("cascade"))
|
||||
.addColumn("sha256", "blob", (c) => c.notNull())
|
||||
.addColumn("sha256", "blob", (c) => c.notNull().references("File.sha256").onDelete("restrict").onUpdate("restrict"))
|
||||
.addColumn("filename", "text", (c) => c.notNull())
|
||||
.addColumn("mediaType", "text", (c) => c.notNull())
|
||||
.$call(systemInformation)
|
||||
.execute();
|
||||
|
||||
const { count } = await db
|
||||
.selectFrom("User")
|
||||
.select((eb) => eb.fn.countAll().as("count"))
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
if (BigInt(count) === 0n) {
|
||||
const userId = UserId(Bun.randomUUIDv7());
|
||||
const username = "admin";
|
||||
const password = await Bun.password.hash("admin");
|
||||
const admin = 1;
|
||||
|
||||
await db
|
||||
.insertInto("User")
|
||||
.values({ userId, username, password, admin })
|
||||
.execute();
|
||||
}
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { StrictMode } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
||||
import { Home } from "./routes/Home";
|
||||
import { Login } from "./routes/Login";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { StrictMode } from "react";
|
||||
import "./style.css";
|
||||
import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { treaty } from "@elysiajs/eden";
|
||||
import type { App } from "backend/app";
|
||||
|
||||
export const client = treaty<App>("localhost:3000");
|
||||
export const client = treaty<App>("localhost:3000", { fetch: { credentials: "include" } });
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
export const logout = {
|
||||
padding: 8,
|
||||
|
||||
backgroundColor: "#C0C0C0",
|
||||
|
||||
borderWidth: 2,
|
||||
borderStyle: "solid",
|
||||
borderTopColor: "#E0E0E0",
|
||||
borderLeftColor: "#E0E0E0",
|
||||
borderRightColor: "#404040",
|
||||
borderBottomColor: "#404040",
|
||||
|
||||
borderRadius: 4,
|
||||
|
||||
cursor: "pointer",
|
||||
|
||||
"selectors": {
|
||||
"&:focus": {
|
||||
outlineWidth: 2,
|
||||
outlineStyle: "solid",
|
||||
outlineColor: "#8080FF",
|
||||
|
||||
"@media": {
|
||||
"(prefers-color-scheme: dark)": {
|
||||
outlineColor: "#C0C0FF",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
"&:active": {
|
||||
borderTopColor: "#404040",
|
||||
borderLeftColor: "#404040",
|
||||
borderRightColor: "#E0E0E0",
|
||||
borderBottomColor: "#E0E0E0",
|
||||
|
||||
"@media": {
|
||||
"(prefers-color-scheme: dark)": {
|
||||
outlineColor: "#C0C0FF",
|
||||
|
||||
borderTopColor: "#202020",
|
||||
borderLeftColor: "#202020",
|
||||
borderRightColor: "#606060",
|
||||
borderBottomColor: "#606060",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
"@media": {
|
||||
"(prefers-color-scheme: dark)": {
|
||||
backgroundColor: "#404040",
|
||||
|
||||
borderTopColor: "#606060",
|
||||
borderLeftColor: "#606060",
|
||||
borderRightColor: "#202020",
|
||||
borderBottomColor: "#202020",
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -2,8 +2,9 @@ import { useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { client } from "../client";
|
||||
import { useStore } from "../store";
|
||||
import { Button } from "../styled/Button";
|
||||
|
||||
export const Home = () => {
|
||||
export function Home() {
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -28,7 +29,7 @@ export const Home = () => {
|
||||
}, []);
|
||||
|
||||
const onLogoutClick = async () => {
|
||||
const { data, error } = await client.logout.post();
|
||||
const { error } = await client.logout.post();
|
||||
|
||||
if (error !== null) {
|
||||
console.error("Response was not ok");
|
||||
@@ -53,15 +54,11 @@ export const Home = () => {
|
||||
Użytkownik: {user.username}
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
className="p-2 bg-stone-300 border-2 border-t-stone-200 border-l-stone-200 border-r-stone-600 border-b-stone-600 rounded"
|
||||
type="button"
|
||||
onClick={onLogoutClick}
|
||||
>
|
||||
<Button type="button" onClick={onLogoutClick}>
|
||||
Wyloguj się
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
export const submit = {
|
||||
padding: 8,
|
||||
|
||||
backgroundColor: "#C0C0C0",
|
||||
|
||||
borderWidth: 2,
|
||||
borderStyle: "solid",
|
||||
borderTopColor: "#E0E0E0",
|
||||
borderLeftColor: "#E0E0E0",
|
||||
borderRightColor: "#404040",
|
||||
borderBottomColor: "#404040",
|
||||
|
||||
borderRadius: 4,
|
||||
|
||||
cursor: "pointer",
|
||||
|
||||
"selectors": {
|
||||
"&:focus": {
|
||||
outlineWidth: 2,
|
||||
outlineStyle: "solid",
|
||||
outlineColor: "#8080FF",
|
||||
|
||||
"@media": {
|
||||
"(prefers-color-scheme: dark)": {
|
||||
outlineColor: "#C0C0FF",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
"&:active": {
|
||||
borderTopColor: "#404040",
|
||||
borderLeftColor: "#404040",
|
||||
borderRightColor: "#E0E0E0",
|
||||
borderBottomColor: "#E0E0E0",
|
||||
|
||||
"@media": {
|
||||
"(prefers-color-scheme: dark)": {
|
||||
outlineColor: "#C0C0FF",
|
||||
|
||||
borderTopColor: "#202020",
|
||||
borderLeftColor: "#202020",
|
||||
borderRightColor: "#606060",
|
||||
borderBottomColor: "#606060",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
"@media": {
|
||||
"(prefers-color-scheme: dark)": {
|
||||
backgroundColor: "#404040",
|
||||
|
||||
borderTopColor: "#606060",
|
||||
borderLeftColor: "#606060",
|
||||
borderRightColor: "#202020",
|
||||
borderBottomColor: "#202020",
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -2,8 +2,10 @@ import { FormEventHandler, useId } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { client } from "../client";
|
||||
import { useStore } from "../store";
|
||||
import { Button } from "../styled/Button";
|
||||
import { Input } from "../styled/Input";
|
||||
|
||||
export const Login = () => {
|
||||
export function Login() {
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -43,9 +45,8 @@ export const Login = () => {
|
||||
<form className="p-2 flex flex-col gap-2 border border-black rounded dark:border-white" onSubmit={onSubmit}>
|
||||
<header className="pb-2 border-b border-black text-center font-bold dark:border-white">Repozytorium muzyczne</header>
|
||||
<label htmlFor={usernameId}>Nazwa użytkownika</label>
|
||||
<input
|
||||
<Input
|
||||
id={usernameId}
|
||||
className="w-[32ch] p-2 bg-transparent border border-black rounded focus:outline focus:outline-2 focus:outline-sky-500 focus:dark:outline-sky-200 dark:border-white"
|
||||
type="text"
|
||||
value={loginUsername}
|
||||
autoFocus
|
||||
@@ -53,21 +54,17 @@ export const Login = () => {
|
||||
onInput={(e) => setLoginUsername(e.currentTarget.value)}
|
||||
/>
|
||||
<label htmlFor={passwordId}>Hasło</label>
|
||||
<input
|
||||
<Input
|
||||
id={passwordId}
|
||||
className="w-[32ch] p-2 bg-transparent border border-black rounded focus:outline focus:outline-2 focus:outline-sky-500 focus:dark:outline-sky-200 dark:border-white"
|
||||
type="password"
|
||||
value={loginPassword}
|
||||
required
|
||||
onInput={(e) => setLoginPassword(e.currentTarget.value)}
|
||||
/>
|
||||
<button
|
||||
className="p-2 bg-stone-300 border-2 border-t-stone-200 border-l-stone-200 border-r-stone-600 border-b-stone-600 rounded dark:bg-stone-700 dark:border-t-stone-600 dark:border-l-stone-600 dark:border-r-stone-900 dark:border-b-stone-900"
|
||||
type="submit"
|
||||
>
|
||||
<Button type="submit">
|
||||
Zaloguj się
|
||||
</button>
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
41
packages/frontend/src/styled/Button.tsx
Normal file
41
packages/frontend/src/styled/Button.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { ButtonHTMLAttributes, DetailedHTMLProps } from "react";
|
||||
|
||||
export namespace Button {
|
||||
export type Props = Omit<DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "className">;
|
||||
}
|
||||
|
||||
export function Button({ children, ...props }: Button.Props) {
|
||||
return (
|
||||
<button
|
||||
{...props}
|
||||
className="
|
||||
p-2
|
||||
bg-stone-300
|
||||
border-2
|
||||
border-t-stone-200
|
||||
border-l-stone-200
|
||||
border-r-stone-600
|
||||
border-b-stone-600
|
||||
active:border-t-stone-600
|
||||
active:border-l-stone-600
|
||||
active:border-r-stone-200
|
||||
active:border-b-stone-200
|
||||
rounded
|
||||
focus:outline
|
||||
focus:outline-2
|
||||
focus:outline-red-500
|
||||
dark:bg-stone-700
|
||||
dark:border-t-stone-600
|
||||
dark:border-l-stone-600
|
||||
dark:border-r-stone-900
|
||||
dark:border-b-stone-900
|
||||
dark:active:border-t-stone-900
|
||||
dark:active:border-l-stone-900
|
||||
dark:active:border-r-stone-600
|
||||
dark:active:border-b-stone-600
|
||||
"
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
27
packages/frontend/src/styled/Input.tsx
Normal file
27
packages/frontend/src/styled/Input.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { DetailedHTMLProps, InputHTMLAttributes } from "react";
|
||||
|
||||
export namespace Input {
|
||||
export type Props = Omit<DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "className">;
|
||||
}
|
||||
|
||||
export function Input({ children, ...props }: Input.Props) {
|
||||
return (
|
||||
<input
|
||||
{...props}
|
||||
className="
|
||||
w-[32ch]
|
||||
p-2
|
||||
bg-transparent
|
||||
border
|
||||
border-black
|
||||
rounded
|
||||
focus:outline
|
||||
focus:outline-2
|
||||
focus:outline-red-500
|
||||
dark:border-white
|
||||
"
|
||||
>
|
||||
{children}
|
||||
</input>
|
||||
);
|
||||
}
|
||||
@@ -5,6 +5,5 @@
|
||||
{ "path": "packages/backend" },
|
||||
{ "path": "packages/common" },
|
||||
{ "path": "packages/frontend" },
|
||||
{ "path": "packages/make-api" },
|
||||
],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user