commit 71262323b984312932730ce7abbf9341d93b6639 Author: Jannis Heydemann Date: Wed May 6 09:49:51 2026 +0200 using beego framework diff --git a/README.md b/README.md new file mode 100644 index 0000000..0ba4b28 --- /dev/null +++ b/README.md @@ -0,0 +1,209 @@ +# 🗂️ Gitea Projekt-Dashboard + +Ein selbst gehostetes Dashboard zur zentralen Verwaltung aller persönlichen Projekte – technische wie nicht-technische. Projekte können mit einer Gitea-Repository verknüpft sein, müssen es aber nicht. + +--- + +## 🏷️ Funktionsprinzip: Tag-basierte Projektanzeige + +Jedes Repository, das auf dieser Gitea-Instanz mit dem Topic-Tag `dashboard` versehen ist, wird automatisch im Dashboard angezeigt. + +### So funktioniert es: + +1. Du setzt in einem Gitea-Repository unter **Settings → Topics** das Tag `dashboard` +2. Der Backend-Service pollt regelmäßig (oder per Webhook) die Gitea API nach allen Repos mit diesem Tag +3. Die gefundenen Repos werden in der PostgreSQL-Datenbank gespeichert und gecacht +4. Das Frontend zeigt alle getaggten Repos als Projektkarten mit Live-Daten an + +> **Beispiel:** Repo `mein-projekt` bekommt das Topic `dashboard` → erscheint sofort im Dashboard mit Issues, letztem Commit, Status und Beschreibung. + +--- + +## 🧱 Architektur + +``` +┌─────────────────┐ Gitea REST API v1 ┌──────────────────────┐ +│ Gitea Server │ ◄────────────────────────► │ Backend Service │ +│ (Repos + Topics)│ │ (Go, net/http) │ +└─────────────────┘ └──────────┬───────────┘ + │ + ┌──────────▼───────────┐ + │ PostgreSQL DB │ + │ (Repos, Issues, │ + │ Milestones, Cache) │ + └──────────┬───────────┘ + │ + ┌──────────▼───────────┐ + │ Frontend │ + │ (SvelteKit) │ + └──────────────────────┘ +``` + +--- + +## 🖥️ Frontend: **SvelteKit** + +**Warum SvelteKit?** +- Leichtgewichtig und schnell – ideal für ein internes Dashboard +- Server-Side Rendering (SSR) out of the box – kein Flackern beim Laden +- Einfache Reaktivität ohne Overhead +- Perfekt für datengetriebene Dashboards mit Echtzeit-Updates via SSE oder WebSocket + +**Features im Frontend:** +- Projektkarten mit Repo-Name, Beschreibung, letztem Commit, offenen Issues +- Filterfunktion nach Topics, Sprache, Aktivität +- Detailansicht: Issues & Milestones direkt im Dashboard bearbeiten (bi-direktional) +- Live-Updates via Webhook-Events (Server-Sent Events) +- Dark Mode, responsive Design + +--- + +## ⚙️ Backend: **Go (net/http + pgx)** + +Go eignet sich hervorragend als Backend-Sprache – die Standardbibliothek ist so vollständig, dass kein Web-Framework nötig ist. `net/http` liefert alles was gebraucht wird: Routing, Handler, Middleware. Das Ergebnis ist eine dependency-arme, gut lesbare Codebasis. + +**Warum kein Framework?** +- `net/http` aus der Standardbibliothek reicht für ~6 Endpoints vollständig aus +- Kein Framework-Overhead, keine Breaking Changes durch externe Dependencies +- Go-typischer Ansatz: explizit, simpel, lesbar +- Kompiliert zu einer einzigen statischen Binary – minimaler Docker-Footprint + +**Externe Dependencies (minimal):** +- `pgx` – PostgreSQL-Treiber (direktes SQL, kein ORM) +- `godotenv` – `.env`-Datei laden +- `golang.org/x/oauth2` – OAuth2-Flow für Gitea-Login + +**Backend-Aufgaben:** +- `GET /api/projects` – alle getaggten Repos aus der DB zurückgeben +- `POST /api/webhook` – Gitea Webhook-Listener für Push, Issue, Tag-Events +- `GET /api/projects/{id}/issues` – Issues eines Repos live aus Gitea holen +- Hintergrund-Goroutine: alle 5 Minuten Gitea API nach Repos mit Tag `dashboard` abfragen +- Repo-Daten in PostgreSQL cachen (inkl. Topics, letzter Aktivität, Issue-Count) + +**Beispiel – HTTP-Server ohne Framework:** +```go +mux := http.NewServeMux() +mux.HandleFunc("GET /api/projects", h.listProjects) +mux.HandleFunc("POST /api/webhook", h.handleWebhook) +mux.HandleFunc("GET /api/projects/{id}/issues", h.listIssues) + +log.Fatal(http.ListenAndServe(":8080", mux)) +``` + +--- + +## 🔐 Auth: **Gitea OAuth2** + +Gitea kann selbst als OAuth2-Provider fungieren – Nutzer loggen sich mit ihrem Gitea-Account im Dashboard ein, genau wie "Login with GitHub". + +### Setup in Gitea: + +1. In Gitea unter **Settings → Applications → OAuth2 Applications** eine neue App registrieren +2. `Client ID` und `Client Secret` in die `.env` eintragen +3. Redirect URI auf `https://dashboard.example.com/auth/callback` setzen + +### Flow: + +``` +Nutzer klickt "Login mit Gitea" + → Weiterleitung zur Gitea-Instanz (Authorization Endpoint) + → Nutzer bestätigt Zugriff + → Gitea leitet mit Authorization Code zurück + → Backend tauscht Code gegen Access Token + → Nutzer ist eingeloggt, Gitea-Identität bekannt +``` + +**Vorteile:** +- Kein eigenes Auth-System nötig – Gitea übernimmt Passwörter und Sessions +- Nutzeridentität direkt bekannt → Repos und Issues können nutzerbasiert gefiltert werden +- Schreibrechte (Issues erstellen/schließen) nur für den jeweiligen Repo-Owner +- Implementiert mit `golang.org/x/oauth2` – offizielles Go-Paket, keine Drittanbieter-Lib nötig + +--- + +## 🗄️ Datenbank: **PostgreSQL** + +**Schema-Übersicht:** + +```sql +-- Gecachte Repo-Informationen +CREATE TABLE projects ( + id SERIAL PRIMARY KEY, + gitea_id INTEGER UNIQUE NOT NULL, + name VARCHAR(255) NOT NULL, + full_name VARCHAR(255) NOT NULL, + description TEXT, + html_url TEXT, + topics TEXT[], -- z.B. ["dashboard", "freelancer"] + language VARCHAR(100), + open_issues INTEGER DEFAULT 0, + last_push TIMESTAMPTZ, + is_private BOOLEAN DEFAULT false, + synced_at TIMESTAMPTZ DEFAULT NOW() +); + +-- Gemanagte Issues / Aufgaben +CREATE TABLE issues ( + id SERIAL PRIMARY KEY, + gitea_id INTEGER NOT NULL, + project_id INTEGER REFERENCES projects(id), + title TEXT NOT NULL, + state VARCHAR(20), -- open / closed + assignee VARCHAR(100), + milestone TEXT, + updated_at TIMESTAMPTZ +); + +-- Webhook-Event-Log +CREATE TABLE webhook_events ( + id SERIAL PRIMARY KEY, + event_type VARCHAR(50), + payload JSONB, + received_at TIMESTAMPTZ DEFAULT NOW() +); +``` + +--- + +## 🚀 Roadmap + +- [ ] **v0.1** – Repo-Listing via Tag `dashboard`, Polling alle 5 min +- [ ] **v0.2** – Webhook-Listener für Echtzeit-Updates +- [ ] **v0.3** – Issues & Milestones im Dashboard anzeigen +- [ ] **v0.4** – Issues direkt aus dem Dashboard erstellen/schließen (bi-direktional) +- [ ] **v0.5** – Gitea OAuth2 Login +- [ ] **v0.6** – Verknüpfung mit Freelancer-Dashboard (Repos = Projekte) +- [ ] **v1.0** – Multi-User, öffentliche Projektsseiten + +--- + +## 🔧 Tech Stack + +| Schicht | Technologie | +|--------------|--------------------------------| +| Frontend | SvelteKit + TailwindCSS | +| Backend | Go + net/http (Standardlib) | +| Datenbank | PostgreSQL + pgx | +| Auth | Gitea OAuth2 + golang.org/x/oauth2 | +| API | Gitea REST API v1 | +| Deployment | Docker Compose | + +--- + +## 📦 Getting Started + +```bash +# Repo klonen +git clone https://gitea.starfour.de/Jannis/gitea-projekt-dashboard + +# Umgebungsvariablen setzen +cp .env.example .env +# GITEA_URL, GITEA_TOKEN, GITEA_CLIENT_ID, GITEA_CLIENT_SECRET, DATABASE_URL, DASHBOARD_TAG eintragen + +# Mit Docker starten +docker compose up -d +``` + +--- + +*Dieses Projekt ist Teil der persönlichen Projekt-Ideen-Sammlung. Zugehöriges Übersichts-Repo: [projekt-ideen](https://gitea.starfour.de/Jannis/projekt-ideen)* \ No newline at end of file diff --git a/conf/app.conf b/conf/app.conf new file mode 100644 index 0000000..173ecb0 --- /dev/null +++ b/conf/app.conf @@ -0,0 +1,3 @@ +appname = gitea-projekt-dashboard +httpport = 8080 +runmode = dev diff --git a/controllers/default.go b/controllers/default.go new file mode 100644 index 0000000..fc1e72b --- /dev/null +++ b/controllers/default.go @@ -0,0 +1,15 @@ +package controllers + +import ( + beego "github.com/beego/beego/v2/server/web" +) + +type MainController struct { + beego.Controller +} + +func (c *MainController) Get() { + c.Data["Website"] = "beego.vip" + c.Data["Email"] = "astaxie@gmail.com" + c.TplName = "index.tpl" +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..73b34b6 --- /dev/null +++ b/go.mod @@ -0,0 +1,6 @@ +module gitea-projekt-dashboard + +go 1.26 + +require github.com/beego/beego/v2 v2.1.0 +require github.com/smartystreets/goconvey v1.6.4 diff --git a/main.go b/main.go new file mode 100644 index 0000000..058620b --- /dev/null +++ b/main.go @@ -0,0 +1,11 @@ +package main + +import ( + _ "gitea-projekt-dashboard/routers" + beego "github.com/beego/beego/v2/server/web" +) + +func main() { + beego.Run() +} + diff --git a/routers/router.go b/routers/router.go new file mode 100644 index 0000000..b283297 --- /dev/null +++ b/routers/router.go @@ -0,0 +1,10 @@ +package routers + +import ( + "gitea-projekt-dashboard/controllers" + beego "github.com/beego/beego/v2/server/web" +) + +func init() { + beego.Router("/", &controllers.MainController{}) +} diff --git a/static/js/reload.min.js b/static/js/reload.min.js new file mode 100644 index 0000000..e780033 --- /dev/null +++ b/static/js/reload.min.js @@ -0,0 +1 @@ +function b(a){var c=new WebSocket(a);c.onclose=function(){setTimeout(function(){b(a)},2E3)};c.onmessage=function(){location.reload()}}try{if(window.WebSocket)try{b("ws://localhost:12450/reload")}catch(a){console.error(a)}else console.log("Your browser does not support WebSockets.")}catch(a){console.error("Exception during connecting to Reload:",a)}; diff --git a/tests/default_test.go b/tests/default_test.go new file mode 100644 index 0000000..8363d66 --- /dev/null +++ b/tests/default_test.go @@ -0,0 +1,42 @@ +package test + +import ( + "net/http" + "net/http/httptest" + "testing" + "runtime" + "path/filepath" + + "github.com/beego/beego/v2/core/logs" + + _ "gitea-projekt-dashboard/routers" + + beego "github.com/beego/beego/v2/server/web" + . "github.com/smartystreets/goconvey/convey" +) + +func init() { + _, file, _, _ := runtime.Caller(0) + apppath, _ := filepath.Abs(filepath.Dir(filepath.Join(file, ".." + string(filepath.Separator)))) + beego.TestBeegoInit(apppath) +} + + +// TestBeego is a sample to run an endpoint test +func TestBeego(t *testing.T) { + r, _ := http.NewRequest("GET", "/", nil) + w := httptest.NewRecorder() + beego.BeeApp.Handlers.ServeHTTP(w, r) + + logs.Trace("testing", "TestBeego", "Code[%d]\n%s", w.Code, w.Body.String()) + + Convey("Subject: Test Station Endpoint\n", t, func() { + Convey("Status Code Should Be 200", func() { + So(w.Code, ShouldEqual, 200) + }) + Convey("The Result Should Not Be Empty", func() { + So(w.Body.Len(), ShouldBeGreaterThan, 0) + }) + }) +} + diff --git a/views/index.tpl b/views/index.tpl new file mode 100644 index 0000000..8d6fbec --- /dev/null +++ b/views/index.tpl @@ -0,0 +1,95 @@ + + + + + Beego + + + + + + + +
+

Welcome to Beego

+
+ Beego is a simple & powerful Go web framework which is inspired by tornado and sinatra. +
+
+ +
+ + + +