first commit

This commit is contained in:
2025-11-23 20:04:00 +03:00
commit 4c8c468acd
31 changed files with 1380 additions and 0 deletions

307
README.md Normal file
View File

@@ -0,0 +1,307 @@
# 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ı.