Skip to content

Manager-Content (Go Server)

Rola w ekosystemie

Manager-content pełni podwójną rolę:

  1. Web UI — panel webowy dla rodzica/szkoły (dashboard, content manager, custom worlds)
  2. Endpointy /app/* — REST/JSON dla Flutter app (dane, auth, content)
  3. Endpointy /api/* — REST/JSON do integracji z managerem (jeśli potrzebne)

To jest jedyny serwer w ekosystemie — przechowuje wszystkie dane i obsługuje przeglądarkę (HTML), aplikację mobilną (/app/*) i ewentualne integracje (/api/*). Podział na /app/ i /api/ wyraźnie rozróżnia klientów.

Tech Stack

TechnologiaZastosowanie
GoBackend, /app/*, /api/*, server-side rendering
chiHTTP router
html/templateSzablony HTML (web UI)
HTMXInteraktywność bez pełnego JS (web UI)
Alpine.jsLekka reaktywność na frontendzie (web UI)
TailwindCSSUtility-first styling (web UI)
SQLite / PostgreSQLBaza danych

Architektura

Dwa interfejsy, jeden serwer

Manager-content obsługuje dwa typy klientów z tego samego serwera Go:

  • Przeglądarka (web UI) → SSR + HTMX + Alpine.js → zwraca HTML
  • Flutter app (/app/*) → endpointy dedykowane dla aplikacji mobilnej → zwraca JSON
  • Integracje (/api/*) → endpointy do komunikacji z managerem → zwraca JSON
┌──────────────────┐          ┌──────────────────┐
│   Przeglądarka   │          │   Flutter App    │
│   (rodzic/szkoła)│          │   (dziecko)      │
│                  │          │                  │
│  HTMX + Alpine   │          │  Dio (HTTP)      │
└────────┬─────────┘          └────────┬─────────┘
         │ HTML fragments              │ JSON
         │                             │
┌────────▼─────────────────────────────▼─────────┐
│                   Go Server                     │
│                                                │
│  ┌─────────────┐  ┌──────────┐  ┌───────────┐ │
│  │ Web Handlers│  │ Services │  │API Handlers│ │
│  │ (HTML/HTMX) │──▶│ (logic)  │◀──│ (JSON)    │ │
│  └─────────────┘  └────┬─────┘  └───────────┘ │
│                         │                       │
│  ┌──────────┐     ┌────▼──────┐                │
│  │Templates │     │ Database  │                │
│  │(html/tpl)│     └───────────┘                │
│  └──────────┘                                  │
└────────────────────────────────────────────────┘

Jak działa HTMX

Zamiast pełnego przeładowania strony, HTMX wysyła żądania AJAX i podmienia fragmenty HTML:

html
<!-- Przycisk ładujący listę zadań -->
<button hx-get="/tasks" hx-target="#task-list" hx-swap="innerHTML">
  Załaduj zadania
</button>

<!-- Kontener na wynik -->
<div id="task-list"></div>

Serwer Go zwraca fragment HTML (nie JSON), który HTMX wstawia do DOM.

Jak działa Alpine.js

Alpine.js obsługuje lekki stan po stronie klienta — dropdowny, modale, walidacja formularzy:

html
<!-- Dropdown z Alpine.js -->
<div x-data="{ open: false }">
  <button @click="open = !open">Menu</button>
  <div x-show="open" @click.away="open = false">
    <a href="/dashboard">Dashboard</a>
    <a href="/content">Content</a>
  </div>
</div>

Struktura Projektu

manager-content/
├── cmd/
│   └── server/
│       └── main.go                # Entry point

├── internal/
│   ├── handler/                   # HTTP handlers (chi)
│   │   ├── web/                   # Web UI handlers (zwracają HTML)
│   │   │   ├── dashboard.go
│   │   │   ├── content.go
│   │   │   ├── worlds.go
│   │   │   └── todo.go
│   │   ├── app/                   # /app/* handlers dla Flutter (JSON)
│   │   │   ├── worlds.go
│   │   │   ├── tasks.go
│   │   │   ├── users.go
│   │   │   └── auth.go
│   │   ├── api/                   # /api/* handlers do integracji (JSON)
│   │   │   └── ...
│   │   └── auth.go                # Logowanie (kod z app / email)
│   │
│   ├── service/                   # Logika biznesowa
│   │   ├── content.go
│   │   ├── world.go
│   │   ├── todo.go
│   │   └── auth.go
│   │
│   ├── model/                     # Modele danych
│   │   ├── world.go
│   │   ├── location.go
│   │   ├── task.go
│   │   ├── user.go
│   │   └── todo.go
│   │
│   ├── repository/                # Warstwa dostępu do danych
│   │   ├── world_repo.go
│   │   ├── task_repo.go
│   │   └── user_repo.go
│   │
│   └── middleware/                 # Middleware (auth, logging)
│       ├── auth.go
│       └── logging.go

├── web/
│   ├── templates/                 # Szablony Go html/template
│   │   ├── layouts/
│   │   │   └── base.html          # Layout bazowy
│   │   ├── pages/
│   │   │   ├── dashboard.html
│   │   │   ├── content.html
│   │   │   └── worlds.html
│   │   ├── partials/              # Fragmenty HTMX
│   │   │   ├── task-list.html
│   │   │   ├── task-card.html
│   │   │   └── todo-item.html
│   │   └── components/            # Reużywalne komponenty UI
│   │       ├── button.html
│   │       ├── card.html
│   │       ├── modal.html
│   │       └── form-field.html
│   │
│   ├── static/
│   │   ├── css/
│   │   │   └── output.css         # TailwindCSS (skompilowany)
│   │   └── js/
│   │       ├── htmx.min.js
│   │       └── alpine.min.js
│   │
│   └── tailwind.config.js         # Konfiguracja TailwindCSS

├── go.mod
└── go.sum

Kluczowe Ekrany

1. Logowanie

Dwa tryby logowania:

  • Kod z aplikacji — 8-znakowy kod alfanumeryczny (wielkie litery + cyfry) wygenerowany w Flutter app
  • Kod emailowy — 8-znakowy kod alfanumeryczny wysyłany na email (dla rodziców bez app)

2. Dashboard Rodzica

Przegląd statystyk dziecka:

  • Postępy w zadaniach (ukończone / w trakcie)
  • Aktywność (ostatnie zadania, czas gry)
  • XP i poziom
  • Todo lista z deadline'ami

3. Content Manager

Tworzenie i edycja treści edukacyjnych:

  • Formularz tworzenia quizu (pytania, odpowiedzi, punkty)
  • Upload video i audio
  • Przypisywanie do lokacji i grup wiekowych
  • Podgląd zadania

4. Custom Worlds

Tworzenie własnych planet:

  • Formularz tworzenia planety (nazwa, opis, grafika)
  • Dodawanie lokacji na mapę planety
  • Przypisywanie zadań do lokacji
  • Zarządzanie dostępem (otwarta / zamknięta)

5. Todo Lista

Zarządzanie zadaniami dziecka:

  • Dodawanie zadań realnych (np. "posprzątaj pokój")
  • Linkowanie z zadaniami in-game
  • Ustawianie deadline'ów
  • Śledzenie statusu

Routing (chi)

go
r := chi.NewRouter()
r.Use(middleware.Logger)

// === Web UI (HTML) — dla przeglądarki ===
r.Group(func(r chi.Router) {
    r.Use(middleware.WebAuth)

    r.Get("/dashboard", webHandler.Dashboard)
    r.Route("/content", func(r chi.Router) {
        r.Get("/", webHandler.ListContent)
        r.Post("/", webHandler.CreateTask)
        r.Get("/{id}", webHandler.GetTask)
        r.Put("/{id}", webHandler.UpdateTask)
        r.Delete("/{id}", webHandler.DeleteTask)
    })
    r.Route("/worlds", func(r chi.Router) {
        r.Get("/", webHandler.ListWorlds)
        r.Post("/", webHandler.CreateWorld)
    })
})

// === /app/* (JSON) — dla Flutter app ===
r.Route("/app", func(r chi.Router) {
    r.Use(middleware.AppAuth)

    r.Get("/worlds", appHandler.ListWorlds)
    r.Get("/worlds/{id}", appHandler.GetWorld)
    r.Get("/worlds/{id}/locations", appHandler.GetLocations)
    r.Get("/locations/{id}/tasks", appHandler.GetTasks)
    r.Get("/users/{id}", appHandler.GetUser)
    r.Post("/auth/login", appHandler.Login)
    r.Post("/auth/manager-code", appHandler.GenerateManagerCode)
})

// === /api/* (JSON) — integracje z managerem ===
r.Route("/api", func(r chi.Router) {
    r.Use(middleware.APIAuth)
    // endpointy dodawane w miarę potrzeb
})

Web Handler (HTML)

go
func (h *WebContentHandler) CreateTask(w http.ResponseWriter, r *http.Request) {
    var task model.Task
    if err := json.NewDecoder(r.Body).Decode(&task); err != nil {
        http.Error(w, "Invalid request", http.StatusBadRequest)
        return
    }

    created, err := h.service.CreateTask(r.Context(), task)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    // Dla HTMX: zwracamy fragment HTML
    if r.Header.Get("HX-Request") == "true" {
        h.renderPartial(w, "task-card.html", created)
        return
    }

    // Dla zwykłego żądania: redirect
    http.Redirect(w, r, "/content", http.StatusSeeOther)
}

App Handler (JSON — Flutter)

go
func (h *APIWorldHandler) ListWorlds(w http.ResponseWriter, r *http.Request) {
    worlds, err := h.service.GetWorlds(r.Context())
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(worlds)
}

Wszystkie handlery (web, /app/*, /api/*) korzystają z tych samych services — logika biznesowa jest współdzielona, różni się tylko format odpowiedzi (HTML vs JSON) i kontekst klienta.

Design System

Manager-content ma odseparowany design system oparty na TailwindCSS:

  • Tokeny — kolory, spacing, typografia w tailwind.config.js
  • Komponenty — szablony Go (button, card, modal, form-field)
  • Interaktywność — HTMX dla dynamicznych fragmentów, Alpine.js dla stanu UI

Szczegóły: Design Systemy

Internacjonalizacja (i18n)

Na start: polski + angielski.

  • Tłumaczenia w plikach JSON (locales/pl.json, locales/en.json), ładowane przez Go server
  • Przełączanie języka: ikony flag (🇵🇱 / 🇬🇧) w top bar managera, obok avatara użytkownika
  • Wybrany język zapisywany w session cookie (lang), domyślnie "pl"
  • Szablony Go korzystają z funkcji t w FuncMap do tłumaczenia stringów (np. t .Lang "dashboard.title")

Lumos Islands v2 - Dokumentacja Projektowa