mirror of
https://github.com/grey-cat-1908/formaptix-server.git
synced 2024-09-22 19:52:00 +03:00
иногда лучше закоммитить на всякий
This commit is contained in:
parent
7e4d9b7f77
commit
d9cf862040
13 changed files with 137 additions and 7 deletions
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 Viktor K. (mrkrk)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -2,6 +2,8 @@ from models import settings
|
|||
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker
|
||||
from sqlalchemy.orm import DeclarativeBase
|
||||
|
||||
from .user import *
|
||||
|
||||
engine = create_async_engine(settings.database)
|
||||
sessions = async_sessionmaker(engine)
|
||||
|
||||
|
|
11
database/user.py
Normal file
11
database/user.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from database import Base
|
||||
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = "users"
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
username: Mapped[str] = mapped_column(unique=True)
|
||||
password: Mapped[str]
|
||||
salt: Mapped[str]
|
|
@ -9,6 +9,8 @@ services:
|
|||
- DATABASE=postgresql+asyncpg://${DATABASE_USER}:${DATABASE_PASSWORD}@inputly-database:5432/${DATABASE_NAME}
|
||||
- SECRET=${SECRET}
|
||||
- PORT=${PORT}
|
||||
- ADMIN_PASSWORD=${ADMIN_PASSWORD}
|
||||
- DISABLE_ADMIN=${DISABLE_ADMIN}
|
||||
ports:
|
||||
- ${PORT}:${PORT}
|
||||
depends_on:
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
from fastapi import APIRouter
|
||||
|
||||
router = APIRouter()
|
4
main.py
4
main.py
|
@ -6,7 +6,7 @@ from fastapi import FastAPI
|
|||
|
||||
import models
|
||||
import database
|
||||
import endpoints
|
||||
import routes
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
|
@ -19,6 +19,6 @@ async def lifespan(app: FastAPI):
|
|||
yield
|
||||
|
||||
app = FastAPI(lifespan=lifespan)
|
||||
app.include_router(endpoints.router)
|
||||
app.include_router(routes.router)
|
||||
|
||||
uvicorn.run(app, host="0.0.0.0", port=models.settings.port)
|
||||
|
|
|
@ -1 +1,10 @@
|
|||
from .settings import *
|
||||
import pydantic
|
||||
|
||||
|
||||
class BaseModel(pydantic.BaseModel):
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
from .settings import settings
|
||||
from .user import *
|
||||
|
|
|
@ -5,6 +5,8 @@ class Settings(BaseSettings):
|
|||
database: str
|
||||
secret: str
|
||||
port: int
|
||||
admin_password: str
|
||||
disable_admin: bool
|
||||
|
||||
|
||||
settings = Settings()
|
||||
|
|
19
models/user.py
Normal file
19
models/user.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
from pydantic import Field
|
||||
|
||||
from models import BaseModel
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
id: int
|
||||
username: str
|
||||
|
||||
|
||||
class Auth(BaseModel):
|
||||
username: str
|
||||
password: str
|
||||
|
||||
|
||||
class Token(BaseModel):
|
||||
id: int
|
||||
username: str
|
||||
token: str
|
|
@ -3,7 +3,7 @@ name = "inputly-server"
|
|||
version = "0.1.0"
|
||||
description = "Simple forms service backend"
|
||||
authors = ["Viktor K. <tech@arbuz.icu>"]
|
||||
license = "APGL-3.0-only"
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
|
|
7
routes/__init__.py
Normal file
7
routes/__init__.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from fastapi import APIRouter
|
||||
|
||||
from . import admin
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
router.include_router(admin.router)
|
52
routes/admin.py
Normal file
52
routes/admin.py
Normal file
|
@ -0,0 +1,52 @@
|
|||
import secrets
|
||||
from typing import Annotated
|
||||
import hashlib
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy import select
|
||||
|
||||
import database
|
||||
from models import settings, user
|
||||
|
||||
router = APIRouter(prefix="/admin")
|
||||
|
||||
|
||||
def hash_password(password: str, salt: str) -> str:
|
||||
return hashlib.sha512((password + salt).encode('utf-8')).hexdigest()
|
||||
|
||||
|
||||
def verify_admin(token: str):
|
||||
if token != settings.admin_password:
|
||||
raise HTTPException(401, "Unauthorized")
|
||||
return True
|
||||
|
||||
|
||||
Admin = Annotated[bool, Depends(verify_admin, use_cache=False)]
|
||||
|
||||
|
||||
@router.post("/user")
|
||||
async def create_user(auth: user.Auth, admin_token: Admin):
|
||||
if len(auth.username.strip()) == 0:
|
||||
raise HTTPException(400, "Username must not be empty")
|
||||
if len(auth.password.strip()) == 0:
|
||||
raise HTTPException(400, "Password must not be empty")
|
||||
if settings.disable_admin:
|
||||
raise HTTPException(403, "You are not admin")
|
||||
|
||||
salt = secrets.token_hex(8)
|
||||
|
||||
async with database.sessions.begin() as session:
|
||||
stmt = select(database.User).where(database.User.username == auth.username)
|
||||
db_user = session.execute(stmt).scalar_one_or_none()
|
||||
if db_user is not None:
|
||||
raise HTTPException(400, "User with this username already exists")
|
||||
|
||||
new_user = database.User(
|
||||
username=auth.username,
|
||||
password=hash_password(auth.password, salt),
|
||||
salt=salt,
|
||||
)
|
||||
session.add(new_user)
|
||||
await session.flush()
|
||||
|
||||
return {'status': 'Success'}
|
8
routes/user.py
Normal file
8
routes/user.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
import hashlib
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
|
||||
import database
|
||||
|
||||
router = APIRouter(prefix="/user")
|
Loading…
Reference in a new issue