cos tam dziala

This commit is contained in:
Patryk Koreń
2026-07-06 23:17:37 +02:00
parent e7ec832f5b
commit ff707dd762
26 changed files with 494 additions and 60 deletions

4
front/.dockerignore Normal file
View File

@@ -0,0 +1,4 @@
node_modules
dist
.git
.DS_Store

1
front/.gitignore vendored
View File

@@ -13,6 +13,7 @@ dist-ssr
*.local
.env
.env.*
# Editor directories and files
.vscode/*

19
front/Dockerfile Normal file
View File

@@ -0,0 +1,19 @@
FROM oven/bun:latest
WORKDIR /app
COPY package.json bun.lockb* ./
RUN bun install
COPY . .
ARG VITE_API_URL
ENV VITE_API_URL=$VITE_API_URL
RUN rm -f .env .env.*
RUN bun run build --mode production
EXPOSE 5173
CMD ["bun", "run", "preview", "--", "--host", "--port", "5173"]

View File

@@ -1,10 +0,0 @@
import { Button } from "@/components/ui/button"
export function App() {
return (
<div className="flex min-h-svh p-6">
</div>
)
}
export default App

View File

@@ -25,13 +25,12 @@ export async function login(username: string, password: string): Promise<LoginRe
}
export async function apiFetch(url: string, options: RequestInit = {}) {
const token = localStorage.getItem("token");
return fetch(`${import.meta.env.VITE_API_URL}${url}`, {
...options,
credentials: "include",
headers: {
...options.headers,
Authorization: token ? `Bearer ${token}` : "",
// Authorization: token ? `Bearer ${token}` : "",
"Content-Type": "application/json"
}
});

View File

@@ -1,4 +1,4 @@
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Outlet } from "react-router";
export default function AuthLayout() {

View File

@@ -1,7 +1,21 @@
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Separator } from "@/components/ui/separator";
import { useAuth } from "@/providers/AuthProvider";
import { Link, Outlet } from "react-router";
import { type ReactNode } from "react";
import { Link, Outlet, useLocation } from "react-router";
function Path({ url, children }: { url: string, children: ReactNode }) {
const location = useLocation();
let isActive = location.pathname === url;
return (
<Button
variant={isActive ? "default" : "secondary"}
asChild
>
<Link to={url}>{children}</Link>
</Button>
)
}
export function MainLayout() {
const { isAuthenticated, user, logout } = useAuth()
@@ -20,7 +34,12 @@ export function MainLayout() {
className="h-6 w-6"
/>
<span className="text-lg font-semibold">papryk</span>
</Link>
<Separator className="m-4" orientation="vertical" />
{isAuthenticated && user &&
<Path url="/video">video</Path>
}
<div className="flex-1 flex justify-center">
{/* Search
@@ -36,19 +55,18 @@ export function MainLayout() {
<div className="flex items-center gap-2 shrink-0">
{isAuthenticated && user &&
<>
<Link to="/video">Video</Link>
<div>
{user.username}
</div>
</>
}
<Button asChild size="sm">
{isAuthenticated ?
<Button onClick={logout}>Logout</Button>
:
{isAuthenticated ?
<Button onClick={logout}>Logout</Button>
:
<Button asChild>
<Link to="/auth/login">Login</Link>
}
</Button>
</Button>
}
</div>
</div>
</header>

View File

@@ -1,6 +1,4 @@
import { Button } from "@/components/ui/button";
import type { ReactNode } from "react";
import { Link } from "react-router";
export function Home() {
return (

View File

@@ -28,7 +28,7 @@ export function LoginForm({
const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [_error, setError] = useState<string | null>(null);
async function handleSubmit(e: FormEvent) {
e.preventDefault();

View File

@@ -1,19 +1,38 @@
import { apiFetch } from "@/api/auth";
import { Button } from "@/components/ui/button";
import { useEffect, useState } from "react";
export function Video() {
const [video, setVideo] = useState<string | null>(null);
const getVideo = async () => {
const res = await apiFetch("/protected/video");
const txt = await res.text()
setVideo(txt)
const refresh = async () => {
await apiFetch(`/protected/refreshfiles`, {
method: "POST",
});
}
useEffect(() => {
getVideo()
}, [])
const create = async () => {
await apiFetch(`/protected/createclip`, {
method: "POST",
});
}
return (
<div className="flex flex-1 flex-col gap-4 py-16">
{video}
<div>
<Button
onClick={() => { refresh() }}
>Refresh</Button>
<Button
onClick={() => { create() }}
>Create Clip</Button>
</div>
<video controls width="600">
<source
src={`${import.meta.env.VITE_API_URL}/protected/video`}
type="video/mp4"
/>
Your browser does not support the video tag.
</video>
</div>
);
}

View File

@@ -26,20 +26,19 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
// restore session
useEffect(() => {
const savedToken = localStorage.getItem("token");
const savedUser = localStorage.getItem("user");
if (savedToken && savedUser) {
setToken(savedToken);
if (savedUser) {
setUser(JSON.parse(savedUser));
}
}, []);
async function login(username: string, password: string) {
const res = await fetch("http://localhost:3000/auth/login", {
const res = await fetch(`${import.meta.env.VITE_API_URL}/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username, password })
body: JSON.stringify({ username, password }),
credentials: "include",
});
if (!res.ok) {
@@ -51,7 +50,6 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
setToken(data.token);
setUser(data.user);
localStorage.setItem("token", data.token);
localStorage.setItem("user", JSON.stringify(data.user));
}
@@ -59,7 +57,6 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
setUser(null);
setToken(null);
localStorage.removeItem("token");
localStorage.removeItem("user");
}

View File

@@ -23,7 +23,7 @@
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
"baseUrl": ".",
// "baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}