chore(frontend): before ux polish
This commit is contained in:
1 parent
5b66e57d2e
commit
09599ff7e9
9 files changed
+375
-8
No files matched your search
@@ -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.
|
||||
@@ -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
@@ -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}`);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
turbopack: {
|
||||
root: __dirname,
|
||||
},
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
@@ -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"
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
Generated
+192
-6
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
Binary file not shown.
Reference in new issue
Block a user