22 KiB
22 KiB
🔄 Hosting Platform - İş Akışı Dokümantasyonu
Bu dokümantasyon, Hosting Platform projesinin tüm iş akışlarını detaylı olarak açıklar.
📋 İçindekiler
- Proje Genel Bakış
- Müşteri Panel İş Akışları
- Admin Panel İş Akışları
- API Endpoint Referansı
- Veritabanı Yapısı
- Sistem Mimarisi
🎯 Proje Genel Bakış
Müşteri Panel (Customer Platform)
- Frontend: https://argeict.net (React + Vite, Port 3000)
- Backend API: https://api.argeict.net (Flask, Port 5000)
- Database:
hosting_db(PostgreSQL) - Repository: https://gitea.argeict.net/hostadmin/hosting-platform
Admin Panel (Admin Platform)
- Frontend: https://admin.argeict.net (React + Vite, Port 3001)
- Backend API: https://admin-api.argeict.net (Flask, Port 5001)
- Database:
admin_hosting_db(PostgreSQL) - Repository: https://gitea.argeict.net/argeict/admin-panel
Teknoloji Stack
- Backend: Flask 3.0, SQLAlchemy 2.0, PostgreSQL, JWT, Redis
- Frontend: React 18, Vite, TailwindCSS, Axios
- Infrastructure: Docker, Nginx, Supervisor, Cloudflare
- Security: Separate databases, JWT authentication, Token encryption
👤 Müşteri Panel İş Akışları
1. Kullanıcı Kaydı ve Giriş
Kayıt Akışı
1. Kullanıcı → /register sayfası
2. Form doldurma (email, password, full_name)
3. POST /api/auth/register
4. Backend:
- Email kontrolü (unique)
- Password hash (bcrypt)
- User kaydı oluştur
- Customer profili oluştur (default plan)
- JWT token üret
5. Response: {token, user, customer}
6. Frontend: Token'ı localStorage'a kaydet
7. Redirect → Dashboard
Giriş Akışı
1. Kullanıcı → /login sayfası
2. Email & Password gir
3. POST /api/auth/login
4. Backend:
- User kontrolü
- Password doğrulama
- is_active kontrolü
- JWT token üret
- last_login güncelle
5. Response: {token, user, customer}
6. Frontend: Token'ı localStorage'a kaydet
7. Redirect → Dashboard
2. Domain Ekleme İş Akışı (Detaylı)
Adım 1: Domain Doğrulama
Endpoint: POST /api/dns/validate-domain
Request: {domain: "example.com"}
Backend İşlemleri:
1. Domain formatı kontrolü (regex)
2. Veritabanında domain var mı kontrol
3. Customer domain limiti kontrolü
4. Response: {status: "success", domain: "example.com"}
Adım 2: Cloudflare Hesap Seçimi
A) Company Account Kullanımı:
- GET /api/admin/cf-accounts
- Aktif CF hesapları listele
- Kota kontrolü (current_domain_count < max_domains)
- Kullanıcı hesap seçer
B) Own Account Kullanımı:
- Kullanıcı CF email ve API token girer
- Token frontend'de tutulur (geçici)
Adım 3: Zone Doğrulama
Endpoint: POST /api/dns/validate-zone
Request: {
domain: "example.com",
cf_token: "xxx",
cf_email: "user@example.com"
}
Backend İşlemleri:
1. CloudflareService başlat
2. cf.zones.get() ile zone bilgisi al
3. Zone ID, nameservers, status kontrol
4. Response: {
zone_id: "xxx",
nameservers: ["ns1.cloudflare.com", ...],
status: "active"
}
Adım 4: Nameserver Kontrolü
Endpoint: GET /api/dns/check-nameservers?domain=example.com&zone_id=xxx
Backend İşlemleri:
1. cf.zones.get(zone_id) ile zone bilgisi al
2. status === "active" kontrolü
3. Response: {
configured: true/false,
current_nameservers: [...],
required_nameservers: [...]
}
Frontend:
- Eğer configured=false ise:
- NS talimatları göster
- "Kontrol Et" butonu
- Polling ile kontrol et
Adım 5: DNS Önizleme
Endpoint: POST /api/dns/preview-changes
Request: {
domain: "example.com",
zone_id: "xxx",
cf_token: "xxx"
}
Backend İşlemleri:
1. Load Balancer IP seç (hash-based):
- MD5(domain) % len(LB_IPS)
- LB_IPS = [185.199.108.153, 185.199.109.153, 185.199.110.153]
2. Mevcut DNS kayıtlarını al:
- cf.zones.dns_records.get(zone_id, type="A")
3. Değişiklikleri hesapla:
- Root domain (example.com) → new_ip
- WWW subdomain (www.example.com) → new_ip
4. Response: {
domain: "example.com",
new_ip: "185.199.108.153",
changes: [
{name: "example.com", action: "update", old: {...}, new: {...}},
{name: "www.example.com", action: "create", new: {...}}
]
}
Adım 6: DNS Değişikliklerini Uygula
Endpoint: POST /api/dns/apply-changes
Request: {
domain: "example.com",
zone_id: "xxx",
cf_token: "xxx",
preview: {...},
proxy_enabled: true
}
Backend İşlemleri:
1. Her değişiklik için:
- update: cf.zones.dns_records.patch(record_id)
- create: cf.zones.dns_records.post()
- delete: cf.zones.dns_records.delete(record_id)
2. SSL Yapılandırması:
- SSL Mode: Full
- Always Use HTTPS: ON
- Auto HTTPS Rewrites: ON
- Minimum TLS Version: 1.2
3. Domain veritabanına kaydet:
- Domain tablosu
- DNS Records tablosu
4. CF Account güncelle (company account ise):
- current_domain_count += 1
5. Response: {status: "success", domain: {...}}
3. Container Deployment İş Akışı
Container Oluşturma
Endpoint: POST /api/containers/deploy
Request: {
domain_id: 1,
container_type: "wordpress",
config: {
php_version: "8.1",
mysql_version: "8.0"
}
}
Backend İşlemleri:
1. Domain kontrolü (customer'a ait mi?)
2. Container limiti kontrolü
3. Docker container oluştur
4. Database oluştur
5. Nginx config oluştur
6. Container başlat
7. Veritabanına kaydet
8. Response: {container_id, status, access_url}
4. Subscription Management
Plan Görüntüleme
Endpoint: GET /api/customer/plans
Response: [
{
id: 1,
name: "Basic",
price_monthly: 9.99,
max_domains: 1,
max_containers: 1,
features: [...]
},
...
]
Plan Yükseltme
Endpoint: POST /api/customer/upgrade-plan
Request: {
plan_id: 2,
billing_cycle: "monthly"
}
Backend İşlemleri:
1. Plan kontrolü
2. Ödeme işlemi (Stripe/PayPal)
3. Customer güncelle:
- subscription_plan
- max_domains
- max_containers
- subscription_end
4. Response: {status: "success", customer: {...}}
🔧 Admin Panel İş Akışları
1. Admin Giriş
Endpoint: POST /api/auth/login
Request: {username: "admin", password: "xxx"}
Backend İşlemleri:
1. AdminUser kontrolü
2. Password doğrulama
3. is_active kontrolü
4. JWT token üret (role bilgisi dahil)
5. last_login güncelle
6. AuditLog kaydet (action: "login")
7. Response: {token, admin: {...}}
2. Subscription Plan Yönetimi
Plan Oluşturma
Endpoint: POST /api/plans
Request: {
name: "Premium",
slug: "premium",
description: "Premium plan",
price_monthly: 29.99,
price_yearly: 299.99,
max_domains: 10,
max_containers: 10,
max_storage_gb: 100,
max_bandwidth_gb: 1000,
features: ["SSL", "Backup", "Support"],
is_active: true,
is_visible: true,
sort_order: 2
}
Backend İşlemleri:
1. Slug unique kontrolü
2. SubscriptionPlan oluştur
3. AuditLog kaydet
4. Response: {status: "success", plan: {...}}
Plan Güncelleme
Endpoint: PUT /api/plans/:id
Request: {price_monthly: 24.99, ...}
Backend İşlemleri:
1. Plan kontrolü
2. Güncelleme
3. AuditLog kaydet
4. Response: {status: "success", plan: {...}}
3. Cloudflare Hesap Yönetimi
CF Hesabı Ekleme
Endpoint: POST /api/cf-accounts
Request: {
name: "CF Account 1",
email: "cf@company.com",
api_token: "xxx",
max_domains: 100,
notes: "Primary account"
}
Backend İşlemleri:
1. Name unique kontrolü
2. Token şifreleme (Fernet encryption)
3. CloudflareAccount oluştur
4. AuditLog kaydet
5. Response: {status: "success", account: {...}}
CF Hesabı Güncelleme
Endpoint: PUT /api/cf-accounts/:id
Request: {max_domains: 150, is_active: true}
Backend İşlemleri:
1. Account kontrolü
2. Token güncelleme (varsa şifrele)
3. Güncelleme
4. AuditLog kaydet
5. Response: {status: "success", account: {...}}
4. Müşteri Yönetimi
Müşteri Listesi
Endpoint: GET /api/customers
Response: {
status: "success",
customers: [
{
id: 1,
email: "user@example.com",
full_name: "John Doe",
subscription_plan: "basic",
domain_count: 2,
is_active: true,
created_at: "2024-01-01"
},
...
]
}
Müşteri Plan Güncelleme
Endpoint: PUT /api/customers/:id/plan
Request: {
plan_slug: "premium",
billing_cycle: "yearly"
}
Backend İşlemleri:
1. Customer kontrolü
2. Plan kontrolü
3. Customer güncelle
4. AuditLog kaydet
5. Response: {status: "success", customer: {...}}
5. Audit Log Sistemi
Log Görüntüleme
Endpoint: GET /api/audit/logs?page=1&limit=50&action=create_plan
Response: {
logs: [
{
id: 1,
admin: "admin1",
action: "create_plan",
resource_type: "plan",
resource_id: 5,
details: {...},
ip_address: "192.168.1.1",
created_at: "2024-01-01 10:00:00"
},
...
],
total: 150,
page: 1,
pages: 3
}
📡 API Endpoint Referansı
Müşteri Panel API (Port 5000)
Authentication
POST /api/auth/register- Yeni kullanıcı kaydıPOST /api/auth/login- Kullanıcı girişiGET /api/auth/me- Mevcut kullanıcı bilgisiPOST /api/auth/logout- Çıkış
Customer
GET /api/customer/domains- Domain listesiPOST /api/customer/domains- Yeni domain ekleGET /api/customer/domains/:id- Domain detayDELETE /api/customer/domains/:id- Domain silGET /api/customer/stats- Müşteri istatistikleri
DNS Management
POST /api/dns/validate-domain- Domain doğrulamaPOST /api/dns/validate-zone- CF zone doğrulamaPOST /api/dns/select-company-account- Şirket CF hesabı seçGET /api/dns/check-nameservers- NS kontrolüPOST /api/dns/preview-changes- DNS değişiklik önizlemePOST /api/dns/apply-changes- DNS değişikliklerini uygula
Admin (Customer Panel)
GET /api/admin/cf-accounts- CF hesap listesiPOST /api/admin/cf-accounts- Yeni CF hesabıPUT /api/admin/cf-accounts/:id- CF hesap güncelleGET /api/admin/customers- Müşteri listesiGET /api/admin/customers/:id- Müşteri detayPUT /api/admin/customers/:id/plan- Müşteri planı güncelleGET /api/admin/stats- Admin istatistikleri
Containers
GET /api/containers- Container listesiPOST /api/containers/deploy- Container deployGET /api/containers/:id- Container detayDELETE /api/containers/:id- Container sil
Admin Panel API (Port 5001)
Authentication
POST /api/auth/login- Admin girişiGET /api/auth/me- Mevcut admin bilgisiPOST /api/auth/logout- Çıkış
Plans
GET /api/plans- Plan listesiPOST /api/plans- Yeni plan oluşturGET /api/plans/:id- Plan detayPUT /api/plans/:id- Plan güncelleDELETE /api/plans/:id- Plan sil
Cloudflare Accounts
GET /api/cf-accounts- CF hesap listesiPOST /api/cf-accounts- Yeni CF hesabıGET /api/cf-accounts/:id- CF hesap detayPUT /api/cf-accounts/:id- CF hesap güncelleDELETE /api/cf-accounts/:id- CF hesap sil
Customers
GET /api/customers- Müşteri listesi (Customer API'den)GET /api/customers/:id- Müşteri detayPUT /api/customers/:id/plan- Plan güncellePUT /api/customers/:id/status- Durum güncelle
Audit
GET /api/audit/logs- Audit log listesiGET /api/audit/logs/:id- Log detayGET /api/audit/stats- Audit istatistikleri
🗄️ Veritabanı Yapısı
Müşteri Panel Database (hosting_db)
users
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
full_name VARCHAR(100),
role VARCHAR(20) DEFAULT 'customer',
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_login TIMESTAMP
);
customers
CREATE TABLE customers (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id),
subscription_plan VARCHAR(50) DEFAULT 'free',
max_domains INTEGER DEFAULT 1,
max_containers INTEGER DEFAULT 1,
max_storage_gb INTEGER DEFAULT 10,
subscription_start TIMESTAMP,
subscription_end TIMESTAMP,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
domains
CREATE TABLE domains (
id SERIAL PRIMARY KEY,
domain_name VARCHAR(255) UNIQUE NOT NULL,
customer_id INTEGER REFERENCES customers(id),
created_by INTEGER REFERENCES users(id),
project_name VARCHAR(100),
use_cloudflare BOOLEAN DEFAULT TRUE,
cf_account_type VARCHAR(20), -- 'company' or 'own'
cf_account_id INTEGER REFERENCES cloudflare_accounts(id),
cf_zone_id VARCHAR(100),
cf_api_token_encrypted TEXT, -- Encrypted token for 'own' accounts
cf_proxy_enabled BOOLEAN DEFAULT TRUE,
lb_ip VARCHAR(45),
status VARCHAR(20) DEFAULT 'pending',
dns_configured BOOLEAN DEFAULT FALSE,
ssl_configured BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
dns_records
CREATE TABLE dns_records (
id SERIAL PRIMARY KEY,
domain_id INTEGER REFERENCES domains(id),
record_type VARCHAR(10), -- A, CNAME, MX, TXT, etc.
name VARCHAR(255),
value TEXT,
ttl INTEGER DEFAULT 300,
proxied BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
cloudflare_accounts
CREATE TABLE cloudflare_accounts (
id SERIAL PRIMARY KEY,
name VARCHAR(100) UNIQUE NOT NULL,
email VARCHAR(255) NOT NULL,
api_token_encrypted TEXT NOT NULL, -- Fernet encrypted
max_domains INTEGER DEFAULT 100,
current_domain_count INTEGER DEFAULT 0,
notes TEXT,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Admin Panel Database (admin_hosting_db)
admin_users
CREATE TABLE admin_users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(120) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
full_name VARCHAR(100),
role VARCHAR(20) DEFAULT 'admin', -- 'admin' or 'super_admin'
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_login TIMESTAMP
);
subscription_plans
CREATE TABLE subscription_plans (
id SERIAL PRIMARY KEY,
name VARCHAR(50) UNIQUE NOT NULL,
slug VARCHAR(50) UNIQUE NOT NULL,
description TEXT,
price_monthly DECIMAL(10, 2) DEFAULT 0,
price_yearly DECIMAL(10, 2) DEFAULT 0,
max_domains INTEGER DEFAULT 1,
max_containers INTEGER DEFAULT 1,
max_storage_gb INTEGER DEFAULT 10,
max_bandwidth_gb INTEGER DEFAULT 100,
features JSON, -- Array of feature strings
is_active BOOLEAN DEFAULT TRUE,
is_visible BOOLEAN DEFAULT TRUE,
sort_order INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
cloudflare_accounts
CREATE TABLE cloudflare_accounts (
id SERIAL PRIMARY KEY,
name VARCHAR(100) UNIQUE NOT NULL,
email VARCHAR(255) NOT NULL,
api_token TEXT NOT NULL, -- TODO: Should be encrypted
max_domains INTEGER DEFAULT 100,
current_domain_count INTEGER DEFAULT 0,
notes TEXT,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
audit_logs
CREATE TABLE audit_logs (
id SERIAL PRIMARY KEY,
admin_id INTEGER REFERENCES admin_users(id),
action VARCHAR(100) NOT NULL,
resource_type VARCHAR(50), -- 'customer', 'plan', 'cf_account'
resource_id INTEGER,
details JSON,
ip_address VARCHAR(45),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_audit_logs_admin ON audit_logs(admin_id);
CREATE INDEX idx_audit_logs_action ON audit_logs(action);
CREATE INDEX idx_audit_logs_created ON audit_logs(created_at);
🏗️ Sistem Mimarisi
Infrastructure Components
Load Balancers
LB1: 185.199.108.153
LB2: 185.199.109.153
LB3: 185.199.110.153
Selection Algorithm: Hash-based
- MD5(domain_name) % 3
- Ensures same domain always routes to same LB
Cloudflare Configuration
DNS Proxy: Enabled (Orange Cloud)
SSL Mode: Full (Strict)
Always Use HTTPS: ON
Auto HTTPS Rewrites: ON
Minimum TLS Version: 1.2
HTTP/2: Enabled
HTTP/3 (QUIC): Enabled
Nginx Configuration
# Customer Frontend (argeict.net)
server {
listen 443 ssl http2;
server_name argeict.net;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
# Customer API (api.argeict.net)
server {
listen 443 ssl http2;
server_name api.argeict.net;
location / {
proxy_pass http://localhost:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Supervisor Configuration
[program:customer-api]
command=/path/to/venv/bin/gunicorn -w 4 -b 0.0.0.0:5000 app.main:app
directory=/path/to/MusteriPanel/backend
autostart=true
autorestart=true
stderr_logfile=/var/log/customer-api.err.log
stdout_logfile=/var/log/customer-api.out.log
[program:admin-api]
command=/path/to/venv/bin/gunicorn -w 4 -b 0.0.0.0:5001 app.main:app
directory=/path/to/AdminPanel/backend
autostart=true
autorestart=true
stderr_logfile=/var/log/admin-api.err.log
stdout_logfile=/var/log/admin-api.out.log
🔒 Güvenlik
Authentication & Authorization
JWT Token Structure
{
"user_id": 1,
"email": "user@example.com",
"role": "customer",
"exp": 1704067200,
"iat": 1704063600
}
Token Encryption (Cloudflare API Tokens)
from cryptography.fernet import Fernet
# Encryption
key = os.environ['ENCRYPTION_KEY']
f = Fernet(key)
encrypted_token = f.encrypt(api_token.encode())
# Decryption
decrypted_token = f.decrypt(encrypted_token).decode()
CORS Configuration
# Customer Panel
CORS(app, resources={
r"/api/*": {
"origins": ["https://argeict.net"],
"methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
"allow_headers": ["Content-Type", "Authorization"]
}
})
# Admin Panel
CORS(app, resources={
r"/api/*": {
"origins": ["https://admin.argeict.net"],
"methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
"allow_headers": ["Content-Type", "Authorization"]
}
})
Password Hashing
import bcrypt
# Hash password
password_hash = bcrypt.hashpw(
password.encode('utf-8'),
bcrypt.gensalt()
).decode('utf-8')
# Verify password
is_valid = bcrypt.checkpw(
password.encode('utf-8'),
password_hash.encode('utf-8')
)
Database Security
- Separate databases for Customer and Admin panels
- No direct database access between panels
- API-based communication only
- Encrypted sensitive data (API tokens)
- Regular backups
🚀 Deployment
Production Checklist
Environment Variables
# Customer Panel Backend
export FLASK_ENV=production
export DATABASE_URL=postgresql://user:pass@localhost/hosting_db
export JWT_SECRET_KEY=xxx
export ENCRYPTION_KEY=xxx
export REDIS_URL=redis://localhost:6379/0
export CLOUDFLARE_API_TOKEN=xxx
# Admin Panel Backend
export FLASK_ENV=production
export DATABASE_URL=postgresql://user:pass@localhost/admin_hosting_db
export JWT_SECRET_KEY=xxx
export CUSTOMER_API_URL=https://api.argeict.net
Build Frontend
# Customer Panel
cd MusteriPanel/frontend
npm install
npm run build
# Output: dist/
# Admin Panel
cd AdminPanel/frontend
npm install
npm run build
# Output: dist/
Database Migration
# Customer Panel
cd MusteriPanel/backend
flask db upgrade
# Admin Panel
cd AdminPanel/backend
flask db upgrade
Start Services
# Start all services
sudo supervisorctl start all
# Check status
sudo supervisorctl status
# Restart specific service
sudo supervisorctl restart customer-api
📊 Monitoring & Logging
Health Checks
GET /health
Response: {
"status": "healthy",
"service": "customer-panel",
"timestamp": "2024-01-01T10:00:00Z"
}
Log Files
Customer API: /var/log/customer-api.out.log
Admin API: /var/log/admin-api.out.log
Nginx Access: /var/log/nginx/access.log
Nginx Error: /var/log/nginx/error.log
Metrics to Monitor
- API response times
- Database query performance
- Cloudflare API rate limits
- Container resource usage
- Domain count per CF account
- Active user sessions
🔄 Common Workflows
Yeni Müşteri Onboarding
- Müşteri kayıt olur
- Email doğrulama (opsiyonel)
- Default "free" plan atanır
- Dashboard'a yönlendirilir
- İlk domain ekleme wizard'ı gösterilir
Domain Silme
- Müşteri domain sil butonuna tıklar
- Onay dialogu gösterilir
- DELETE /api/customer/domains/:id
- Backend:
- Domain kontrolü
- Container varsa sil
- DNS kayıtlarını sil (opsiyonel)
- CF account domain count güncelle
- Domain soft delete veya hard delete
- Frontend: Liste güncellenir
Plan Değiştirme (Admin)
- Admin müşteri detayına gider
- "Change Plan" butonuna tıklar
- Yeni plan seçer
- PUT /api/customers/:id/plan
- Backend:
- Plan limitleri günceller
- Subscription dates günceller
- AuditLog kaydeder
- Müşteri email bildirimi alır (opsiyonel)
📝 Notlar
Önemli Limitler
- JWT Token Expiration: 24 saat
- Max Domains per CF Account: 100 (yapılandırılabilir)
- API Rate Limit: 100 req/min per user
- File Upload Max Size: 10MB
- Database Connection Pool: 20
Gelecek Geliştirmeler
- Email verification sistemi
- Two-factor authentication
- Payment gateway integration (Stripe/PayPal)
- Automated backups
- Container auto-scaling
- Advanced analytics dashboard
- Multi-language support
- Mobile app
Son Güncelleme: 2024-01-12 Versiyon: 1.0.0 Hazırlayan: AI Assistant