Some checks failed
🧪 CI (QA) / 🧪 Quality Assurance (push) Failing after 1m3s
- Restructure to pnpm monorepo (site moved to apps/web) - Integrate @mintel/tsconfig, @mintel/eslint-config, @mintel/husky-config - Implement Docker service architecture (Varnish, Directus, Gatekeeper) - Setup environment-aware Gitea Actions deployment
192 lines
4.9 KiB
Markdown
192 lines
4.9 KiB
Markdown
# 🚀 Hetzner Deployment - Complete Plan
|
|
|
|
## ✅ What's Ready
|
|
|
|
### 1. **Docker Setup**
|
|
- ✅ Multi-stage Dockerfile (optimized build)
|
|
- ✅ Nginx config with caching & security headers
|
|
- ✅ Caddy reverse proxy with automatic SSL
|
|
- ✅ Redis container for caching
|
|
- ✅ Docker Compose for local & production
|
|
|
|
### 2. **CI/CD Pipeline**
|
|
- ✅ Woodpecker `.woodpecker.yml` config
|
|
- ✅ Automatic testing, building, deployment
|
|
- ✅ Slack notifications (optional)
|
|
- ✅ Gitea integration ready
|
|
|
|
### 3. **Cache Architecture** (Professional DI)
|
|
```
|
|
src/utils/cache/
|
|
├── interfaces.ts # Contracts
|
|
├── memory-adapter.ts # In-memory implementation
|
|
├── redis-adapter.ts # Redis implementation
|
|
└── index.ts # Service + Factory
|
|
```
|
|
|
|
**Usage:**
|
|
```typescript
|
|
// Choose your adapter at construction
|
|
const cache = new CacheService(new RedisCacheAdapter(config));
|
|
// or
|
|
const cache = new CacheService(new MemoryCacheAdapter(config));
|
|
|
|
// Use it
|
|
await cache.wrap('key', asyncFn, ttl);
|
|
```
|
|
|
|
### 4. **Clean Analytics Architecture** (Constructor DI)
|
|
```
|
|
src/utils/analytics/
|
|
├── interfaces.ts # Contracts
|
|
├── plausible-adapter.ts # Plausible implementation
|
|
└── index.ts # Service + Factory
|
|
```
|
|
|
|
**Usage:**
|
|
```typescript
|
|
// Choose adapter at construction
|
|
const analytics = new AnalyticsService(new PlausibleAdapter(config));
|
|
|
|
// Track events
|
|
await analytics.trackEvent('Page Load', { loadTime: 123 });
|
|
await analytics.trackOutboundLink(url, text);
|
|
await analytics.trackSearch(query, path);
|
|
```
|
|
|
|
**Astro Component:**
|
|
```astro
|
|
---
|
|
import { createPlausibleAnalytics } from '../utils/analytics';
|
|
const analytics = createPlausibleAnalytics({ domain, scriptUrl });
|
|
const adapter = analytics.getAdapter();
|
|
const scriptTag = adapter.getScriptTag?.();
|
|
---
|
|
{scriptTag && <Fragment set:html={scriptTag} />}
|
|
```
|
|
|
|
### 5. **Deployment Scripts**
|
|
- ✅ `deploy.sh` - Manual deployment
|
|
- ✅ `docker-compose.yml` - Container orchestration
|
|
- ✅ `DEPLOYMENT.md` - Complete documentation
|
|
|
|
## 🎯 Simple Deployment Flow
|
|
|
|
### Option A: Manual (5 minutes)
|
|
```bash
|
|
# On Hetzner VM
|
|
git clone your-repo /opt/mintel
|
|
cd /opt/mintel
|
|
echo "DOMAIN=mintel.me" > .env
|
|
echo "ADMIN_EMAIL=admin@mintel.me" >> .env
|
|
./deploy.sh
|
|
```
|
|
|
|
### Option B: CI/CD (Zero touch)
|
|
```bash
|
|
# On your machine
|
|
git push origin main
|
|
# Woodpecker handles everything
|
|
```
|
|
|
|
## 📊 Performance Features
|
|
|
|
| Feature | Implementation | Benefit |
|
|
|---------|---------------|---------|
|
|
| **Static Caching** | Nginx 1-year cache | Instant asset loading |
|
|
| **Redis Cache** | CacheService wrapper | Fast data retrieval |
|
|
| **Gzip/Brotli** | Nginx compression | ~70% smaller transfers |
|
|
| **HTTP/2** | Caddy reverse proxy | Multiplexed requests |
|
|
| **Auto-SSL** | Let's Encrypt via Caddy | Zero config HTTPS |
|
|
|
|
## 🔒 Security
|
|
|
|
- ✅ HSTS headers
|
|
- ✅ XSS protection
|
|
- ✅ Clickjacking prevention
|
|
- ✅ Server header hiding
|
|
- ✅ Automatic SSL renewal
|
|
|
|
## 🏗️ Architecture (Clean & Decoupled)
|
|
|
|
**Cache Pattern:**
|
|
```
|
|
Your Code → CacheService → [Redis | Memory]Adapter
|
|
↓
|
|
CacheAdapter Interface
|
|
```
|
|
|
|
**Analytics Pattern:**
|
|
```
|
|
Your Code → AnalyticsService → [Plausible]Adapter
|
|
↓
|
|
AnalyticsAdapter Interface
|
|
```
|
|
|
|
**Constructor Injection:**
|
|
```typescript
|
|
// Cache - Production
|
|
const cache = new CacheService(new RedisCacheAdapter(config));
|
|
|
|
// Cache - Development/Testing
|
|
const cache = new CacheService(new MemoryCacheAdapter(config));
|
|
|
|
// Analytics
|
|
const analytics = new AnalyticsService(new PlausibleAdapter(config));
|
|
```
|
|
|
|
## 📁 Files Created
|
|
|
|
```
|
|
mintel.me/
|
|
├── docker/
|
|
│ ├── Dockerfile
|
|
│ ├── nginx.conf
|
|
│ └── Caddyfile
|
|
├── docker-compose.yml
|
|
├── .woodpecker.yml
|
|
├── deploy.sh
|
|
├── DEPLOYMENT.md
|
|
├── DEPLOYMENT_SUMMARY.md
|
|
├── src/
|
|
│ ├── utils/cache/
|
|
│ │ ├── interfaces.ts
|
|
│ │ ├── memory-adapter.ts
|
|
│ │ ├── redis-adapter.ts
|
|
│ │ └── index.ts
|
|
│ ├── utils/analytics/
|
|
│ │ ├── interfaces.ts
|
|
│ │ ├── plausible-adapter.ts
|
|
│ │ └── index.ts
|
|
│ ├── components/
|
|
│ │ └── Analytics.astro (clean DI pattern)
|
|
│ └── layouts/
|
|
│ └── BaseLayout.astro (includes analytics)
|
|
└── package.json (ioredis added)
|
|
```
|
|
|
|
## 🎯 Next Steps
|
|
|
|
1. **Configure** environment variables
|
|
2. **Test locally**: `docker-compose up`
|
|
3. **Setup Woodpecker secrets** in Gitea
|
|
4. **Deploy** to Hetzner
|
|
5. **Monitor** with Plausible
|
|
|
|
## 🚀 Result
|
|
|
|
- **Fast**: Redis + Nginx caching
|
|
- **Secure**: Auto SSL + security headers
|
|
- **Simple**: One command deployment
|
|
- **Clean**: Proper DI architecture
|
|
- **Smart**: CI/CD automation
|
|
- **Private**: Self-hosted analytics
|
|
|
|
**Total setup time**: ~15 minutes
|
|
**Maintenance**: Near zero
|
|
**Performance**: Excellent
|
|
**Security**: Production-ready
|
|
|
|
---
|
|
|
|
**Ready to deploy!** 🎉 |