Files
comment-api/README.md
2025-11-23 20:04:00 +03:00

308 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Comment API iskeleti
Node.js + Express + Socket.io + Supabase + Redis tabanlı login/register API iskeleti. Güvenlik odaklı, host allowlist, rate limit, admin rol kilidi ve Redis oturum yönetimi içerir. Docker-compose ile Redis + API birlikte ayağa kalkar.
## Dizın yapısı
```
src/
app.js // Express app
server.js // HTTP + Socket.io bootstrap
config/
env.js // .env yükleme ve parse
supabase.js // Supabase client
redis.js // Redis client
middleware/
authRequired.js
adminRequired.js
rateLimit.js
hostOnly.js
errorHandler.js
routes/
auth.routes.js
health.routes.js
controllers/
auth.controller.js
services/
auth.service.js
session.service.js
adminLock.service.js
utils/
crypto.js // bcrypt + JWT yardımcıları
response.js // standart JSON çıktı
```
## Çalıştırma
1. Bağımlılıklar:
```bash
npm install
```
> Not: `metascraper` bağımlılığı özel git repodan gelir (`git+https://gitea.wisecolt-panda.net/wisecolt/metascraper.git`). Erişim için uygun token/SSH anahtarı olan bir ortamda `npm install` çalıştırın.
> Docker build için base imaja `git` eklenmiştir; özel repo erişimi için gerekirse `GIT_SSH_COMMAND` veya `GITHUB_TOKEN` benzeri auth ekleyin.
2. Ortam değişkenleri: `.env.example` dosyasını `.env` olarak kopyalayıp doldurun.
3. Geliştirme:
```bash
npm run dev
```
4. Üretim:
```bash
npm start
```
## Docker
Yerelde (dev, hot-reload) Redis ile birlikte çalıştırmak için:
```bash
docker compose -f docker-compose.dev.yml up --build
# durdurmak için
docker compose -f docker-compose.dev.yml down
```
- API: `http://localhost:${PORT}`
- Redis: `redis://localhost:6379` (container içinde `redis://redis:6379`)
### Prod modunda Docker
1. `.env` içinde `NODE_ENV=production` ve mümkünse `COOKIE_SECURE=true`, `TRUST_PROXY=loopback` (veya proxy sayısı) olarak ayarlayın.
2. Çalıştırın:
```bash
docker compose up --build -d
```
3. Durdurun:
```bash
docker compose down
```
## .env örneği
```
NODE_ENV=development
PORT=3000
TRUST_PROXY=loopback
SUPABASE_URL=https://YOUR-SUPABASE-PROJECT.supabase.co
SUPABASE_SERVICE_ROLE_KEY=YOUR_SUPABASE_SERVICE_ROLE_KEY
REDIS_URL=redis://redis:6379
JWT_SECRET=supersecretjwt
JWT_EXPIRES_IN=7d
COOKIE_NAME=sid
COOKIE_SECURE=false
ADMIN_EMAIL=admin@example.com
ADMIN_USERNAME=admin
ADMIN_NAME=Admin User
ADMIN_ROLE_LOCK=true
HOST_ONLY_ALLOWLIST=127.0.0.1,::1
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX=100
LOGIN_RATE_LIMIT_WINDOW_MS=60000
LOGIN_RATE_LIMIT_MAX=5
```
## Endpointler
- `GET /health` — `{ ok: true, time }`
- `POST /auth/register` — body `{ email, name, username, password }`
- `POST /auth/login` — body `{ emailOrUsername, password }`
- `POST /auth/logout` — cookie tabanlı JWT siler
- `GET /auth/me` — aktif kullanıcıyı döndürür
- `POST /auth/refresh` — yeni JWT üretir (iskelet)
- `POST /meta/scrape` — body `{ url }`, destekli domainler: `netflix.com`, `primevideo.com` (yalnızca `role: user`)
- Başarılı sorgu Supabase `media_data` tablosuna kaydedilir: `media_id, media_name, year, type(movie/tv), thumbnail_url, info, genre, media_provider (netflix/primevideo)`
### Postman örnekleri
**Register**
```
POST {{baseUrl}}/auth/register
Content-Type: application/json
Body:
{
"email": "user@example.com",
"name": "Test User",
"username": "testuser",
"password": "password123"
}
```
**200**:
```
{
"user": {
"id": "...",
"email": "user@example.com",
"name": "Test User",
"username": "testuser",
"roleEffective": "user"
}
}
```
**Login**
```
POST {{baseUrl}}/auth/login
Content-Type: application/json
Body:
{
"emailOrUsername": "testuser",
"password": "password123"
}
```
**200**:
```
{
"user": {
"id": "...",
"email": "user@example.com",
"name": "Test User",
"username": "testuser",
"roleEffective": "user"
}
}
```
JWT otomatik olarak `Cookie: sid=<token>` olarak set edilir.
**Me**
```
GET {{baseUrl}}/auth/me
Cookie: sid=<token>
```
**200**:
```
{
"user": {
"id": "...",
"email": "...",
"username": "...",
"roleEffective": "user"
}
}
```
**Logout**
```
POST {{baseUrl}}/auth/logout
Cookie: sid=<token>
```
**200**:
```
{ "message": "Çıkış yapıldı" }
```
**Meta scrape (Netflix/Prime)**
```
POST {{baseUrl}}/meta/scrape
Content-Type: application/json
Cookie: sid=<token> # roleEffective=user
Body:
{ "url": "https://www.netflix.com/tr/title/81507921" }
```
Prime örneği:
```
{ "url": "https://www.primevideo.com/-/tr/detail/0NHIN3TGAI9L7VZ45RS52RHUPL/ref=share_ios_movie" }
```
**200** (örnek):
```
{
"provider": "netflix",
"type": "movie",
"media": {
"url": "https://www.netflix.com/title/82123114",
"id": "82123114",
"name": "ONE SHOT with Ed Sheeran",
"year": "2025",
"seasons": null,
"thumbnail": "...",
"info": "...",
"genre": "..."
},
"saved": {
"media_id": "82123114",
"media_name": "ONE SHOT with Ed Sheeran",
"year": "2025",
"type": "movie",
"thumbnail_url": "...",
"info": "...",
"genre": "...",
"media_provider": "netflix",
"created_at": "..."
}
}
```
Hata çıktıları tek tip:
```
{
"error": { "code": "ERROR_CODE", "message": "Açıklama", "details": [...] }
}
```
## Supabase şeması (SQL)
```sql
create extension if not exists "pgcrypto";
create table if not exists public.users (
id uuid primary key default gen_random_uuid(),
email text unique not null,
name text not null,
username text unique not null,
password_hash text not null,
role text not null default 'user' check (role in ('user','admin')),
created_at timestamp with time zone default now()
);
create table if not exists public.media_data (
media_id varchar,
media_name text,
year numeric,
type text,
thumbnail_url varchar,
info text,
genre text,
media_provider text,
created_at timestamp with time zone default now()
);
```
> Admin rol kilidi: Sadece `.env` içindeki `ADMIN_EMAIL` ve `ADMIN_USERNAME` ile birebir eşleşen kullanıcı API tarafından `admin` kabul edilir. Supabase üzerinde rol elle değiştirilse bile etkisizdir.
## Güvenlik notları
- `hostOnly` middleware ve CORS sadece allowlistteki host/ip erişimine izin verir.
- Global rate limit + login özel rate limit aktiftir.
- JWT httpOnly cookie olarak set edilir; Redis içinde `session:{jti}` anahtarıyla oturum metadata tutulur.
## Socket.io
- Handshake sırasında cookiedeki JWT doğrulanır, Redis oturum kontrol edilir.
- Başarılı bağlantıda `welcome` eventi döner.
- Örnek ping/pong:
- Client: `socket.emit('ping')`
- Server cevabı: `pong` içinde zaman damgası.
## Docker notları
- `docker-compose.yml` içinde `api` servisi `redis` servisine bağımlıdır.
- Geliştirme için kod klasörü container içine mount edilerek canlı yenileme (`npm run dev`) sağlanır.
- Sağlık kontrolü: container içinde `GET /health` çağrısı ile yapılır.
## Yapılandırılabilir noktalar
- Rate limit penceresi ve max değerleri `.env` ile ayarlanır.
- `HOST_ONLY_ALLOWLIST` ile izin verilen host/ip listesi yönetilir.
- `ADMIN_ROLE_LOCK` kapatılmadıkça tek admin `.env`deki kullanıcıdır.
## TODO / Genişletme fikirleri
- Refresh token için ayrı bir anahtar ve whitelist mekanizması.
- Audit logları (morgan loglarını dosyaya yazma).
- Supabase RPC veya policylerle ek güvenlik.
- Testler (Jest/Supertest) ve CI pipelineı.