This commit is contained in:
2025-08-04 20:43:47 +03:00
parent d5ed160746
commit 18b13fdb57
5 changed files with 188 additions and 3 deletions

33
src/api/accounts.py Normal file
View File

@ -0,0 +1,33 @@
from typing import Annotated
from fastapi import APIRouter, Depends, HTTPException, status
from psycopg2._psycopg import connection
import db.accounts as db
import settings.settings as settings
from api.utils import get_password_hash
from db.internal import get_db_connection
accounts_router = APIRouter(prefix="/api/accounts", tags=["anon"])
@accounts_router.post("/add/user")
async def add_account(
platform: str,
login: str,
password: str,
metadata: dict,
conn: Annotated[connection, Depends(get_db_connection)]
):
if not settings.settings.allow_create_users:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not allowed",
)
if db.check_account_existence(conn, platform, login, password):
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="Account already exists",
)
hashed_password = get_password_hash(password)
return db.create_user(conn, username, hashed_password, "user")

View File

@ -3,6 +3,9 @@ from typing import Annotated
import bcrypt import bcrypt
import jwt import jwt
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad
from fastapi import Depends, HTTPException, status from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer from fastapi.security import OAuth2PasswordBearer
from jwt.exceptions import InvalidTokenError from jwt.exceptions import InvalidTokenError
@ -17,6 +20,19 @@ from db.internal import get_db_connection
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
def encrypt_str(string, key_hex):
key = bytes.fromhex(key_hex)
cipher = AES.new(key, AES.MODE_ECB) # Простейший режим (небезопасно для больших данных!)
encrypted_string = cipher.encrypt(pad(string.encode(), AES.block_size))
return encrypted_string
def decrypt_str(encrypted_string, key_hex):
key = bytes.fromhex(key_hex)
cipher = AES.new(key, AES.MODE_ECB)
string = unpad(cipher.decrypt(encrypted_string), AES.block_size)
return string.decode()
def verify_password(plain_password: str, hashed_password: str): def verify_password(plain_password: str, hashed_password: str):
return bcrypt.checkpw(plain_password.encode("utf-8"), hashed_password.encode("utf-8")) return bcrypt.checkpw(plain_password.encode("utf-8"), hashed_password.encode("utf-8"))

128
src/db/accounts.py Normal file
View File

@ -0,0 +1,128 @@
import json
import psycopg2.extras
from psycopg2._psycopg import connection
# account create and delete
def create_account(
conn: connection,
platform: str,
login: str,
password: int,
metadata: dict
):
with conn.cursor() as cur:
cur.execute(
"""
insert into picrinth.accounts
(platform, login, password,
metadata, created_at)
values (%s, %s, %s, %s, now())
returning id
""",
(platform, login, password, json.dumps(metadata)),
)
conn.commit()
return cur.rowcount > 0
def delete_account(
conn: connection,
account_id: str
):
with conn.cursor() as cur:
cur.execute(
"""
delete from picrinth.accounts
where account_id = %s
""",
(account_id),
)
conn.commit()
return cur.rowcount > 0
# account checks
def check_account_existence_by_id(
conn: connection,
account_id: str
):
with conn.cursor() as cur:
cur.execute(
"""
select exists(
select 1
from picrinth.accounts
where account_id = %s
);
""",
(account_id),
)
return cur.fetchone()[0] # type: ignore
def check_account_existence(
conn: connection,
platform: str,
login: str,
password: str
):
with conn.cursor() as cur:
cur.execute(
"""
select exists(
select 1
from picrinth.accounts
where platform = %s, login = %s, password = %s
);
""",
(platform, login, password),
)
return cur.fetchone()[0] # type: ignore
# account update
def update_account(
conn: connection,
account_id: str,
platform: str,
login: str,
password: int,
metadata: dict
):
with conn.cursor() as cur:
cur.execute(
"""
update picrinth.accounts
SET platform = %s,
login = %s,
password = %s,
metadata = %s
where account_id = %s
""",
(platform, login, password, json.dumps(metadata), account_id),
)
conn.commit()
return cur.rowcount > 0
# account receiving
def get_account(
conn: connection,
account_id: str
):
with conn.cursor(cursor_factory=psycopg2.extras.DictCursor) as cur:
cur.execute(
"""
select username,
account_id, platform, login,
password, metadata, created_at
from picrinth.accounts
where account_id = %s
""",
(account_id),
)
return cur.fetchone()

View File

@ -1,3 +1,4 @@
from fastapi import HTTPException, status
from psycopg2._psycopg import connection from psycopg2._psycopg import connection
import db.pictures as db import db.pictures as db
@ -26,6 +27,12 @@ def generate_feed(
pixiv() pixiv()
case "gelbooru": case "gelbooru":
gelbooru() gelbooru()
case _:
return HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Platform is not supported",
)
# TODO: remove mock results # TODO: remove mock results
pictures_ids = db.get_all_pictures_ids(conn) pictures_ids = db.get_all_pictures_ids(conn)
return pictures_ids return pictures_ids

View File

@ -73,8 +73,9 @@ create table picrinth.accounts (
platform text not null, platform text not null,
login text not null, login text not null,
password text not null, password text not null,
metadata jsonb null, -- meybe not needed metadata jsonb null,
created_at timestamp with time zone default now(), created_at timestamp with time zone default now(),
primary key (id),
constraint unique_account_per_platform unique (platform, login) constraint unique_account_per_platform unique (platform, login)
); );
@ -82,7 +83,7 @@ create table picrinth.group_accounts (
groupname text not null, groupname text not null,
account_id int not null, account_id int not null,
created_at timestamp with time zone default now(), created_at timestamp with time zone default now(),
constraint unique_group_accounts unique (account_id, groupname) constraint unique_group_accounts unique (account_id, groupname),
foreign key (groupname) references picrinth.groups (groupname) on delete cascade on update cascade foreign key (groupname) references picrinth.groups (groupname) on delete cascade on update cascade,
foreign key (account_id) references picrinth.accounts (id) on delete cascade on update cascade foreign key (account_id) references picrinth.accounts (id) on delete cascade on update cascade
); );