chore(frontend): before ux polish

This commit is contained in:
admin committed 2025-12-27 12:35:58 +01:00
1 parent 5b66e57d2e
commit 09599ff7e9
9 files changed
+375 -8

No files matched your search

+129
View File
@@ -0,0 +1,129 @@
# 🧊 Backend Freeze – Frontend Ready
**Projekt:** OnlyPets
**Stand:** Backend eingefroren (Frontend-ready)
**Datum:** 27.12.2025
Ziel dieses Dokuments ist es, klar festzuhalten:
> Das Backend ist technisch und strukturell abgeschlossen,
> sodass die weitere Entwicklung primär im Frontend stattfinden kann.
Spezial-Features werden bewusst **getrennt** und **nachgelagert** umgesetzt.
---
## 🧱 1. Architektur & Infrastruktur
- NestJS stabil gebootstrapped
- Module sauber getrennt:
- Posts
- Feed
- Prisma
- Auth
- Dependency Injection stabil (keine offenen DI-Fehler)
- Prisma korrekt als Service eingebunden
- Strict TypeScript aktiv (keine `any`-Leaks)
- Ordnerstruktur final (kein späteres Refactoring notwendig)
**Status:** ✅ abgeschlossen
---
## 🔐 2. Auth-Grundlage (Phase 1)
- Globaler `DummyAuthGuard` aktiv
- `req.user` immer definiert (`null | FakeUser`)
- Rollenmodell vorbereitet (`USER | MOD | ADMIN`)
- `@Public()` Decorator vorhanden
- Guard-first-Architektur (keine Auth-Logik in Services)
> Echte Auth (JWT, Login) ist **bewusst nicht Teil** des Freeze-Zustands.
**Status:** ✅ abgeschlossen (Phase-1-Scope)
---
## 🗃️ 3. Datenmodell & Prisma
- Prisma Schema stabil
- Keine redundanten oder abgeleiteten Felder
- Alter wird **nicht gespeichert**
- `birthDate` ist Single Source of Truth
- Beziehungen konsistent:
- User ↔ Pet ↔ Post
- Seed reproduzierbar & idempotent
- DB-Zugriff ausschließlich über Services
**Status:** ✅ abgeschlossen
---
## 📰 4. Feed & Content (Frontend-relevant)
Aktive Endpoints:
- `GET /posts`
- `GET /feed`
Eigenschaften:
- `/feed` ist 1:1 aus `/posts` gespiegelt
- Deterministischer Feed
- Keine Random- oder Discovery-Logik
- Sortierung: `createdAt DESC`
- Inkludierte Relationen:
- `pet`
- `author`
Smoke-Tests erfolgreich (Invoke-RestMethod).
**Status:** ✅ abgeschlossen / frontend-ready
---
## 🩺 5. Betrieb & Stabilität
- `GET /health` Endpoint vorhanden
- Server startet ohne Errors
- Keine offenen TypeScript- oder DI-Probleme
- Lokales DEV-Setup reproduzierbar
**Status:** ✅ abgeschlossen
---
## 🧩 6. Bewusst ausgeklammerte Spezial-Features
Diese Themen sind **nicht Teil des Backend-Freeze** und werden separat umgesetzt:
- Discovery Feed (siehe Issue #243)
- Likes
- Kommentare
- Notifications
- Admin / Moderation
- Echte Auth (JWT, Login)
> Diese Features sind **entkoppelt geplant**,
> sodass sie nach Live-Frontend **ohne Rewrites** ergänzt werden können.
---
## 🧊 Freeze-Regel
Ab diesem Punkt gilt für das Backend:
- ❄️ Keine neuen Features
- ❄️ Nur Bugfixes
- ❄️ Keine API-Breaking-Changes
- ❄️ Fokus liegt auf Frontend-Entwicklung
---
## ✅ Fazit
Das Backend befindet sich im **frontend-ready Freeze-Zustand**.
- Die API ist stabil
- Die Architektur ist vorbereitet
- Erweiterungen sind planbar, aber nicht blockierend
➡️ **Empfohlener nächster Schritt:** Frontend entwickeln.
+26
View File
@@ -0,0 +1,26 @@
## Lokales Dev-Setup
Frontend:
- URL: http://localhost:3000
Backend:
- URL: http://localhost:3001
ENV:
- NEXT_PUBLIC_API_URL=http://localhost:3001
Hinweis:
Backend ist frontend-ready eingefroren.
Neue Features nur über separate Issues.
# 🛠 DEV SETUP – OnlyPets
## Ports
- Frontend: http://localhost:3000
- Backend: http://localhost:3001
## Frontend (Next.js)
```bash
cd frontend
npm install
npm run dev
+1 -1
View File
@@ -4,7 +4,7 @@ import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const port = process.env.PORT || 3000;
const port = process.env.PORT || 3001;
await app.listen(port);
console.log(`🚀 Backend running on http://localhost:${port}`);
+3 -1
View File
@@ -1,7 +1,9 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
turbopack: {
root: __dirname,
},
};
export default nextConfig;
+1
View File
@@ -4,6 +4,7 @@
"private": true,
"scripts": {
"dev": "next dev",
"dev": "NODE_OPTIONS=--max_old_space_size=4096 next dev",
"build": "next build",
"start": "next start",
"lint": "eslint"
+20
View File
@@ -0,0 +1,20 @@
export function timeAgo(date: string | Date) {
const ts = new Date(date).getTime();
const diffMs = Date.now() - ts;
const minutes = Math.floor(diffMs / 60000);
if (minutes < 1) return "gerade eben";
if (minutes < 60) return `vor ${minutes} Minute${minutes === 1 ? "" : "n"}`;
const hours = Math.floor(minutes / 60);
if (hours < 24) return `vor ${hours} Stunde${hours === 1 ? "" : "n"}`;
const days = Math.floor(hours / 24);
if (days < 7) return `vor ${days} Tag${days === 1 ? "" : "en"}`;
const weeks = Math.floor(days / 7);
if (weeks < 4) return `vor ${weeks} Woche${weeks === 1 ? "" : "n"}`;
// Fallback: Datum kurz
return new Date(date).toLocaleDateString("de-DE");
}
+192 -6
View File
@@ -13,8 +13,11 @@
"react-dom": "^18.0.0"
},
"devDependencies": {
"autoprefixer": "^10.4.23",
"eslint": "^8.40.0",
"next-auth": "^4.20.0",
"postcss": "^8.5.6",
"tailwindcss": "^4.1.18",
"ts-node": "^10.9.2",
"typescript": "^5.9.3"
}
@@ -977,6 +980,43 @@
"dev": true,
"license": "Python-2.0"
},
"node_modules/autoprefixer": {
"version": "10.4.23",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz",
"integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"browserslist": "^4.28.1",
"caniuse-lite": "^1.0.30001760",
"fraction.js": "^5.3.4",
"picocolors": "^1.1.1",
"postcss-value-parser": "^4.2.0"
},
"bin": {
"autoprefixer": "bin/autoprefixer"
},
"engines": {
"node": "^10 || ^12 || >=14"
},
"peerDependencies": {
"postcss": "^8.1.0"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -1004,6 +1044,40 @@
"concat-map": "0.0.1"
}
},
"node_modules/browserslist": {
"version": "4.28.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
"integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/browserslist"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
"electron-to-chromium": "^1.5.263",
"node-releases": "^2.0.27",
"update-browserslist-db": "^1.2.0"
},
"bin": {
"browserslist": "cli.js"
},
"engines": {
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -1174,6 +1248,23 @@
"node": ">=6.0.0"
}
},
"node_modules/electron-to-chromium": {
"version": "1.5.267",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
"integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==",
"dev": true,
"license": "ISC"
},
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -1421,6 +1512,20 @@
"dev": true,
"license": "ISC"
},
"node_modules/fraction.js": {
"version": "5.3.4",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
"integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "*"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/rawify"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -1852,6 +1957,41 @@
}
}
},
"node_modules/next/node_modules/postcss": {
"version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.6",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/node-releases": {
"version": "2.0.27",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
"integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
"dev": true,
"license": "MIT"
},
"node_modules/oauth": {
"version": "0.9.15",
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
@@ -2005,9 +2145,10 @@
"license": "ISC"
},
"node_modules/postcss": {
"version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"dev": true,
"funding": [
{
"type": "opencollective",
@@ -2024,14 +2165,21 @@
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.6",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/postcss-value-parser": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"dev": true,
"license": "MIT"
},
"node_modules/preact": {
"version": "10.28.1",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.28.1.tgz",
@@ -2352,6 +2500,13 @@
"node": ">=8"
}
},
"node_modules/tailwindcss": {
"version": "4.1.18",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
"integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==",
"dev": true,
"license": "MIT"
},
"node_modules/text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
@@ -2457,6 +2612,37 @@
"license": "MIT",
"peer": true
},
"node_modules/update-browserslist-db": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
"integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/browserslist"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"escalade": "^3.2.0",
"picocolors": "^1.1.1"
},
"bin": {
"update-browserslist-db": "cli.js"
},
"peerDependencies": {
"browserslist": ">= 4.21.0"
}
},
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+3
View File
@@ -14,8 +14,11 @@
"react-dom": "^18.0.0"
},
"devDependencies": {
"autoprefixer": "^10.4.23",
"eslint": "^8.40.0",
"next-auth": "^4.20.0",
"postcss": "^8.5.6",
"tailwindcss": "^4.1.18",
"ts-node": "^10.9.2",
"typescript": "^5.9.3"
}
BIN
View File
Binary file not shown.