mirror of
https://github.com/fromchat-messenger/web.git
synced 2026-09-22 19:15:08 +03:00
Add an admin account
This commit is contained in:
@@ -2,3 +2,4 @@ DATABASE_URL = "sqlite:///./data/database.db"
|
|||||||
JWT_SECRET_KEY = "fromchat-jwt-secret"
|
JWT_SECRET_KEY = "fromchat-jwt-secret"
|
||||||
JWT_ALGORITHM = "HS256"
|
JWT_ALGORITHM = "HS256"
|
||||||
ACCESS_TOKEN_EXPIRE_HOURS = 24
|
ACCESS_TOKEN_EXPIRE_HOURS = 24
|
||||||
|
OWNER_USERNAME = "denis0001-dev"
|
||||||
@@ -3,6 +3,7 @@ from fastapi import APIRouter, Depends, HTTPException, status
|
|||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from routes.messaging import convert_message
|
from routes.messaging import convert_message
|
||||||
|
from constants import OWNER_USERNAME
|
||||||
from dependencies import get_current_user, get_db
|
from dependencies import get_current_user, get_db
|
||||||
from models import LoginRequest, RegisterRequest, User
|
from models import LoginRequest, RegisterRequest, User
|
||||||
from utils import create_token, get_password_hash, verify_password
|
from utils import create_token, get_password_hash, verify_password
|
||||||
@@ -16,14 +17,16 @@ def convert_user(user: User) -> dict:
|
|||||||
"created_at": user.created_at.isoformat(),
|
"created_at": user.created_at.isoformat(),
|
||||||
"last_seen": user.last_seen.isoformat(),
|
"last_seen": user.last_seen.isoformat(),
|
||||||
"online": user.online,
|
"online": user.online,
|
||||||
"username": user.username
|
"username": user.username,
|
||||||
|
"admin": user.username == OWNER_USERNAME
|
||||||
}
|
}
|
||||||
|
|
||||||
@router.get("/check_auth")
|
@router.get("/check_auth")
|
||||||
def check_auth(current_user: User = Depends(get_current_user)):
|
def check_auth(current_user: User = Depends(get_current_user)):
|
||||||
return {
|
return {
|
||||||
"authenticated": True,
|
"authenticated": True,
|
||||||
"username": current_user.username
|
"username": current_user.username,
|
||||||
|
"admin": current_user.username == OWNER_USERNAME
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -57,6 +60,16 @@ def register(request: RegisterRequest, db: Session = Depends(get_db)):
|
|||||||
password = request.password.strip()
|
password = request.password.strip()
|
||||||
confirm_password = request.confirm_password.strip()
|
confirm_password = request.confirm_password.strip()
|
||||||
|
|
||||||
|
# Determine if owner already exists
|
||||||
|
owner_exists = db.query(User).filter(User.username == OWNER_USERNAME).first() is not None
|
||||||
|
|
||||||
|
# If owner not yet registered, only allow the owner to register
|
||||||
|
if not owner_exists and username != OWNER_USERNAME:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail="Регистрация временно закрыта до регистрации владельца"
|
||||||
|
)
|
||||||
|
|
||||||
# Validate input
|
# Validate input
|
||||||
if not is_valid_username(username):
|
if not is_valid_username(username):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -76,6 +89,13 @@ def register(request: RegisterRequest, db: Session = Depends(get_db)):
|
|||||||
detail="Пароли не совпадают"
|
detail="Пароли не совпадают"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# After owner exists, disallow registering the reserved owner username via public registration
|
||||||
|
if owner_exists and username == OWNER_USERNAME:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail="Это имя пользователя зарезервировано"
|
||||||
|
)
|
||||||
|
|
||||||
existing_user = db.query(User).filter(User.username == username).first()
|
existing_user = db.query(User).filter(User.username == username).first()
|
||||||
if existing_user:
|
if existing_user:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -100,6 +120,34 @@ def register(request: RegisterRequest, db: Session = Depends(get_db)):
|
|||||||
"message": "Регистрация прошла успешно Теперь вы можете войти."
|
"message": "Регистрация прошла успешно Теперь вы можете войти."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/admin/user/{user_id}")
|
||||||
|
def delete_user_as_owner(
|
||||||
|
user_id: int,
|
||||||
|
current_user: User = Depends(get_current_user),
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
):
|
||||||
|
# Only owner can delete users
|
||||||
|
if current_user.username != OWNER_USERNAME:
|
||||||
|
raise HTTPException(status_code=403, detail="Only owner can perform this action")
|
||||||
|
|
||||||
|
user = db.query(User).filter(User.id == user_id).first()
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(status_code=404, detail="User not found")
|
||||||
|
|
||||||
|
# Prevent deleting the owner account via API
|
||||||
|
if user.username == OWNER_USERNAME:
|
||||||
|
raise HTTPException(status_code=400, detail="Cannot delete owner account")
|
||||||
|
|
||||||
|
# Manually delete user's messages to satisfy FK constraints
|
||||||
|
from models import Message # local import to avoid circular
|
||||||
|
db.query(Message).filter(Message.user_id == user.id).delete()
|
||||||
|
|
||||||
|
db.delete(user)
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
return {"status": "success", "deleted_user_id": user_id}
|
||||||
|
|
||||||
@router.get("/logout")
|
@router.get("/logout")
|
||||||
def logout(
|
def logout(
|
||||||
current_user: User = Depends(get_current_user),
|
current_user: User = Depends(get_current_user),
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from fastapi import APIRouter, Depends, HTTPException, WebSocket, WebSocketDisco
|
|||||||
from fastapi.security import HTTPAuthorizationCredentials
|
from fastapi.security import HTTPAuthorizationCredentials
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from dependencies import get_current_user, get_db
|
from dependencies import get_current_user, get_db
|
||||||
|
from constants import OWNER_USERNAME
|
||||||
from models import Message, SendMessageRequest, EditMessageRequest, ReplyMessageRequest, User
|
from models import Message, SendMessageRequest, EditMessageRequest, ReplyMessageRequest, User
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
@@ -105,7 +106,8 @@ async def delete_message(
|
|||||||
if not message:
|
if not message:
|
||||||
raise HTTPException(status_code=404, detail="Message not found")
|
raise HTTPException(status_code=404, detail="Message not found")
|
||||||
|
|
||||||
if message.user_id != current_user.id:
|
# Allow owner to delete any message
|
||||||
|
if current_user.username != OWNER_USERNAME and message.user_id != current_user.id:
|
||||||
raise HTTPException(status_code=403, detail="You can only delete your own messages")
|
raise HTTPException(status_code=403, detail="You can only delete your own messages")
|
||||||
|
|
||||||
db.delete(message)
|
db.delete(message)
|
||||||
|
|||||||
@@ -78,17 +78,15 @@ function bindEvents(): void {
|
|||||||
export function show(message: Message, x: number, y: number): void {
|
export function show(message: Message, x: number, y: number): void {
|
||||||
currentMessage = message;
|
currentMessage = message;
|
||||||
|
|
||||||
// Only show edit and delete for own messages
|
// Show delete for own messages and for owner on any message
|
||||||
const editItem = menu.querySelector('[data-action="edit"]') as HTMLElement;
|
const editItem = menu.querySelector('[data-action="edit"]') as HTMLElement;
|
||||||
const deleteItem = menu.querySelector('[data-action="delete"]') as HTMLElement;
|
const deleteItem = menu.querySelector('[data-action="delete"]') as HTMLElement;
|
||||||
|
|
||||||
if (message.username === currentUser?.username) {
|
const isAuthor = message.username === currentUser?.username;
|
||||||
editItem.style.display = 'flex';
|
const isOwner = !!currentUser?.admin;
|
||||||
deleteItem.style.display = 'flex';
|
|
||||||
} else {
|
editItem.style.display = isAuthor ? 'flex' : 'none';
|
||||||
editItem.style.display = 'none';
|
deleteItem.style.display = (isAuthor || isOwner) ? 'flex' : 'none';
|
||||||
deleteItem.style.display = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Position the menu properly
|
// Position the menu properly
|
||||||
menu.style.display = 'block';
|
menu.style.display = 'block';
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ export interface User {
|
|||||||
last_seen: string;
|
last_seen: string;
|
||||||
online: boolean;
|
online: boolean;
|
||||||
username: string;
|
username: string;
|
||||||
|
admin?: boolean;
|
||||||
bio?: string;
|
bio?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user