mirror of
https://github.com/fromchat-messenger/web.git
synced 2026-09-22 19:15:08 +03:00
Try fixing login/register
This commit is contained in:
@@ -156,7 +156,6 @@ async def proxy_moderation(path: str, request: Request):
|
||||
"""Proxy moderation service requests."""
|
||||
return await _proxy_to_service("moderation", path, request)
|
||||
|
||||
|
||||
async def _proxy_to_service(service: str, path: str, request: Request):
|
||||
"""Helper function to proxy requests to microservices."""
|
||||
from fastapi.responses import Response
|
||||
|
||||
@@ -147,7 +147,11 @@ def run_migrations():
|
||||
command.upgrade(alembic_cfg, "head")
|
||||
logger.info("Database migrations completed successfully.")
|
||||
except Exception as upgrade_error:
|
||||
if "Can't locate revision identified by 'direct_creation'" in str(upgrade_error):
|
||||
error_msg = str(upgrade_error)
|
||||
# Handle PostgreSQL "already exists" errors gracefully
|
||||
if "already exists" in error_msg.lower() or "relation" in error_msg.lower() and "exists" in error_msg.lower():
|
||||
pass
|
||||
elif "Can't locate revision identified by 'direct_creation'" in error_msg:
|
||||
logger.info("Found 'direct_creation' revision - resetting migration state...")
|
||||
# Clear the alembic_version table and start fresh
|
||||
engine = create_engine(DATABASE_URL)
|
||||
|
||||
+45
-80
@@ -10,7 +10,7 @@ from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||||
|
||||
from backend.shared.constants import OWNER_USERNAME
|
||||
from backend.shared.dependencies import get_current_user, get_db
|
||||
from backend.shared.models import LoginRequest, RegisterRequest, ChangePasswordRequest, User, CryptoPublicKey, CryptoBackup, DeviceSession
|
||||
from backend.shared.models import LoginRequest, RegisterRequest, ChangePasswordRequest, User, CryptoPublicKey, CryptoBackup
|
||||
from backend.shared.utils import create_token, get_password_hash, verify_password, get_client_ip
|
||||
from backend.shared.validation import is_valid_password, is_valid_username, is_valid_display_name
|
||||
import os
|
||||
@@ -47,16 +47,16 @@ def convert_user(user: User) -> dict:
|
||||
"id": user.id,
|
||||
"created_at": user.created_at.isoformat(),
|
||||
"last_seen": user.last_seen.isoformat(),
|
||||
"online": user.online,
|
||||
"online": user.is_online,
|
||||
"username": user.username,
|
||||
"display_name": user.display_name,
|
||||
"profile_picture": user.profile_picture,
|
||||
"profile_picture": user.avatar_url,
|
||||
"bio": user.bio,
|
||||
"admin": _is_admin(user),
|
||||
"verified": user.verified,
|
||||
"suspended": user.suspended or False,
|
||||
"suspended": user.suspended,
|
||||
"suspension_reason": user.suspension_reason,
|
||||
"deleted": (user.deleted or user.suspended) or False # Treat suspended as deleted
|
||||
"deleted": user.deleted
|
||||
}
|
||||
|
||||
@router.get("/check_auth")
|
||||
@@ -77,7 +77,7 @@ def login(request: Request, login_request: LoginRequest, db: Session = Depends(g
|
||||
|
||||
user = db.query(User).filter(User.username == username).first()
|
||||
|
||||
if not user or not verify_password(login_request.password.strip(), user.password_hash):
|
||||
if not user or not verify_password(login_request.password.strip(), user.hashed_password):
|
||||
log_security(
|
||||
"login_failed",
|
||||
severity="warning",
|
||||
@@ -112,30 +112,9 @@ def login(request: Request, login_request: LoginRequest, db: Session = Depends(g
|
||||
detail="Неверное имя пользователя или пароль"
|
||||
)
|
||||
|
||||
# Create device session and embed into JWT
|
||||
raw_ua = request.headers.get("user-agent")
|
||||
device_name = request.headers.get("x-device-name")
|
||||
ua = parse_ua(raw_ua or "")
|
||||
# Generate session ID for JWT (device session will be created on first device service access)
|
||||
session_id = uuid.uuid4().hex
|
||||
|
||||
device = DeviceSession(
|
||||
user_id=user.id,
|
||||
raw_user_agent=raw_ua,
|
||||
device_name=device_name,
|
||||
device_type=("mobile" if ua.is_mobile else "tablet" if ua.is_tablet else "bot" if ua.is_bot else "desktop"),
|
||||
os_name=(ua.os.family or None),
|
||||
os_version=(ua.os.version_string or None),
|
||||
browser_name=(ua.browser.family or None),
|
||||
browser_version=(ua.browser.version_string or None),
|
||||
brand=(ua.device.brand or None),
|
||||
model=(ua.device.model or None),
|
||||
session_id=session_id,
|
||||
created_at=datetime.now(),
|
||||
last_seen=datetime.now(),
|
||||
revoked=False,
|
||||
)
|
||||
db.add(device)
|
||||
|
||||
user.online = True
|
||||
user.last_seen = datetime.now()
|
||||
db.commit()
|
||||
@@ -148,15 +127,19 @@ def login(request: Request, login_request: LoginRequest, db: Session = Depends(g
|
||||
for identifier in identifiers:
|
||||
_reset_failed_logins(identifier)
|
||||
|
||||
# Parse user agent for logging
|
||||
ua = parse_ua(raw_ua or "")
|
||||
device_type = "mobile" if ua.is_mobile else "tablet" if ua.is_tablet else "bot" if ua.is_bot else "desktop"
|
||||
|
||||
log_security(
|
||||
"login_success",
|
||||
username=user.username,
|
||||
user_id=user.id,
|
||||
ip=client_ip,
|
||||
session_id=session_id,
|
||||
device=device.device_type,
|
||||
os=device.os_name,
|
||||
browser=device.browser_name,
|
||||
device=device_type,
|
||||
os=ua.os.family,
|
||||
browser=ua.browser.family,
|
||||
)
|
||||
|
||||
return {
|
||||
@@ -230,8 +213,9 @@ def register(request: Request, register_request: RegisterRequest, db: Session =
|
||||
new_user = User(
|
||||
username=username,
|
||||
display_name=display_name,
|
||||
password_hash=hashed_password,
|
||||
online=True,
|
||||
hashed_password=hashed_password,
|
||||
salt="", # Not used since bcrypt includes salt in hash
|
||||
is_online=True,
|
||||
last_seen=datetime.now(),
|
||||
verified=is_owner
|
||||
)
|
||||
@@ -240,32 +224,13 @@ def register(request: Request, register_request: RegisterRequest, db: Session =
|
||||
db.commit()
|
||||
db.refresh(new_user)
|
||||
|
||||
# Create initial device session
|
||||
raw_ua = request.headers.get("user-agent")
|
||||
device_name = request.headers.get("x-device-name")
|
||||
ua = parse_ua(raw_ua or "")
|
||||
# Generate a temporary session ID for the token (device session will be created on first device service access)
|
||||
session_id = uuid.uuid4().hex
|
||||
device = DeviceSession(
|
||||
user_id=new_user.id,
|
||||
raw_user_agent=raw_ua,
|
||||
device_name=device_name,
|
||||
device_type=("mobile" if ua.is_mobile else "tablet" if ua.is_tablet else "bot" if ua.is_bot else "desktop"),
|
||||
os_name=(ua.os.family or None),
|
||||
os_version=(ua.os.version_string or None),
|
||||
browser_name=(ua.browser.family or None),
|
||||
browser_version=(ua.browser.version_string or None),
|
||||
brand=(ua.device.brand or None),
|
||||
model=(ua.device.model or None),
|
||||
session_id=session_id,
|
||||
created_at=datetime.now(),
|
||||
last_seen=datetime.now(),
|
||||
revoked=False,
|
||||
)
|
||||
db.add(device)
|
||||
db.commit()
|
||||
|
||||
token = create_token(new_user.id, new_user.username, session_id)
|
||||
|
||||
# Parse user agent for logging
|
||||
raw_ua = request.headers.get("user-agent")
|
||||
ua = parse_ua(raw_ua or "")
|
||||
os_name = ua.os.family or "Unknown OS"
|
||||
if ua.os.version_string:
|
||||
os_name = f"{os_name} {ua.os.version_string}"
|
||||
@@ -380,14 +345,14 @@ def logout(
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
# Revoke current session
|
||||
from backend.shared.utils import verify_token as _verify_token
|
||||
payload = _verify_token(credentials.credentials)
|
||||
if payload and payload.get("session_id"):
|
||||
db.query(DeviceSession).filter(
|
||||
DeviceSession.user_id == current_user.id,
|
||||
DeviceSession.session_id == payload["session_id"],
|
||||
).update({DeviceSession.revoked: True})
|
||||
# Revoke current session - TODO: Move to device service
|
||||
# from backend.shared.utils import verify_token as _verify_token
|
||||
# payload = _verify_token(credentials.credentials)
|
||||
# if payload and payload.get("session_id"):
|
||||
# db.query(DeviceSession).filter(
|
||||
# DeviceSession.user_id == current_user.id,
|
||||
# DeviceSession.session_id == payload["session_id"],
|
||||
# ).update({DeviceSession.revoked: True})
|
||||
|
||||
current_user.online = False
|
||||
current_user.last_seen = datetime.now()
|
||||
@@ -399,7 +364,7 @@ def logout(
|
||||
username=current_user.username,
|
||||
user_id=current_user.id,
|
||||
ip=client_ip,
|
||||
session_id=payload.get("session_id") if payload else None,
|
||||
session_id=None, # TODO: Get session_id from device service
|
||||
)
|
||||
|
||||
return {
|
||||
@@ -418,25 +383,25 @@ def change_password(
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
# Verify current derived password against stored hash
|
||||
if not verify_password(password_request.currentPasswordDerived.strip(), current_user.password_hash):
|
||||
if not verify_password(password_request.currentPasswordDerived.strip(), current_user.hashed_password):
|
||||
raise HTTPException(status_code=401, detail="Текущий пароль неверный")
|
||||
|
||||
# Update password hash to hash of new derived password
|
||||
current_user.password_hash = get_password_hash(password_request.newPasswordDerived.strip())
|
||||
current_user.hashed_password = get_password_hash(password_request.newPasswordDerived.strip())
|
||||
db.commit()
|
||||
|
||||
# Optionally revoke all other sessions, keeping the current one
|
||||
if password_request.logoutAllExceptCurrent:
|
||||
from backend.shared.utils import verify_token as _verify_token
|
||||
payload = _verify_token(credentials.credentials)
|
||||
if not payload:
|
||||
raise HTTPException(status_code=401, detail="Invalid token")
|
||||
current_session_id = payload.get("session_id")
|
||||
db.query(DeviceSession).filter(
|
||||
DeviceSession.user_id == current_user.id,
|
||||
DeviceSession.session_id != current_session_id,
|
||||
).update({DeviceSession.revoked: True})
|
||||
db.commit()
|
||||
# Optionally revoke all other sessions, keeping the current one - TODO: Move to device service
|
||||
# if password_request.logoutAllExceptCurrent:
|
||||
# from backend.shared.utils import verify_token as _verify_token
|
||||
# payload = _verify_token(credentials.credentials)
|
||||
# if not payload:
|
||||
# raise HTTPException(status_code=401, detail="Invalid token")
|
||||
# current_session_id = payload.get("session_id")
|
||||
# db.query(DeviceSession).filter(
|
||||
# DeviceSession.user_id == current_user.id,
|
||||
# DeviceSession.session_id != current_session_id,
|
||||
# ).update({DeviceSession.revoked: True})
|
||||
# db.commit()
|
||||
|
||||
client_ip = get_client_ip(request)
|
||||
log_security(
|
||||
@@ -496,7 +461,7 @@ async def _delete_user_data(user: User, db: Session):
|
||||
user.deleted = True
|
||||
user.display_name = f"Deleted User #{user_id}"
|
||||
user.bio = None
|
||||
user.password_hash = ""
|
||||
user.hashed_password = ""
|
||||
user.username = f"deleted_{user_id}"
|
||||
user.profile_picture = None
|
||||
user.last_seen = None # Clear last seen timestamp
|
||||
|
||||
@@ -13,7 +13,6 @@ class User(Base):
|
||||
|
||||
id = Column(BigInteger, primary_key=True, index=True)
|
||||
username = Column(String(50), unique=True, index=True, nullable=False)
|
||||
email = Column(String(100), unique=True, index=True, nullable=False)
|
||||
hashed_password = Column(String(255), nullable=False)
|
||||
salt = Column(String(64), nullable=False)
|
||||
display_name = Column(String(100), nullable=True)
|
||||
@@ -34,6 +33,9 @@ class User(Base):
|
||||
public_key = Column(Text, nullable=True)
|
||||
private_key = Column(Text, nullable=True)
|
||||
encryption_enabled = Column(Boolean, default=False)
|
||||
suspended = Column(Boolean, default=False)
|
||||
suspension_reason = Column(Text, nullable=True)
|
||||
deleted = Column(Boolean, default=False)
|
||||
|
||||
# Relationships
|
||||
messages = relationship("Message", back_populates="sender", cascade="all, delete-orphan")
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
-- This script creates dedicated users with limited privileges for each service
|
||||
|
||||
-- Create service-specific database roles with limited privileges
|
||||
-- All services use the same password from DB_PASSWORD environment variable
|
||||
CREATE ROLE account_service_user LOGIN PASSWORD '${DB_PASSWORD}';
|
||||
CREATE ROLE profile_service_user LOGIN PASSWORD '${DB_PASSWORD}';
|
||||
CREATE ROLE device_service_user LOGIN PASSWORD '${DB_PASSWORD}';
|
||||
CREATE ROLE messaging_service_user LOGIN PASSWORD '${DB_PASSWORD}';
|
||||
CREATE ROLE push_service_user LOGIN PASSWORD '${DB_PASSWORD}';
|
||||
CREATE ROLE webrtc_service_user LOGIN PASSWORD '${DB_PASSWORD}';
|
||||
CREATE ROLE moderation_service_user LOGIN PASSWORD '${DB_PASSWORD}';
|
||||
CREATE ROLE gateway_user LOGIN PASSWORD '${DB_PASSWORD}';
|
||||
-- All services use the same password
|
||||
CREATE ROLE account_service_user LOGIN PASSWORD 'development';
|
||||
CREATE ROLE profile_service_user LOGIN PASSWORD 'development';
|
||||
CREATE ROLE device_service_user LOGIN PASSWORD 'development';
|
||||
CREATE ROLE messaging_service_user LOGIN PASSWORD 'development';
|
||||
CREATE ROLE push_service_user LOGIN PASSWORD 'development';
|
||||
CREATE ROLE webrtc_service_user LOGIN PASSWORD 'development';
|
||||
CREATE ROLE moderation_service_user LOGIN PASSWORD 'development';
|
||||
CREATE ROLE gateway_user LOGIN PASSWORD 'development';
|
||||
|
||||
-- Create dedicated schemas for each service
|
||||
CREATE SCHEMA IF NOT EXISTS account_schema AUTHORIZATION account_service_user;
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
CREATE TABLE IF NOT EXISTS account_schema.users (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
username VARCHAR(50) UNIQUE NOT NULL,
|
||||
email VARCHAR(100) UNIQUE NOT NULL,
|
||||
hashed_password VARCHAR(255) NOT NULL,
|
||||
salt VARCHAR(64) NOT NULL,
|
||||
display_name VARCHAR(100),
|
||||
@@ -28,7 +27,10 @@ CREATE TABLE IF NOT EXISTS account_schema.users (
|
||||
locked_until TIMESTAMP,
|
||||
public_key TEXT,
|
||||
private_key TEXT,
|
||||
encryption_enabled BOOLEAN DEFAULT FALSE
|
||||
encryption_enabled BOOLEAN DEFAULT FALSE,
|
||||
suspended BOOLEAN DEFAULT FALSE,
|
||||
suspension_reason TEXT,
|
||||
deleted BOOLEAN DEFAULT FALSE
|
||||
);
|
||||
|
||||
-- Profile schema tables (references account_schema.users)
|
||||
@@ -54,6 +56,24 @@ CREATE TABLE IF NOT EXISTS device_schema.devices (
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS device_schema.device_session (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id BIGINT REFERENCES account_schema.users(id) ON DELETE CASCADE,
|
||||
raw_user_agent TEXT,
|
||||
device_name VARCHAR(128),
|
||||
device_type VARCHAR(32),
|
||||
os_name VARCHAR(64),
|
||||
os_version VARCHAR(64),
|
||||
browser_name VARCHAR(64),
|
||||
browser_version VARCHAR(64),
|
||||
brand VARCHAR(64),
|
||||
model VARCHAR(64),
|
||||
session_id VARCHAR(64) UNIQUE NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
last_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
revoked BOOLEAN DEFAULT FALSE
|
||||
);
|
||||
|
||||
-- Messaging schema tables
|
||||
CREATE TABLE IF NOT EXISTS messaging_schema.messages (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
@@ -125,3 +145,14 @@ CREATE TABLE IF NOT EXISTS moderation_schema.moderation_actions (
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
expires_at TIMESTAMP
|
||||
);
|
||||
|
||||
-- Grant permissions on sequences (after all tables are created)
|
||||
GRANT USAGE ON SEQUENCE account_schema.users_id_seq TO account_service_user;
|
||||
GRANT USAGE ON SEQUENCE device_schema.devices_id_seq TO device_service_user;
|
||||
GRANT USAGE ON SEQUENCE device_schema.device_session_id_seq TO account_service_user;
|
||||
GRANT USAGE ON SEQUENCE messaging_schema.messages_id_seq TO messaging_service_user;
|
||||
GRANT USAGE ON SEQUENCE messaging_schema.message_recipients_id_seq TO messaging_service_user;
|
||||
GRANT USAGE ON SEQUENCE messaging_schema.message_reactions_id_seq TO messaging_service_user;
|
||||
GRANT USAGE ON SEQUENCE push_schema.push_subscriptions_id_seq TO push_service_user;
|
||||
GRANT USAGE ON SEQUENCE webrtc_schema.webrtc_sessions_id_seq TO webrtc_service_user;
|
||||
GRANT USAGE ON SEQUENCE moderation_schema.moderation_actions_id_seq TO moderation_service_user;
|
||||
|
||||
@@ -5,7 +5,7 @@ services:
|
||||
environment:
|
||||
POSTGRES_DB: fromchat
|
||||
POSTGRES_USER: fromchat_admin
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD:-changeme}
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
volumes:
|
||||
- database:/var/lib/postgresql/data
|
||||
- ./db-init:/docker-entrypoint-initdb.d
|
||||
@@ -25,8 +25,8 @@ services:
|
||||
dockerfile: docker/Dockerfile.multi
|
||||
target: migration_runner
|
||||
environment:
|
||||
DATABASE_URL: postgresql://fromchat_admin:${DB_PASSWORD:-changeme}@database:5432/fromchat
|
||||
JWT_SECRET: ${JWT_SECRET:-changeme}
|
||||
DATABASE_URL: postgresql://fromchat_admin:${DB_PASSWORD}@database:5432/fromchat
|
||||
JWT_SECRET: ${JWT_SECRET}
|
||||
depends_on:
|
||||
database:
|
||||
condition: service_healthy
|
||||
@@ -58,7 +58,7 @@ services:
|
||||
target: gateway
|
||||
ports: ["8300:8300"]
|
||||
environment:
|
||||
DATABASE_URL: postgresql://gateway_user:${DB_PASSWORD:-changeme}@database:5432/fromchat
|
||||
DATABASE_URL: postgresql://gateway_user:${DB_PASSWORD}@database:5432/fromchat
|
||||
PORT: 8300
|
||||
ACCOUNT_SERVICE_URL: http://account_service:8302
|
||||
PROFILE_SERVICE_URL: http://profile_service:8303
|
||||
@@ -102,8 +102,8 @@ services:
|
||||
dockerfile: docker/Dockerfile.multi
|
||||
target: account_service
|
||||
environment:
|
||||
DATABASE_URL: postgresql://account_service_user:${DB_PASSWORD:-changeme}@database:5432/fromchat
|
||||
JWT_SECRET: ${JWT_SECRET:-changeme}
|
||||
DATABASE_URL: postgresql://account_service_user:${DB_PASSWORD}@database:5432/fromchat
|
||||
JWT_SECRET: ${JWT_SECRET}
|
||||
PORT: 8302
|
||||
depends_on:
|
||||
migration_runner:
|
||||
@@ -130,11 +130,11 @@ services:
|
||||
dockerfile: docker/Dockerfile.multi
|
||||
target: profile_service
|
||||
environment:
|
||||
DATABASE_URL: postgresql://profile_service_user:${DB_PASSWORD:-changeme}@database:5432/fromchat
|
||||
FIREBASE_CERT: ${FIREBASE_CERT:-}
|
||||
VAPID_PRIVATE_KEY: ${VAPID_PRIVATE_KEY:-}
|
||||
VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY:-}
|
||||
VAPID_SUBJECT: ${VAPID_SUBJECT:-mailto:admin@example.com}
|
||||
DATABASE_URL: postgresql://profile_service_user:${DB_PASSWORD}@database:5432/fromchat
|
||||
FIREBASE_CERT: ${FIREBASE_CERT}
|
||||
VAPID_PRIVATE_KEY: ${VAPID_PRIVATE_KEY}
|
||||
VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY}
|
||||
VAPID_SUBJECT: ${VAPID_SUBJECT}
|
||||
MESSAGING_SERVICE_URL: http://messaging_service:8305
|
||||
PORT: 8303
|
||||
depends_on:
|
||||
@@ -162,7 +162,7 @@ services:
|
||||
dockerfile: docker/Dockerfile.multi
|
||||
target: device_service
|
||||
environment:
|
||||
DATABASE_URL: postgresql://device_service_user:${DB_PASSWORD:-changeme}@database:5432/fromchat
|
||||
DATABASE_URL: postgresql://device_service_user:${DB_PASSWORD}@database:5432/fromchat
|
||||
PORT: 8304
|
||||
depends_on:
|
||||
migration_runner:
|
||||
@@ -191,11 +191,11 @@ services:
|
||||
ports:
|
||||
- "8305:8305"
|
||||
environment:
|
||||
DATABASE_URL: postgresql://messaging_service_user:${DB_PASSWORD:-changeme}@database:5432/fromchat
|
||||
FIREBASE_CERT: ${FIREBASE_CERT:-}
|
||||
VAPID_PRIVATE_KEY: ${VAPID_PRIVATE_KEY:-}
|
||||
VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY:-}
|
||||
VAPID_SUBJECT: ${VAPID_SUBJECT:-mailto:admin@example.com}
|
||||
DATABASE_URL: postgresql://messaging_service_user:${DB_PASSWORD}@database:5432/fromchat
|
||||
FIREBASE_CERT: ${FIREBASE_CERT}
|
||||
VAPID_PRIVATE_KEY: ${VAPID_PRIVATE_KEY}
|
||||
VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY}
|
||||
VAPID_SUBJECT: ${VAPID_SUBJECT}
|
||||
PUSH_SERVICE_URL: http://push_service:8306
|
||||
PORT: 8305
|
||||
depends_on:
|
||||
@@ -226,11 +226,11 @@ services:
|
||||
dockerfile: docker/Dockerfile.multi
|
||||
target: push_service
|
||||
environment:
|
||||
DATABASE_URL: postgresql://push_service_user:${DB_PASSWORD:-changeme}@database:5432/fromchat
|
||||
FIREBASE_CERT: ${FIREBASE_CERT:-}
|
||||
VAPID_PRIVATE_KEY: ${VAPID_PRIVATE_KEY:-}
|
||||
VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY:-}
|
||||
VAPID_SUBJECT: ${VAPID_SUBJECT:-mailto:admin@example.com}
|
||||
DATABASE_URL: postgresql://push_service_user:${DB_PASSWORD}@database:5432/fromchat
|
||||
FIREBASE_CERT: ${FIREBASE_CERT}
|
||||
VAPID_PRIVATE_KEY: ${VAPID_PRIVATE_KEY}
|
||||
VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY}
|
||||
VAPID_SUBJECT: ${VAPID_SUBJECT}
|
||||
PORT: 8306
|
||||
depends_on:
|
||||
migration_runner:
|
||||
@@ -260,7 +260,7 @@ services:
|
||||
dockerfile: docker/Dockerfile.multi
|
||||
target: webrtc_service
|
||||
environment:
|
||||
DATABASE_URL: postgresql://webrtc_service_user:${DB_PASSWORD:-changeme}@database:5432/fromchat
|
||||
DATABASE_URL: postgresql://webrtc_service_user:${DB_PASSWORD}@database:5432/fromchat
|
||||
PORT: 8307
|
||||
depends_on:
|
||||
migration_runner:
|
||||
@@ -287,7 +287,7 @@ services:
|
||||
dockerfile: docker/Dockerfile.multi
|
||||
target: moderation_service
|
||||
environment:
|
||||
DATABASE_URL: postgresql://moderation_service_user:${DB_PASSWORD:-changeme}@database:5432/fromchat
|
||||
DATABASE_URL: postgresql://moderation_service_user:${DB_PASSWORD}@database:5432/fromchat
|
||||
PORT: 8308
|
||||
depends_on:
|
||||
migration_runner:
|
||||
@@ -348,6 +348,7 @@ services:
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- fromchat_external
|
||||
- fromchat_internal
|
||||
|
||||
volumes:
|
||||
database:
|
||||
|
||||
@@ -8,15 +8,7 @@ const port = Number(process.env.PORT) || 8301;
|
||||
const backendHost = process.env.BACKEND_HOST || "http://localhost:8300";
|
||||
const filePath = process.env.STATIC_FILE_PATH || ".";
|
||||
|
||||
// API proxy middleware
|
||||
app.use('/api', createProxyMiddleware({
|
||||
target: backendHost,
|
||||
changeOrigin: true,
|
||||
pathRewrite: { '^/api': '' },
|
||||
ws: true
|
||||
}));
|
||||
|
||||
// Direct WebSocket proxy for chat - bypass gateway
|
||||
// Direct WebSocket proxy for chat - bypass gateway (must come before general API proxy)
|
||||
app.use('/api/chat/ws', createProxyMiddleware({
|
||||
target: 'http://messaging_service:8305',
|
||||
changeOrigin: true,
|
||||
@@ -24,6 +16,20 @@ app.use('/api/chat/ws', createProxyMiddleware({
|
||||
ws: true
|
||||
}));
|
||||
|
||||
// API proxy middleware (exclude WebSocket paths)
|
||||
app.use('/api', (req, res, next) => {
|
||||
// Skip WebSocket upgrade requests - let them be handled by specific proxies
|
||||
if (req.headers.upgrade === 'websocket') {
|
||||
return next();
|
||||
}
|
||||
createProxyMiddleware({
|
||||
target: backendHost,
|
||||
changeOrigin: true,
|
||||
pathRewrite: { '^/api': '' },
|
||||
ws: true
|
||||
})(req, res, next);
|
||||
});
|
||||
|
||||
// Serve static files
|
||||
app.use(express.static(resolve(filePath)));
|
||||
|
||||
|
||||
@@ -5,9 +5,11 @@ echo > deployment/.env
|
||||
./.venv/bin/python3 backend/generate_vapid_keys.py >> deployment/.env
|
||||
|
||||
cat >> deployment/.env <<EOF
|
||||
VAPID_SUBJECT=mailto:support@fromchat.ru
|
||||
JWT_SECRET="$(openssl rand -base64 32)"
|
||||
TURN_USERNAME=<set>
|
||||
TURN_SECRET=<set>
|
||||
DEPLOYMENT_SERVER=<set>
|
||||
FIREBASE_CERT=<set>
|
||||
DB_PASSWORD=development
|
||||
EOF
|
||||
|
||||
Reference in New Issue
Block a user