diff --git a/apps/manacore/apps/landing/src/components/navigation/Navbar.astro b/apps/manacore/apps/landing/src/components/navigation/Navbar.astro index 555201722..56591e233 100644 --- a/apps/manacore/apps/landing/src/components/navigation/Navbar.astro +++ b/apps/manacore/apps/landing/src/components/navigation/Navbar.astro @@ -21,6 +21,7 @@ const navLinks = [ { href: getLocalizedRoute('/apps', lang), label: t('nav.apps') }, { href: getLocalizedRoute('/pricing', lang), label: t('nav.pricing') }, { href: getLocalizedRoute('/clients', lang), label: t('nav.references') }, + { href: getLocalizedRoute('/devlog', lang), label: t('nav.devlog') }, { href: getLocalizedRoute('/privacy', lang), label: t('nav.privacy') }, ]; diff --git a/apps/manacore/apps/landing/src/content/config.ts b/apps/manacore/apps/landing/src/content/config.ts index a041437f4..109dc96a3 100644 --- a/apps/manacore/apps/landing/src/content/config.ts +++ b/apps/manacore/apps/landing/src/content/config.ts @@ -142,6 +142,21 @@ const contextCollection = defineCollection({ }), }); +const devlogCollection = defineCollection({ + type: 'content', + schema: z.object({ + title: z.string(), + description: z.string(), + date: z.date(), + author: z.string().default('Till Schneider'), + category: z.enum(['release', 'infrastructure', 'feature', 'bugfix', 'update']), + tags: z.array(z.string()).optional(), + featured: z.boolean().default(false), + commits: z.number().optional(), + readTime: z.number().optional(), + }), +}); + export const collections = { apps: appsCollection, branchen: targetGroupsCollection, @@ -150,4 +165,5 @@ export const collections = { clients: clientsCollection, mission: missionCollection, context: contextCollection, + devlog: devlogCollection, }; diff --git a/apps/manacore/apps/landing/src/content/devlog/2026-01-23-production-launch.md b/apps/manacore/apps/landing/src/content/devlog/2026-01-23-production-launch.md new file mode 100644 index 000000000..2e0eb259d --- /dev/null +++ b/apps/manacore/apps/landing/src/content/devlog/2026-01-23-production-launch.md @@ -0,0 +1,169 @@ +--- +title: 'Production Launch: 6 Apps Live auf mana.how' +description: 'Mac Mini Server Setup, Contacts App Deployment, Monitoring Stack und Landing Pages - ein produktiver Tag mit 26 Commits' +date: 2026-01-23 +author: 'Till Schneider' +category: 'release' +tags: ['deployment', 'docker', 'monitoring', 'mac-mini', 'contacts', 'infrastructure'] +featured: true +commits: 26 +readTime: 8 +--- + +Heute war ein sehr produktiver Tag mit Fokus auf die **Produktivstellung der ManaCore Apps auf dem Mac Mini Server**. Die wichtigsten Errungenschaften: + +- **6 Apps live** auf https://mana.how (Auth, Dashboard, Chat, Todo, Calendar, Clock) +- **Contacts App** vollständig deployed (Backend + Web) +- **Monitoring Stack** eingerichtet (Prometheus, Grafana, Umami Analytics) +- **Notification System** für Health Checks (Telegram + Email) +- **Shared Landing UI** für einheitliche Landing Pages + +--- + +## Mac Mini Server Setup & Management + +### Auto-Start System + +Einrichtung eines vollständigen Auto-Start-Systems für den Mac Mini Server: + +- **LaunchAgent** für automatischen Start beim Boot +- **Management Scripts:** + - `start-manacore.sh` - Startet alle Docker Container + - `stop-manacore.sh` - Stoppt alle Container + - `health-check.sh` - Prüft alle Services + - `update-images.sh` - Aktualisiert Docker Images + +### Notification System + +Implementierung eines Benachrichtigungssystems: + +- **Telegram Bot** für sofortige Alerts +- **Email Backup** via Gmail SMTP (msmtp) +- Automatische Benachrichtigung bei Service-Ausfällen + +--- + +## Contacts App Deployment + +### Docker Images erstellt + +Erstellung der Docker-Konfiguration für Contacts: + +- `apps/contacts/apps/backend/Dockerfile` (Port 3015) +- `apps/contacts/apps/web/Dockerfile` (Port 5184) +- `docker-entrypoint.sh` für automatische DB-Migrationen +- CI Workflow Updates für Image-Builds + +### MinIO Object Storage + +Einrichtung von MinIO für S3-kompatiblen Object Storage: + +- MinIO Container in docker-compose.macmini.yml +- `contacts-photos` Bucket für Kontaktbilder +- S3 Environment Variables konfiguriert + +**Live URLs:** + +- https://contacts.mana.how (Web App) +- https://contacts-api.mana.how (Backend API) + +--- + +## Monitoring & Analytics Stack + +Vollständiger Monitoring Stack eingerichtet: + +| Service | Port | Beschreibung | +| --------------------- | ---- | ------------------ | +| **Prometheus** | 9090 | Metriken-Sammlung | +| **Grafana** | 3100 | grafana.mana.how | +| **Node Exporter** | 9100 | System-Metriken | +| **cAdvisor** | 8080 | Container-Metriken | +| **Postgres Exporter** | 9187 | Datenbank-Metriken | +| **Redis Exporter** | 9121 | Cache-Metriken | +| **Umami** | 3200 | analytics.mana.how | + +### Umami Analytics Integration + +Integration von Umami Web Analytics in alle Apps: + +- Unique Website IDs für jede App +- Tracking Script in allen Web Apps und Landing Pages +- URL geändert zu stats.mana.how + +--- + +## Landing Pages & Shared Components + +### Shared Landing UI + +Neues Package `@manacore/shared-landing-ui` mit wiederverwendbaren Astro-Komponenten: + +- `Hero.astro` - Hero Section +- `Features.astro` - Feature Grid +- `Pricing.astro` - Preistabellen +- `CTA.astro` - Call-to-Action +- `Footer.astro` - Footer +- `Layout.astro` - Base Layout + +### Zentrales Pricing System + +Einheitliches Pricing für alle Mana Apps: + +| Plan | Preis | Features | +| ---- | ----------- | ------------------------------- | +| Free | 0€ | Basis-Features, limitiert | +| Pro | 4,99€/Monat | Alle Features, unbegrenzt | +| Team | 9,99€/Monat | Team-Features, Priority Support | + +--- + +## Infrastruktur-Übersicht + +### Aktive Services auf Mac Mini + +| Service | Container | Port | Status | +| ---------------- | ------------------- | --------- | ------ | +| PostgreSQL | manacore-postgres | 5432 | ✅ | +| Redis | manacore-redis | 6379 | ✅ | +| MinIO | manacore-minio | 9000/9001 | ✅ | +| Auth | mana-core-auth | 3001 | ✅ | +| Dashboard | manacore-web | 5173 | ✅ | +| Chat Backend | chat-backend | 3002 | ✅ | +| Chat Web | chat-web | 3000 | ✅ | +| Todo Backend | todo-backend | 3018 | ✅ | +| Todo Web | todo-web | 5188 | ✅ | +| Calendar Backend | calendar-backend | 3016 | ✅ | +| Calendar Web | calendar-web | 5186 | ✅ | +| Clock Backend | clock-backend | 3017 | ✅ | +| Clock Web | clock-web | 5187 | ✅ | +| Contacts Backend | contacts-backend | 3015 | ✅ | +| Contacts Web | contacts-web | 5184 | ✅ | +| Prometheus | manacore-prometheus | 9090 | ✅ | +| Grafana | manacore-grafana | 3100 | ✅ | +| Umami | manacore-umami | 3200 | ✅ | + +### Live URLs + +| App | Web | API | +| --------- | ------------------------- | ----------------------------- | +| Dashboard | https://mana.how | - | +| Auth | - | https://auth.mana.how | +| Chat | https://chat.mana.how | https://chat-api.mana.how | +| Todo | https://todo.mana.how | https://todo-api.mana.how | +| Calendar | https://calendar.mana.how | https://calendar-api.mana.how | +| Clock | https://clock.mana.how | https://clock-api.mana.how | +| Contacts | https://contacts.mana.how | https://contacts-api.mana.how | +| Grafana | https://grafana.mana.how | - | +| Analytics | https://stats.mana.how | - | + +--- + +## Nächste Schritte + +1. **DNS konfigurieren** für mana.how Domain +2. **SSL Zertifikate** einrichten (Caddy/Let's Encrypt) +3. **Grafana Dashboards** erstellen +4. **Backup-Strategie** implementieren +5. **Mobile Apps** testen mit neuen APIs +6. **Landing Pages** auf Cloudflare Pages deployen diff --git a/apps/manacore/apps/landing/src/lib/i18n/config.ts b/apps/manacore/apps/landing/src/lib/i18n/config.ts index cdb6ed8a6..a96bb7fe3 100644 --- a/apps/manacore/apps/landing/src/lib/i18n/config.ts +++ b/apps/manacore/apps/landing/src/lib/i18n/config.ts @@ -34,6 +34,7 @@ export const ui = { 'nav.forWhom': 'Für wen?', 'nav.references': 'Referenzen', 'nav.privacy': 'Datenschutz', + 'nav.devlog': 'Devlog', // Buttons 'button.startFree': 'Kostenlos testen', @@ -113,6 +114,7 @@ export const ui = { 'nav.forWhom': 'For whom?', 'nav.references': 'References', 'nav.privacy': 'Privacy', + 'nav.devlog': 'Devlog', // Buttons 'button.startFree': 'Start for free', @@ -191,6 +193,7 @@ export const ui = { 'nav.forWhom': 'Per chi?', 'nav.references': 'Referenze', 'nav.privacy': 'Privacy', + 'nav.devlog': 'Devlog', // Buttons 'button.startFree': 'Prova gratuita', @@ -272,6 +275,7 @@ export const ui = { 'nav.forWhom': 'Pour qui?', 'nav.references': 'Références', 'nav.privacy': 'Confidentialité', + 'nav.devlog': 'Devlog', // Buttons 'button.startFree': 'Essai gratuit', @@ -355,6 +359,7 @@ export const ui = { 'nav.forWhom': '¿Para quién?', 'nav.references': 'Referencias', 'nav.privacy': 'Privacidad', + 'nav.devlog': 'Devlog', // Buttons 'button.startFree': 'Prueba gratuita', diff --git a/apps/manacore/apps/landing/src/pages/devlog/[slug].astro b/apps/manacore/apps/landing/src/pages/devlog/[slug].astro new file mode 100644 index 000000000..661199963 --- /dev/null +++ b/apps/manacore/apps/landing/src/pages/devlog/[slug].astro @@ -0,0 +1,236 @@ +--- +import Layout from '../../layouts/Layout.astro'; +import Navbar from '../../components/navigation/Navbar.astro'; +import Footer from '../../components/navigation/Footer.astro'; +import Section from '../../components/content/Section.astro'; +import Container from '../../components/layout/Container.astro'; +import Heading from '../../components/typography/Heading.astro'; +import Text from '../../components/typography/Text.astro'; +import { getCollection } from 'astro:content'; +import { Icon } from 'astro-icon/components'; + +export async function getStaticPaths() { + const posts = await getCollection('devlog'); + return posts.map((post) => ({ + params: { slug: post.slug }, + props: { post }, + })); +} + +const { post } = Astro.props; +const { Content } = await post.render(); + +const categoryColors: Record = { + release: { + bg: 'from-green-500/10 to-emerald-500/10', + text: 'text-green-500', + border: 'border-green-500/30', + }, + infrastructure: { + bg: 'from-blue-500/10 to-cyan-500/10', + text: 'text-blue-500', + border: 'border-blue-500/30', + }, + feature: { + bg: 'from-purple-500/10 to-pink-500/10', + text: 'text-purple-500', + border: 'border-purple-500/30', + }, + bugfix: { + bg: 'from-orange-500/10 to-amber-500/10', + text: 'text-orange-500', + border: 'border-orange-500/30', + }, + update: { + bg: 'from-gray-500/10 to-slate-500/10', + text: 'text-gray-400', + border: 'border-gray-500/30', + }, +}; + +const categoryLabels: Record = { + release: 'Release', + infrastructure: 'Infrastructure', + feature: 'Feature', + bugfix: 'Bugfix', + update: 'Update', +}; + +const colors = categoryColors[post.data.category] || categoryColors.update; + +const formatDate = (date: Date) => { + return new Intl.DateTimeFormat('de-DE', { + weekday: 'long', + day: '2-digit', + month: 'long', + year: 'numeric', + }).format(date); +}; +--- + + +
+ + + +
+
+
+
+
+
+
+
+
+ + +
+ + + + Zurück zum Devlog + + + +
+ +
+ + {formatDate(post.data.date)} +
+ + + + {categoryLabels[post.data.category]} + + + + { + post.data.commits && ( +
+ + {post.data.commits} Commits +
+ ) + } + + + { + post.data.readTime && ( +
+ + {post.data.readTime} min Lesezeit +
+ ) + } +
+ + + + {post.data.title} + + + + + {post.data.description} + + + +
+
+ {post.data.author.charAt(0)} +
+
+ {post.data.author} + Autor +
+
+
+
+
+ + +
+ +
+ +
+ + + { + post.data.tags && post.data.tags.length > 0 && ( +
+ + Tags + +
+ {post.data.tags.map((tag: string) => ( + + #{tag} + + ))} +
+
+ ) + } + + + +
+
+ +
+
+
+ + diff --git a/apps/manacore/apps/landing/src/pages/devlog/index.astro b/apps/manacore/apps/landing/src/pages/devlog/index.astro new file mode 100644 index 000000000..20a95d219 --- /dev/null +++ b/apps/manacore/apps/landing/src/pages/devlog/index.astro @@ -0,0 +1,205 @@ +--- +import Layout from '../../layouts/Layout.astro'; +import Navbar from '../../components/navigation/Navbar.astro'; +import Footer from '../../components/navigation/Footer.astro'; +import Section from '../../components/content/Section.astro'; +import Container from '../../components/layout/Container.astro'; +import Heading from '../../components/typography/Heading.astro'; +import Text from '../../components/typography/Text.astro'; +import HeroSection from '../../components/content/HeroSection.astro'; +import { getCollection } from 'astro:content'; +import { Icon } from 'astro-icon/components'; + +const posts = await getCollection('devlog'); +const sortedPosts = posts.sort((a, b) => b.data.date.getTime() - a.data.date.getTime()); + +const categoryColors: Record = { + release: { + bg: 'from-green-500/10 to-emerald-500/10', + text: 'text-green-500', + border: 'border-green-500/30', + }, + infrastructure: { + bg: 'from-blue-500/10 to-cyan-500/10', + text: 'text-blue-500', + border: 'border-blue-500/30', + }, + feature: { + bg: 'from-purple-500/10 to-pink-500/10', + text: 'text-purple-500', + border: 'border-purple-500/30', + }, + bugfix: { + bg: 'from-orange-500/10 to-amber-500/10', + text: 'text-orange-500', + border: 'border-orange-500/30', + }, + update: { + bg: 'from-gray-500/10 to-slate-500/10', + text: 'text-gray-400', + border: 'border-gray-500/30', + }, +}; + +const categoryLabels: Record = { + release: 'Release', + infrastructure: 'Infrastructure', + feature: 'Feature', + bugfix: 'Bugfix', + update: 'Update', +}; + +const formatDate = (date: Date) => { + return new Intl.DateTimeFormat('de-DE', { + day: '2-digit', + month: 'long', + year: 'numeric', + }).format(date); +}; +--- + + +
+ + + +
+
+
+
+
+
+
+
+
+ + +
+ + +
+
+
+
+
+
+ + + + + { + sortedPosts.length === 0 && ( +
+
+ +
+ + Noch keine Einträge + + Hier erscheinen bald Entwicklungsberichte. +
+ ) + } +
+
+ +
+