mirror of
https://github.com/grey-cat-1908/formaptix-server.git
synced 2024-09-22 19:52:00 +03:00
cors support
This commit is contained in:
parent
1a8f3e2980
commit
3620ea07a3
8 changed files with 57 additions and 12 deletions
|
@ -1,7 +1,12 @@
|
||||||
DATABASE_USER=admin
|
DATABASE_USER=admin
|
||||||
DATABASE_PASSWORD=password
|
DATABASE_PASSWORD=password
|
||||||
DATABASE_NAME=formaptix
|
DATABASE_NAME=formaptix
|
||||||
|
|
||||||
PORT=3000
|
PORT=3000
|
||||||
SECRET=super_secret
|
SECRET=super_secret
|
||||||
|
|
||||||
|
FRONTEND_HOST="http://example.com"
|
||||||
|
BACKEND_CORS_ORIGINS="http://localhost,http://localhost:5173"
|
||||||
|
|
||||||
ADMIN_PASSWORD=admin_password
|
ADMIN_PASSWORD=admin_password
|
||||||
DISABLE_ADMIN=false
|
DISABLE_ADMIN=false
|
|
@ -2,7 +2,7 @@ from models import settings
|
||||||
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker
|
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker
|
||||||
from sqlalchemy.orm import DeclarativeBase
|
from sqlalchemy.orm import DeclarativeBase
|
||||||
|
|
||||||
engine = create_async_engine(settings.database)
|
engine = create_async_engine(settings.DATABASE)
|
||||||
sessions = async_sessionmaker(engine)
|
sessions = async_sessionmaker(engine)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,8 @@ services:
|
||||||
- PORT=${PORT}
|
- PORT=${PORT}
|
||||||
- ADMIN_PASSWORD=${ADMIN_PASSWORD}
|
- ADMIN_PASSWORD=${ADMIN_PASSWORD}
|
||||||
- DISABLE_ADMIN=${DISABLE_ADMIN}
|
- DISABLE_ADMIN=${DISABLE_ADMIN}
|
||||||
|
- BACKEND_CORS_ORIGINS=${BACKEND_CORS_ORIGINS}
|
||||||
|
- FRONTEND_HOST=${FRONTEND_HOST}
|
||||||
ports:
|
ports:
|
||||||
- ${PORT}:${PORT}
|
- ${PORT}:${PORT}
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|
12
main.py
12
main.py
|
@ -3,6 +3,7 @@ from contextlib import asynccontextmanager
|
||||||
|
|
||||||
import uvicorn
|
import uvicorn
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
|
||||||
import models
|
import models
|
||||||
import database
|
import database
|
||||||
|
@ -22,4 +23,13 @@ async def lifespan(app: FastAPI):
|
||||||
app = FastAPI(lifespan=lifespan)
|
app = FastAPI(lifespan=lifespan)
|
||||||
app.include_router(routes.router)
|
app.include_router(routes.router)
|
||||||
|
|
||||||
uvicorn.run(app, host="0.0.0.0", port=models.settings.port)
|
if models.settings.all_cors_origins:
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=models.settings.all_cors_origins,
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
uvicorn.run(app, host="0.0.0.0", port=models.settings.PORT)
|
||||||
|
|
|
@ -1,12 +1,40 @@
|
||||||
from pydantic_settings import BaseSettings
|
from typing import Annotated, Any
|
||||||
|
|
||||||
|
from pydantic import AnyUrl, BeforeValidator, computed_field
|
||||||
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
|
|
||||||
|
|
||||||
|
def parse_cors(v: Any) -> list[str] | str:
|
||||||
|
if isinstance(v, str) and not v.startswith("["):
|
||||||
|
return [i.strip() for i in v.split(",")]
|
||||||
|
elif isinstance(v, list | str):
|
||||||
|
return v
|
||||||
|
raise ValueError(v)
|
||||||
|
|
||||||
|
|
||||||
class Settings(BaseSettings):
|
class Settings(BaseSettings):
|
||||||
database: str
|
model_config = SettingsConfigDict(
|
||||||
secret: str
|
env_file=".env", env_ignore_empty=True, extra="ignore"
|
||||||
port: int
|
)
|
||||||
admin_password: str
|
|
||||||
disable_admin: bool
|
DATABASE: str
|
||||||
|
SECRET: str
|
||||||
|
PORT: int
|
||||||
|
|
||||||
|
FRONTEND_HOST: str = "http://localhost:5173"
|
||||||
|
BACKEND_CORS_ORIGINS: Annotated[list[AnyUrl] | str, BeforeValidator(parse_cors)] = (
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
|
@computed_field # type: ignore[prop-decorator]
|
||||||
|
@property
|
||||||
|
def all_cors_origins(self) -> list[str]:
|
||||||
|
return [str(origin).rstrip("/") for origin in self.BACKEND_CORS_ORIGINS] + [
|
||||||
|
self.FRONTEND_HOST
|
||||||
|
]
|
||||||
|
|
||||||
|
ADMIN_PASSWORD: str
|
||||||
|
DISABLE_ADMIN: bool = False
|
||||||
|
|
||||||
|
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
|
|
|
@ -16,7 +16,7 @@ async def create_user(auth: user.Auth, admin_token: Admin):
|
||||||
raise HTTPException(400, "Username must not be empty")
|
raise HTTPException(400, "Username must not be empty")
|
||||||
if len(auth.password.strip()) == 0:
|
if len(auth.password.strip()) == 0:
|
||||||
raise HTTPException(400, "Password must not be empty")
|
raise HTTPException(400, "Password must not be empty")
|
||||||
if settings.disable_admin:
|
if settings.DISABLE_ADMIN:
|
||||||
raise HTTPException(403, "You are not admin")
|
raise HTTPException(403, "You are not admin")
|
||||||
|
|
||||||
salt = secrets.token_hex(8)
|
salt = secrets.token_hex(8)
|
||||||
|
|
|
@ -31,7 +31,7 @@ async def login(auth: models.Auth) -> models.Token:
|
||||||
id=user.id,
|
id=user.id,
|
||||||
username=user.username,
|
username=user.username,
|
||||||
token=jwt.encode(
|
token=jwt.encode(
|
||||||
{"sub": user.id}, settings.secret + user.password, "HS256"
|
{"sub": user.id}, settings.SECRET + user.password, "HS256"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ def hash_password(password: str, salt: str) -> str:
|
||||||
|
|
||||||
|
|
||||||
def verify_admin(password: Annotated[str, Header(alias="x-token")]):
|
def verify_admin(password: Annotated[str, Header(alias="x-token")]):
|
||||||
if password.strip() != settings.admin_password:
|
if password.strip() != settings.ADMIN_PASSWORD:
|
||||||
raise HTTPException(401, "Unauthorized")
|
raise HTTPException(401, "Unauthorized")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ async def verify_user(token: Annotated[str, Header(alias="x-token")]) -> databas
|
||||||
raise HTTPException(401, "Invalid token")
|
raise HTTPException(401, "Invalid token")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
jwt.decode(token, settings.secret + user.password, algorithms=["HS256"])
|
jwt.decode(token, settings.SECRET + user.password, algorithms=["HS256"])
|
||||||
except jwt.exceptions.InvalidSignatureError:
|
except jwt.exceptions.InvalidSignatureError:
|
||||||
raise HTTPException(401, "Invalid token")
|
raise HTTPException(401, "Invalid token")
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue