diff --git a/backend/app/auth.py b/backend/app/auth.py index 15905e0..4fbe17b 100644 --- a/backend/app/auth.py +++ b/backend/app/auth.py @@ -13,7 +13,7 @@ from .config import settings SECRET_KEY = settings.secret_key ALGORITHM = "HS256" -ACCESS_TOKEN_EXPIRE_MINUTES = settings.access_token_expire_minuted +ACCESS_TOKEN_EXPIRE_MINUTES = 60 pwd_context = CryptContext(schemes=["bcrypt_sha256"], deprecated="auto") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token") diff --git a/backend/app/config.py b/backend/app/config.py index e530c49..17bf487 100644 --- a/backend/app/config.py +++ b/backend/app/config.py @@ -14,8 +14,6 @@ class Settings(BaseModel): secret_key: str = os.getenv("SECRET_KEY", "CHANGE_ME") - access_token_expire_minuted: int = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "60")) - @property def database_url(self): return ( diff --git a/backend/app/main.py b/backend/app/main.py index d5b64ec..4159e8d 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -10,24 +10,12 @@ from .auth import ( create_access_token, get_password_hash, ) from fastapi.security import OAuth2PasswordRequestForm -from fastapi.middleware.cors import CORSMiddleware app = FastAPI(title="Server Worklog") -app.add_middleware( - CORSMiddleware, - allow_origins=["*"], # oder deine echte Domain - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], -) # Nur für lokale Entwicklung, in Produktion via Alembic Base.metadata.create_all(bind=engine) -@app.options("/{path:path}") -def preflight_handler(path: str): - return {} - @app.post("/auth/register", response_model=schemas.UserRead) def register_user(user_in: schemas.UserCreate, db: Session = Depends(get_db)): existing = db.query(models.User).filter(models.User.username == user_in.username).first() @@ -98,25 +86,6 @@ def create_server( db.refresh(server) return server -@app.put("/servers/{server_id}", response_model=schemas.ServerRead) -def update_server( - server_id: int, - server: schemas.ServerUpdate, - db: Session = Depends(get_db), - user=Depends(get_current_user), -): - db_server = db.query(models.Server).filter(models.Server.id == server_id).first() - if not db_server: - raise HTTPException(status_code=404, detail="Server not found") - - db_server.hostname = server.hostname - db_server.description = server.description - db_server.owner_id = server.owner_id - - db.commit() - db.refresh(db_server) - return db_server - @app.get("/servers", response_model=List[schemas.ServerRead]) def list_servers( db: Session = Depends(get_db), diff --git a/backend/app/schemas.py b/backend/app/schemas.py index 2d88580..ec80298 100644 --- a/backend/app/schemas.py +++ b/backend/app/schemas.py @@ -21,9 +21,6 @@ class ServerBase(BaseModel): class ServerCreate(ServerBase): pass -class ServerUpdate(ServerBase): - pass - class ServerRead(ServerBase): id: int owner: OwnerRead | None = None diff --git a/frontend/app/app.vue b/frontend/app/app.vue index 54b6347..ea4ef5c 100644 --- a/frontend/app/app.vue +++ b/frontend/app/app.vue @@ -1,18 +1,3 @@ - - - - - diff --git a/frontend/app/composables/useAPI.ts b/frontend/app/composables/useAPI.ts deleted file mode 100644 index cb93681..0000000 --- a/frontend/app/composables/useAPI.ts +++ /dev/null @@ -1,59 +0,0 @@ -export function useApi() { - const config = useRuntimeConfig() - const { token, logout } = useAuth() - - async function apiFetch(url: string, options: any = {}) { - try { - return await $fetch(url, { - baseURL: config.public.apiBase, - headers: { - ...(options.headers || {}), - ...(token.value ? { Authorization: `Bearer ${token.value}` } : {}) - }, - ...options - }) - } catch (error: any) { - // Netzwerkfehler oder Server nicht erreichbar - if (!error.response) { - console.error("API unreachable:", error) - throw new Error("Der Server ist nicht erreichbar.") - } - - const status = error.response.status - const data = error.response._data - - // 401 → Token ungültig oder abgelaufen - if (status === 401) { - console.warn("API returned 401 — logging out") - logout() - return { unauthorized: true } - } - - // 403 → keine Berechtigung - if (status === 403) { - throw new Error("Du hast keine Berechtigung für diese Aktion.") - } - - // 400 → Validierungsfehler - if (status === 400) { - throw new Error(data?.detail || "Ungültige Eingabe.") - } - - // 404 → nicht gefunden - if (status === 404) { - throw new Error("Die angeforderte Ressource wurde nicht gefunden.") - } - - // 500 → Serverfehler - if (status >= 500) { - console.error("Server error:", data) - throw new Error("Interner Serverfehler. Bitte später erneut versuchen.") - } - - // Fallback - throw new Error(data?.detail || "Unbekannter Fehler.") - } - } - - return { apiFetch } -} diff --git a/frontend/app/composables/useAuth.ts b/frontend/app/composables/useAuth.ts index 15e3cd2..3acb2c5 100644 --- a/frontend/app/composables/useAuth.ts +++ b/frontend/app/composables/useAuth.ts @@ -1,46 +1,15 @@ -function decodeJwt(token: string) { - try { - const payload = token.split(".")[1] - const decoded = JSON.parse(atob(payload)) - return decoded - } catch { - return null - } -} export function useAuth() { - const token = useState("token", () => null) + const token = useState("token", () => null) function setToken(t: string) { token.value = t - - if (typeof window !== "undefined") { - localStorage.setItem("token", t) - } + localStorage.setItem("token", t) } function loadToken() { - if (typeof window !== "undefined") { - const stored = localStorage.getItem("token") - if (!stored) return - - const payload = decodeJwt(stored) - if( !payload || !payload.exp || payload.exp * 1000 < Date.now()) { - console.info("Token expired - removing it from storage") - localStorage.removeItem("token") - token.value = null - } - token.value = stored - - } + token.value = localStorage.getItem("token") } - function logout() { - token.value = null - - if (typeof window !== "undefined") { - localStorage.removeItem("token") - } - } - - return { token, setToken, loadToken, logout } + return { token, setToken, loadToken } } + diff --git a/frontend/app/composables/useOwners.ts b/frontend/app/composables/useOwners.ts deleted file mode 100644 index 6af05a0..0000000 --- a/frontend/app/composables/useOwners.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { ref } from "vue" -import { isUnauthorized } from "~/utils/auth" - -export function useOwners() { - const owners = ref([]) - const loading = ref(false) - const error = ref("") - - const { apiFetch } = useApi() - - async function loadOwners() { - loading.value = true - error.value = "" - - const result = await apiFetch("/owners") - - if (isUnauthorized(result)) { - loading.value = false - return result - } - - owners.value = result - loading.value = false - return result - } - - return { - owners, - loading, - error, - loadOwners - } -} diff --git a/frontend/app/composables/useServers.ts b/frontend/app/composables/useServers.ts deleted file mode 100644 index 63408e9..0000000 --- a/frontend/app/composables/useServers.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { ref } from "vue" -import { isUnauthorized } from "~/utils/auth" - -export function useServers() { - const servers = ref([]) - const loading = ref(false) - const error = ref("") - - const { apiFetch } = useApi() - - async function loadServers() { - loading.value = true - error.value = "" - - const result = await apiFetch("/servers") - - if (isUnauthorized(result)) { - loading.value = false - return result - } - - servers.value = result - loading.value = false - return result - } - - return { - servers, - loading, - error, - loadServers - } -} diff --git a/frontend/app/layouts/default.vue b/frontend/app/layouts/default.vue index 54ae58a..1a115bb 100644 --- a/frontend/app/layouts/default.vue +++ b/frontend/app/layouts/default.vue @@ -1,48 +1,12 @@ - - - - - + + + Dashboard Owners Servers - - - - Work Entries - - - - - Neu erstellen - - - - Suchen - - - + Work Entries - - - - - Logout - - @@ -50,3 +14,4 @@ function doLogout() { + diff --git a/frontend/app/middleware/auth.global.ts b/frontend/app/middleware/auth.global.ts deleted file mode 100644 index 2fcc50d..0000000 --- a/frontend/app/middleware/auth.global.ts +++ /dev/null @@ -1,15 +0,0 @@ -export default defineNuxtRouteMiddleware((to, from) => { - if (import.meta.server) return // SSR ignorieren - // allow login-page - - if (to.meta.requiresAuth !== true) { - return - } - - const { token, loadToken } = useAuth() - loadToken() - - if (!token.value && to.path !== "/login") { - return navigateTo("/login") - } -}) diff --git a/frontend/app/pages/login.vue b/frontend/app/pages/login.vue deleted file mode 100644 index de69707..0000000 --- a/frontend/app/pages/login.vue +++ /dev/null @@ -1,64 +0,0 @@ - - - - - Login - - - - - - - - Login - - - {{ error }} - - - diff --git a/frontend/app/pages/owners.vue b/frontend/app/pages/owners.vue new file mode 100644 index 0000000..1a6bf69 --- /dev/null +++ b/frontend/app/pages/owners.vue @@ -0,0 +1,19 @@ + + + + + Owners + + + + {{ o.name }} + + + + + diff --git a/frontend/app/pages/owners/index.vue b/frontend/app/pages/owners/index.vue deleted file mode 100644 index d03b97c..0000000 --- a/frontend/app/pages/owners/index.vue +++ /dev/null @@ -1,96 +0,0 @@ - - - - - Owner Verwaltung - - - Neuen Owner anlegen - - - - - - Speichern - - - - - - Owner Liste - - Lade Owner… - {{ error }} - - - - - ID - Name - - - - - {{ owner.id }} - {{ owner.name }} - - - - - - diff --git a/frontend/app/pages/servers/index.vue b/frontend/app/pages/servers/index.vue deleted file mode 100644 index f4c51be..0000000 --- a/frontend/app/pages/servers/index.vue +++ /dev/null @@ -1,234 +0,0 @@ - - - - - Server Verwaltung - - - - Server Liste - - Lade Server… - {{ error }} - - - - - ID - Name - Beschreibung - Eigentümer - - - - - {{ server.id }} - - - - - - {{ server.hostname }} - - - - - - - - - {{ server.description }} - - - - - - - Kein Owner - - {{ owner.name }} - - - - - {{ server.owner ? server.owner.name : "—" }} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Neuen Server anlegen - - - - - - - - Kein Owner - - {{ owner.name }} - - - - - - Speichern - - - - - - diff --git a/frontend/app/pages/workentries/create.vue b/frontend/app/pages/workentries/create.vue deleted file mode 100644 index c8e990d..0000000 --- a/frontend/app/pages/workentries/create.vue +++ /dev/null @@ -1,149 +0,0 @@ - - - - - Neuen Worklog-Eintrag erstellen - - {{ error }} - - - - Eintrag erstellt - - Titel: {{ createdEntry.title }} - Beschreibung: {{ createdEntry.description || "—" }} - Dauer: {{ createdEntry.duration_minutes || "—" }} - - - Server: - - {{ s.hostname }} - - - - - Weiteren Eintrag erstellen - - - - - - - - - - - - - Server auswählen - - - - {{ server.hostname }} - - - - - - Speichern - - - - diff --git a/frontend/app/pages/workentries/search.vue b/frontend/app/pages/workentries/search.vue deleted file mode 100644 index 059742b..0000000 --- a/frontend/app/pages/workentries/search.vue +++ /dev/null @@ -1,147 +0,0 @@ - - - - - Worklog-Suche - - - - Server auswählen… - - {{ s.hostname }} - - - - - - - Suchen - - - - - - Suchergebnisse - - Lade… - {{ error }} - - - - - Titel - Dauer - Erstellt am - Server - - - - - - {{ entry.title }} - {{ entry.duration_minutes ?? "—" }} - {{ new Date(entry.created_at).toLocaleString() }} - - - {{ s.hostname }} - - - - - - - Beschreibung - - {{ entry.description || "Keine Beschreibung vorhanden." }} - - - - - - - - - - Keine Einträge gefunden. - - - - diff --git a/frontend/app/utils/auth.ts b/frontend/app/utils/auth.ts deleted file mode 100644 index a73d282..0000000 --- a/frontend/app/utils/auth.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function isUnauthorized(result: any) { - return result && result.unauthorized === true -} diff --git a/frontend/nuxt.config.ts b/frontend/nuxt.config.ts index c22ab71..0ebc993 100644 --- a/frontend/nuxt.config.ts +++ b/frontend/nuxt.config.ts @@ -1,9 +1,6 @@ // https://nuxt.com/docs/api/configuration/nuxt-config export default defineNuxtConfig({ - modules: [ - "@nuxtjs/tailwindcss", - "@nuxt/icon" - ], + modules: [ "@nuxtjs/tailwindcss" ], srr: true, nitro: { preset: "static" diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d31a986..3a30548 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -7,7 +7,6 @@ "name": "frontend", "hasInstallScript": true, "dependencies": { - "@nuxt/icon": "^2.2.1", "nuxt": "^4.3.0", "vue": "^3.5.27", "vue-router": "^4.6.4" @@ -29,19 +28,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@antfu/install-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", - "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", - "license": "MIT", - "dependencies": { - "package-manager-detector": "^1.3.0", - "tinyexec": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, "node_modules/@babel/code-frame": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", @@ -1006,47 +992,6 @@ "node": ">=18" } }, - "node_modules/@iconify/collections": { - "version": "1.0.642", - "resolved": "https://registry.npmjs.org/@iconify/collections/-/collections-1.0.642.tgz", - "integrity": "sha512-SjQpWEQDG8votqjmF/G0CCmFFYaQfm+GECNjAEhzyIGB/RpzOUWcGCIBnFQA/YCg3I+wC5sZpWF/Mko6KWeMwg==", - "license": "MIT", - "dependencies": { - "@iconify/types": "*" - } - }, - "node_modules/@iconify/types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", - "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", - "license": "MIT" - }, - "node_modules/@iconify/utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.0.tgz", - "integrity": "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==", - "license": "MIT", - "dependencies": { - "@antfu/install-pkg": "^1.1.0", - "@iconify/types": "^2.0.0", - "mlly": "^1.8.0" - } - }, - "node_modules/@iconify/vue": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@iconify/vue/-/vue-5.0.0.tgz", - "integrity": "sha512-C+KuEWIF5nSBrobFJhT//JS87OZ++QDORB6f2q2Wm6fl2mueSTpFBeBsveK0KW9hWiZ4mNiPjsh6Zs4jjdROSg==", - "license": "MIT", - "dependencies": { - "@iconify/types": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/cyberalien" - }, - "peerDependencies": { - "vue": ">=3" - } - }, "node_modules/@ioredis/commands": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.5.0.tgz", @@ -1476,28 +1421,6 @@ "devtools-wizard": "cli.mjs" } }, - "node_modules/@nuxt/icon": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@nuxt/icon/-/icon-2.2.1.tgz", - "integrity": "sha512-GI840yYGuvHI0BGDQ63d6rAxGzG96jQcWrnaWIQKlyQo/7sx9PjXkSHckXUXyX1MCr9zY6U25Td6OatfY6Hklw==", - "license": "MIT", - "dependencies": { - "@iconify/collections": "^1.0.641", - "@iconify/types": "^2.0.0", - "@iconify/utils": "^3.1.0", - "@iconify/vue": "^5.0.0", - "@nuxt/devtools-kit": "^3.1.1", - "@nuxt/kit": "^4.2.2", - "consola": "^3.4.2", - "local-pkg": "^1.1.2", - "mlly": "^1.8.0", - "ohash": "^2.0.11", - "pathe": "^2.0.3", - "picomatch": "^4.0.3", - "std-env": "^3.10.0", - "tinyglobby": "^0.2.15" - } - }, "node_modules/@nuxt/kit": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-4.3.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index def62e7..e29a932 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,7 +10,6 @@ "postinstall": "nuxt prepare" }, "dependencies": { - "@nuxt/icon": "^2.2.1", "nuxt": "^4.3.0", "vue": "^3.5.27", "vue-router": "^4.6.4"
{{ error }}
Titel: {{ createdEntry.title }}
Beschreibung: {{ createdEntry.description || "—" }}
Dauer: {{ createdEntry.duration_minutes || "—" }}
- Server: - - {{ s.hostname }} - -
- {{ entry.description || "Keine Beschreibung vorhanden." }} -