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 { 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 {
|
export function generateSessionId(byteLength: number = 32): SessionId {
|
||||||
const array = new Uint8Array(byteLength);
|
const array = new Uint8Array(byteLength);
|
||||||
@@ -112,6 +112,13 @@ export async function initDatabase(filename: string = "db.sqlite3"): Promise<Kys
|
|||||||
.column("timestamp")
|
.column("timestamp")
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
|
await db.schema
|
||||||
|
.createTable("File")
|
||||||
|
.ifNotExists()
|
||||||
|
.addColumn("sha256", "blob", (c) => c.notNull().primaryKey())
|
||||||
|
.addColumn("data", "blob", (c) => c.notNull())
|
||||||
|
.execute();
|
||||||
|
|
||||||
await db.schema
|
await db.schema
|
||||||
.createTable("User")
|
.createTable("User")
|
||||||
.ifNotExists()
|
.ifNotExists()
|
||||||
@@ -152,11 +159,28 @@ export async function initDatabase(filename: string = "db.sqlite3"): Promise<Kys
|
|||||||
.ifNotExists()
|
.ifNotExists()
|
||||||
.addColumn("attachmentId", "text", (c) => c.notNull().primaryKey())
|
.addColumn("attachmentId", "text", (c) => c.notNull().primaryKey())
|
||||||
.addColumn("pieceId", "text", (c) => c.notNull().references("Piece.pieceId").onDelete("cascade").onUpdate("cascade"))
|
.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("filename", "text", (c) => c.notNull())
|
||||||
.addColumn("mediaType", "text", (c) => c.notNull())
|
.addColumn("mediaType", "text", (c) => c.notNull())
|
||||||
.$call(systemInformation)
|
.$call(systemInformation)
|
||||||
.execute();
|
.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;
|
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 { Home } from "./routes/Home";
|
||||||
import { Login } from "./routes/Login";
|
import { Login } from "./routes/Login";
|
||||||
import { createRoot } from "react-dom/client";
|
|
||||||
import { StrictMode } from "react";
|
|
||||||
import "./style.css";
|
import "./style.css";
|
||||||
import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { treaty } from "@elysiajs/eden";
|
import { treaty } from "@elysiajs/eden";
|
||||||
import type { App } from "backend/app";
|
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 { useNavigate } from "react-router-dom";
|
||||||
import { client } from "../client";
|
import { client } from "../client";
|
||||||
import { useStore } from "../store";
|
import { useStore } from "../store";
|
||||||
|
import { Button } from "../styled/Button";
|
||||||
|
|
||||||
export const Home = () => {
|
export function Home() {
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
@@ -28,7 +29,7 @@ export const Home = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onLogoutClick = async () => {
|
const onLogoutClick = async () => {
|
||||||
const { data, error } = await client.logout.post();
|
const { error } = await client.logout.post();
|
||||||
|
|
||||||
if (error !== null) {
|
if (error !== null) {
|
||||||
console.error("Response was not ok");
|
console.error("Response was not ok");
|
||||||
@@ -53,15 +54,11 @@ export const Home = () => {
|
|||||||
Użytkownik: {user.username}
|
Użytkownik: {user.username}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button
|
<Button type="button" onClick={onLogoutClick}>
|
||||||
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}
|
|
||||||
>
|
|
||||||
Wyloguj się
|
Wyloguj się
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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 { useNavigate } from "react-router-dom";
|
||||||
import { client } from "../client";
|
import { client } from "../client";
|
||||||
import { useStore } from "../store";
|
import { useStore } from "../store";
|
||||||
|
import { Button } from "../styled/Button";
|
||||||
|
import { Input } from "../styled/Input";
|
||||||
|
|
||||||
export const Login = () => {
|
export function Login() {
|
||||||
|
|
||||||
const navigate = useNavigate();
|
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}>
|
<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>
|
<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>
|
<label htmlFor={usernameId}>Nazwa użytkownika</label>
|
||||||
<input
|
<Input
|
||||||
id={usernameId}
|
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"
|
type="text"
|
||||||
value={loginUsername}
|
value={loginUsername}
|
||||||
autoFocus
|
autoFocus
|
||||||
@@ -53,21 +54,17 @@ export const Login = () => {
|
|||||||
onInput={(e) => setLoginUsername(e.currentTarget.value)}
|
onInput={(e) => setLoginUsername(e.currentTarget.value)}
|
||||||
/>
|
/>
|
||||||
<label htmlFor={passwordId}>Hasło</label>
|
<label htmlFor={passwordId}>Hasło</label>
|
||||||
<input
|
<Input
|
||||||
id={passwordId}
|
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"
|
type="password"
|
||||||
value={loginPassword}
|
value={loginPassword}
|
||||||
required
|
required
|
||||||
onInput={(e) => setLoginPassword(e.currentTarget.value)}
|
onInput={(e) => setLoginPassword(e.currentTarget.value)}
|
||||||
/>
|
/>
|
||||||
<button
|
<Button type="submit">
|
||||||
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"
|
|
||||||
>
|
|
||||||
Zaloguj się
|
Zaloguj się
|
||||||
</button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</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/backend" },
|
||||||
{ "path": "packages/common" },
|
{ "path": "packages/common" },
|
||||||
{ "path": "packages/frontend" },
|
{ "path": "packages/frontend" },
|
||||||
{ "path": "packages/make-api" },
|
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user