3 Commits

60 changed files with 465 additions and 1910 deletions

View File

@@ -1,15 +1,15 @@
import sys
import logging import logging
import sys
import loguru import loguru
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from uvicorn import run from uvicorn import run
from api.config import get_settings, DefaultSettings from api.config import DefaultSettings, get_settings
from api.endpoints import list_of_routes from api.endpoints import list_of_routes
from api.utils.common import get_hostname
from api.services.middleware import MiddlewareAccessTokenValidadtion from api.services.middleware import MiddlewareAccessTokenValidadtion
from api.utils.common import get_hostname
logger = logging.getLogger() logger = logging.getLogger()
logger.setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG)
@@ -52,7 +52,6 @@ prod_origins = [""]
origins = dev_origins if get_settings().ENV == "local" else prod_origins origins = dev_origins if get_settings().ENV == "local" else prod_origins
if __name__ == "__main__": if __name__ == "__main__":
settings_for_application = get_settings() settings_for_application = get_settings()
if settings_for_application.ENV == "prod": if settings_for_application.ENV == "prod":

View File

@@ -1,7 +0,0 @@
from sqlalchemy import MetaData
metadata = MetaData()
__all__ = [
"metadata",
]

View File

@@ -7,7 +7,7 @@ from sqlalchemy import pool
from alembic import context from alembic import context
from api.db import metadata, tables from orm import metadata, tables
# this is the Alembic Config object, which provides # this is the Alembic Config object, which provides
# access to the values within the .ini file in use. # access to the values within the .ini file in use.

View File

@@ -6,7 +6,7 @@ from typing import Optional
from sqlalchemy import insert, select, func, or_, and_, asc, desc from sqlalchemy import insert, select, func, or_, and_, asc, desc
from sqlalchemy.ext.asyncio import AsyncConnection from sqlalchemy.ext.asyncio import AsyncConnection
from api.db.tables.account import account_table from orm.tables.account import account_table
from api.schemas.account.account import User from api.schemas.account.account import User
from api.schemas.endpoints.account import all_user_adapter, AllUser, AllUserResponse, UserCreate, UserFilterDTO from api.schemas.endpoints.account import all_user_adapter, AllUser, AllUserResponse, UserCreate, UserFilterDTO

View File

@@ -4,7 +4,7 @@ from sqlalchemy import select, update
from sqlalchemy.ext.asyncio import AsyncConnection from sqlalchemy.ext.asyncio import AsyncConnection
from enum import Enum from enum import Enum
from api.db.tables.account import account_table, account_keyring_table, KeyType, KeyStatus from orm.tables.account import account_table, account_keyring_table, KeyType, KeyStatus
from api.schemas.account.account import User from api.schemas.account.account import User
from api.schemas.account.account_keyring import AccountKeyring from api.schemas.account.account_keyring import AccountKeyring

View File

@@ -6,7 +6,7 @@ from sqlalchemy import insert, select, update
from sqlalchemy.dialects.mysql import insert as mysql_insert from sqlalchemy.dialects.mysql import insert as mysql_insert
from sqlalchemy.ext.asyncio import AsyncConnection from sqlalchemy.ext.asyncio import AsyncConnection
from api.db.tables.account import account_keyring_table, KeyStatus, KeyType from orm.tables.account import account_keyring_table, KeyStatus, KeyType
from api.schemas.account.account_keyring import AccountKeyring from api.schemas.account.account_keyring import AccountKeyring
from api.utils.hasher import hasher from api.utils.hasher import hasher

View File

@@ -7,7 +7,7 @@ from datetime import datetime, timezone
from sqlalchemy import insert, select, func, or_, and_, asc, desc from sqlalchemy import insert, select, func, or_, and_, asc, desc
from sqlalchemy.ext.asyncio import AsyncConnection from sqlalchemy.ext.asyncio import AsyncConnection
from api.db.tables.events import list_events_table from orm.tables.events import list_events_table
from api.schemas.events.list_events import ListEvent from api.schemas.events.list_events import ListEvent

View File

@@ -6,7 +6,7 @@ from datetime import datetime, timezone
from sqlalchemy import insert, select, func, or_, and_, asc, desc from sqlalchemy import insert, select, func, or_, and_, asc, desc
from sqlalchemy.ext.asyncio import AsyncConnection from sqlalchemy.ext.asyncio import AsyncConnection
from api.db.tables.process import process_schema_table from orm.tables.process import process_schema_table
from api.schemas.process.process_schema import ProcessSchema from api.schemas.process.process_schema import ProcessSchema

View File

@@ -1,18 +0,0 @@
__all__ = ["BigIntegerPK", "SAEnum", "UnsignedInt"]
from typing import Any
from sqlalchemy import BigInteger, Enum, Integer
from sqlalchemy.dialects import mysql
# class SAEnum(Enum):
# def __init__(self, *enums: object, **kw: Any):
# validate_strings = kw.pop("validate_strings", True)
# super().__init__(*enums, **kw, validate_strings=validate_strings)
# # https://docs.sqlalchemy.org/en/20/dialects/sqlite.html#allowing-autoincrement-behavior-sqlalchemy-types-other-than-integer-integer
# BigIntegerPK = BigInteger().with_variant(Integer, "sqlite")
UnsignedInt = Integer().with_variant(mysql.INTEGER(unsigned=True), "mysql")

View File

@@ -1 +0,0 @@
from . import account, events, process

View File

@@ -1,65 +0,0 @@
import enum
from sqlalchemy import Table, Column, String, Enum as SQLAEnum, JSON, ForeignKey, DateTime, Index
from sqlalchemy.sql import func
from api.db.sql_types import UnsignedInt
from api.db import metadata
class AccountRole(enum.StrEnum):
OWNER = "OWNER"
ADMIN = "ADMIN"
EDITOR = "EDITOR"
VIEWER = "VIEWER"
class AccountStatus(enum.StrEnum):
ACTIVE = "ACTIVE"
DISABLED = "DISABLED"
BLOCKED = "BLOCKED"
DELETED = "DELETED"
account_table = Table(
"account",
metadata,
Column("id", UnsignedInt, primary_key=True, autoincrement=True),
Column("name", String(100), nullable=False),
Column("login", String(100), nullable=False),
Column("email", String(100), nullable=True),
Column("bind_tenant_id", String(40), nullable=True),
Column("role", SQLAEnum(AccountRole), nullable=False),
Column("meta", JSON, default={}),
Column("creator_id", UnsignedInt, ForeignKey("account.id"), nullable=True),
Column("created_at", DateTime(timezone=True), server_default=func.now()),
Column("status", SQLAEnum(AccountStatus), nullable=False),
Index("idx_login", "login"),
Index("idx_name", "name"),
)
class KeyType(enum.StrEnum):
PASSWORD = "PASSWORD"
ACCESS_TOKEN = "ACCESS_TOKEN"
REFRESH_TOKEN = "REFRESH_TOKEN"
API_KEY = "API_KEY"
class KeyStatus(enum.StrEnum):
ACTIVE = "ACTIVE"
EXPIRED = "EXPIRED"
DELETED = "DELETED"
account_keyring_table = Table(
"account_keyring",
metadata,
Column("owner_id", UnsignedInt, ForeignKey("account.id"), primary_key=True, nullable=False),
Column("key_type", SQLAEnum(KeyType), primary_key=True, nullable=False),
Column("key_id", String(40), primary_key=True, default=None),
Column("key_value", String(512), nullable=False),
Column("created_at", DateTime(timezone=True), server_default=func.now()),
Column("expiry", DateTime(timezone=True), nullable=True),
Column("status", SQLAEnum(KeyStatus), nullable=False),
)

View File

@@ -1,33 +0,0 @@
import enum
from sqlalchemy import Table, Column, String, Enum as SQLAEnum, JSON, ForeignKey, DateTime
from sqlalchemy.sql import func
from api.db.sql_types import UnsignedInt
from api.db import metadata
class EventState(enum.StrEnum):
AUTO = "AUTO"
DESCRIPTED = "DESCRIPTED"
class EventStatus(enum.StrEnum):
ACTIVE = "ACTIVE"
DISABLED = "DISABLED"
DELETED = "DELETED"
list_events_table = Table(
"list_events",
metadata,
Column("id", UnsignedInt, primary_key=True, autoincrement=True),
Column("name", String(40, collation="latin1_bin"), nullable=False, unique=True),
Column("title", String(64), nullable=False),
Column("creator_id", UnsignedInt, ForeignKey("account.id"), nullable=False),
Column("created_at", DateTime(timezone=True), server_default=func.now()),
Column("schema", JSON, default={}),
Column("state", SQLAEnum(EventState), nullable=False),
Column("status", SQLAEnum(EventStatus), nullable=False),
)

View File

@@ -1,107 +0,0 @@
import enum
from sqlalchemy import (
Table,
Column,
String,
Text,
Enum as SQLAEnum,
JSON,
ForeignKey,
DateTime,
Index,
PrimaryKeyConstraint,
)
from sqlalchemy.sql import func
from enum import Enum
from api.db.sql_types import UnsignedInt
from api.db import metadata
class ProcessStatus(enum.StrEnum):
ACTIVE = "ACTIVE"
STOPPING = "STOPPING"
STOPPED = "STOPPED"
DELETED = "DELETED"
process_schema_table = Table(
"process_schema",
metadata,
Column("id", UnsignedInt, primary_key=True, autoincrement=True),
Column("title", String(100), nullable=False),
Column("description", Text, nullable=False),
Column("owner_id", UnsignedInt, ForeignKey("account.id"), nullable=False),
Column("creator_id", UnsignedInt, ForeignKey("account.id"), nullable=False),
Column("created_at", DateTime(timezone=True), server_default=func.now()),
Column("settings", JSON, default={}),
Column("status", SQLAEnum(ProcessStatus), nullable=False),
Index(
"idx_owner_id",
"owner_id",
),
)
process_version_archive_table = Table(
"process_version_archive",
metadata,
Column("id", UnsignedInt, autoincrement=True, nullable=False),
Column("ps_id", UnsignedInt, ForeignKey("process_schema.id"), nullable=False),
Column("version", UnsignedInt, default=1, nullable=False),
Column("snapshot", JSON, default={}),
Column("owner_id", UnsignedInt, ForeignKey("account.id"), nullable=False),
Column("created_at", DateTime(timezone=True), server_default=func.now()),
Column("is_last", UnsignedInt, default=0),
PrimaryKeyConstraint("id", "version"),
)
class NodeStatus(enum.StrEnum):
ACTIVE = "ACTIVE"
DISABLED = "DISABLED"
DELETED = "DELETED"
class NodeType(Enum):
TYPE1 = "Type1"
TYPE2 = "Type2"
TYPE3 = "Type3"
ps_node_table = Table(
"ps_node",
metadata,
Column("id", UnsignedInt, autoincrement=True, primary_key=True, nullable=False),
Column("ps_id", UnsignedInt, ForeignKey("process_schema.id"), nullable=False),
Column("node_type", SQLAEnum(NodeType), nullable=False),
Column("settings", JSON, default={}),
Column("creator_id", UnsignedInt, ForeignKey("account.id"), nullable=False),
Column("created_at", DateTime(timezone=True), server_default=func.now()),
Column("status", SQLAEnum(NodeStatus), nullable=False),
Index("idx_ps_id", "ps_id"),
)
class NodeLinkStatus(enum.StrEnum):
ACTIVE = "ACTIVE"
STOPPING = "STOPPING"
STOPPED = "STOPPED"
DELETED = "DELETED"
node_link_table = Table(
"node_link",
metadata,
Column("id", UnsignedInt, autoincrement=True, primary_key=True, nullable=False),
Column("link_name", String(20), nullable=False),
Column("node_id", UnsignedInt, ForeignKey("ps_node.id"), nullable=False),
Column("next_node_id", UnsignedInt, ForeignKey("ps_node.id"), nullable=False),
Column("settings", JSON, default={}),
Column("creator_id", UnsignedInt, ForeignKey("account.id"), nullable=False),
Column("created_at", DateTime(timezone=True), server_default=func.now()),
Column("status", SQLAEnum(NodeLinkStatus), nullable=False),
Index("idx_node_id", "node_id"),
Index("idx_next_node_id", "next_node_id"),
)

View File

@@ -1,6 +1,7 @@
from fastapi import APIRouter, Depends, HTTPException, status, Query from typing import List, Optional
from typing import Optional, List from fastapi import APIRouter, Depends, HTTPException, Query, status
from orm.tables.account import AccountStatus
from sqlalchemy.ext.asyncio import AsyncConnection from sqlalchemy.ext.asyncio import AsyncConnection
from api.db.connection.session import get_connection_dep from api.db.connection.session import get_connection_dep
@@ -12,10 +13,9 @@ from api.db.logic.account import (
update_user_by_id, update_user_by_id,
) )
from api.db.logic.keyring import create_password_key, update_password_key from api.db.logic.keyring import create_password_key, update_password_key
from api.db.tables.account import AccountStatus
from api.schemas.account.account import User from api.schemas.account.account import User
from api.schemas.base import bearer_schema from api.schemas.base import bearer_schema
from api.schemas.endpoints.account import AllUserResponse, UserCreate, UserUpdate, UserFilterDTO from api.schemas.endpoints.account import AllUserResponse, UserCreate, UserFilterDTO, UserUpdate
from api.services.auth import get_current_user from api.services.auth import get_current_user
from api.services.user_role_validation import db_user_role_validation from api.services.user_role_validation import db_user_role_validation

View File

@@ -1,31 +1,20 @@
from fastapi import ( from fastapi import (
APIRouter, APIRouter,
Body,
Depends, Depends,
Form,
HTTPException, HTTPException,
Response,
status, status,
) )
from orm.tables.account import KeyStatus
from sqlalchemy.ext.asyncio import AsyncConnection from sqlalchemy.ext.asyncio import AsyncConnection
from api.db.connection.session import get_connection_dep from api.db.connection.session import get_connection_dep
from api.db.logic.keyring import create_key, get_key_by_id, update_key_by_id
from api.db.logic.keyring import get_key_by_id, create_key, update_key_by_id from api.schemas.account.account_keyring import AccountKeyring
from api.db.tables.account import KeyStatus
from api.schemas.base import bearer_schema from api.schemas.base import bearer_schema
from api.schemas.endpoints.account_keyring import AccountKeyringUpdate from api.schemas.endpoints.account_keyring import AccountKeyringUpdate
from api.schemas.account.account_keyring import AccountKeyring
from api.services.auth import get_current_user from api.services.auth import get_current_user
from api.services.user_role_validation import db_user_role_validation from api.services.user_role_validation import db_user_role_validation
api_router = APIRouter( api_router = APIRouter(
prefix="/keyring", prefix="/keyring",
tags=["User KeyringModel"], tags=["User KeyringModel"],

View File

@@ -1,37 +1,27 @@
from fastapi import APIRouter, Depends, HTTPException, status, Query from typing import List, Optional
from typing import Optional, List
from fastapi import APIRouter, Depends, HTTPException, Query, status
from orm.tables.events import EventStatus
from sqlalchemy.ext.asyncio import AsyncConnection from sqlalchemy.ext.asyncio import AsyncConnection
from api.db.connection.session import get_connection_dep from api.db.connection.session import get_connection_dep
from api.db.logic.account import get_user_by_login from api.db.logic.account import get_user_by_login
from api.db.logic.list_events import ( from api.db.logic.list_events import (
get_list_events_by_name,
get_list_events_by_id,
create_list_events, create_list_events,
update_list_events_by_id, get_list_events_by_id,
get_list_events_by_name,
get_list_events_page_DTO, get_list_events_page_DTO,
update_list_events_by_id,
) )
from api.schemas.events.list_events import ListEvent
from api.db.tables.events import EventStatus
from api.schemas.base import bearer_schema from api.schemas.base import bearer_schema
from api.schemas.endpoints.list_events import AllListEventResponse, ListEventFilterDTO, ListEventUpdate
from api.schemas.endpoints.list_events import ListEventUpdate, AllListEventResponse, ListEventFilterDTO from api.schemas.events.list_events import ListEvent
from api.services.auth import get_current_user from api.services.auth import get_current_user
from api.services.user_role_validation import ( from api.services.user_role_validation import (
db_user_role_validation_for_list_events_and_process_schema_by_list_event_id,
db_user_role_validation_for_list_events_and_process_schema, db_user_role_validation_for_list_events_and_process_schema,
db_user_role_validation_for_list_events_and_process_schema_by_list_event_id,
) )
api_router = APIRouter( api_router = APIRouter(
prefix="/list_events", prefix="/list_events",
tags=["list events"], tags=["list events"],

View File

@@ -1,36 +1,27 @@
from fastapi import APIRouter, Depends, HTTPException, status, Query from typing import List, Optional
from typing import Optional, List from fastapi import APIRouter, Depends, HTTPException, Query, status
from orm.tables.process import ProcessStatus
from sqlalchemy.ext.asyncio import AsyncConnection from sqlalchemy.ext.asyncio import AsyncConnection
from api.db.connection.session import get_connection_dep from api.db.connection.session import get_connection_dep
from api.db.logic.account import get_user_by_login from api.db.logic.account import get_user_by_login
from api.db.logic.process_schema import ( from api.db.logic.process_schema import (
get_process_schema_by_title,
create_process_schema, create_process_schema,
get_process_schema_by_id, get_process_schema_by_id,
update_process_schema_by_id, get_process_schema_by_title,
get_process_schema_page_DTO, get_process_schema_page_DTO,
update_process_schema_by_id,
) )
from api.schemas.process.process_schema import ProcessSchema
from api.db.tables.process import ProcessStatus
from api.schemas.base import bearer_schema from api.schemas.base import bearer_schema
from api.schemas.endpoints.process_schema import AllProcessSchemaResponse, ProcessSchemaFilterDTO, ProcessSchemaUpdate
from api.schemas.endpoints.process_schema import ProcessSchemaUpdate, AllProcessSchemaResponse, ProcessSchemaFilterDTO from api.schemas.process.process_schema import ProcessSchema
from api.services.auth import get_current_user from api.services.auth import get_current_user
from api.services.user_role_validation import ( from api.services.user_role_validation import (
db_user_role_validation_for_list_events_and_process_schema_by_list_event_id,
db_user_role_validation_for_list_events_and_process_schema, db_user_role_validation_for_list_events_and_process_schema,
db_user_role_validation_for_list_events_and_process_schema_by_list_event_id,
) )
api_router = APIRouter( api_router = APIRouter(
prefix="/process_schema", prefix="/process_schema",
tags=["process schema"], tags=["process schema"],

View File

@@ -4,18 +4,14 @@ from fastapi import (
HTTPException, HTTPException,
status, status,
) )
from sqlalchemy.ext.asyncio import AsyncConnection from sqlalchemy.ext.asyncio import AsyncConnection
from api.db.connection.session import get_connection_dep from api.db.connection.session import get_connection_dep
from api.db.logic.account import get_user_by_id, update_user_by_id, get_user_by_login from api.db.logic.account import get_user_by_id, get_user_by_login, update_user_by_id
from api.schemas.base import bearer_schema
from api.services.auth import get_current_user
from api.schemas.endpoints.account import UserUpdate
from api.schemas.account.account import User from api.schemas.account.account import User
from api.schemas.base import bearer_schema
from api.schemas.endpoints.account import UserUpdate
from api.services.auth import get_current_user
api_router = APIRouter( api_router = APIRouter(
prefix="/profile", prefix="/profile",

View File

@@ -1,8 +1,8 @@
import datetime
from datetime import datetime from datetime import datetime
from typing import Optional from typing import Optional
from orm.tables.account import AccountRole, AccountStatus
from pydantic import EmailStr, Field from pydantic import EmailStr, Field
from api.db.tables.account import AccountRole, AccountStatus
from api.schemas.base import Base from api.schemas.base import Base

View File

@@ -1,8 +1,8 @@
import datetime
from typing import Optional
from pydantic import Field
from datetime import datetime from datetime import datetime
from api.db.tables.account import KeyType, KeyStatus from typing import Optional
from orm.tables.account import KeyStatus, KeyType
from pydantic import Field
from api.schemas.base import Base from api.schemas.base import Base

View File

@@ -1,9 +1,9 @@
from datetime import datetime from datetime import datetime
from typing import List, Optional, Dict from typing import Dict, List, Optional
from orm.tables.account import AccountRole, AccountStatus
from pydantic import EmailStr, Field, TypeAdapter from pydantic import EmailStr, Field, TypeAdapter
from api.db.tables.account import AccountRole, AccountStatus
from api.schemas.base import Base from api.schemas.base import Base

View File

@@ -1,6 +1,8 @@
from typing import Optional from typing import Optional
from orm.tables.account import KeyStatus, KeyType
from pydantic import Field from pydantic import Field
from api.db.tables.account import KeyType, KeyStatus
from api.schemas.base import Base from api.schemas.base import Base

View File

@@ -1,5 +1,6 @@
from api.schemas.base import Base from api.schemas.base import Base
# Таблица для получения информации из запроса # Таблица для получения информации из запроса

View File

@@ -1,10 +1,10 @@
from pydantic import Field, TypeAdapter
from typing import Optional, Dict, Any, List
from datetime import datetime from datetime import datetime
from typing import Any, Dict, List, Optional
from orm.tables.events import EventState, EventStatus
from pydantic import Field, TypeAdapter
from api.schemas.base import Base from api.schemas.base import Base
from api.db.tables.events import EventState, EventStatus
class ListEventUpdate(Base): class ListEventUpdate(Base):

View File

@@ -1,10 +1,10 @@
from pydantic import Field, TypeAdapter
from typing import Optional, Dict, Any, List
from datetime import datetime from datetime import datetime
from typing import Any, Dict, List, Optional
from orm.tables.process import ProcessStatus
from pydantic import Field, TypeAdapter
from api.schemas.base import Base from api.schemas.base import Base
from api.db.tables.process import ProcessStatus
class ProcessSchemaUpdate(Base): class ProcessSchemaUpdate(Base):

View File

@@ -1,9 +1,10 @@
from pydantic import Field
from typing import Dict, Any
from datetime import datetime from datetime import datetime
from typing import Any, Dict
from orm.tables.events import EventState, EventStatus
from pydantic import Field
from api.schemas.base import Base from api.schemas.base import Base
from api.db.tables.events import EventState, EventStatus
class ListEvent(Base): class ListEvent(Base):

View File

@@ -1,9 +1,10 @@
from pydantic import Field
from typing import Dict, Any
from datetime import datetime from datetime import datetime
from typing import Any, Dict
from orm.tables.process import NodeStatus
from pydantic import Field
from api.schemas.base import Base from api.schemas.base import Base
from api.db.tables.process import NodeStatus
class MyModel(Base): class MyModel(Base):

View File

@@ -1,9 +1,10 @@
from pydantic import Field
from typing import Dict, Any
from datetime import datetime from datetime import datetime
from typing import Any, Dict
from orm.tables.process import ProcessStatus
from pydantic import Field
from api.schemas.base import Base from api.schemas.base import Base
from api.db.tables.process import ProcessStatus
class ProcessSchema(Base): class ProcessSchema(Base):

View File

@@ -1,5 +1,5 @@
from typing import Dict, Any
from datetime import datetime from datetime import datetime
from typing import Any, Dict
from api.schemas.base import Base from api.schemas.base import Base

View File

@@ -1,8 +1,9 @@
from datetime import datetime from datetime import datetime
from typing import Dict, Any from typing import Any, Dict
from orm.tables.process import NodeStatus, NodeType
from api.schemas.base import Base from api.schemas.base import Base
from api.db.tables.process import NodeType, NodeStatus
class Ps_Node(Base): class Ps_Node(Base):

View File

@@ -1,10 +1,10 @@
from typing import Optional from typing import Optional
from fastapi import HTTPException, Request from fastapi import HTTPException, Request
from orm.tables.account import AccountStatus
from sqlalchemy.ext.asyncio import AsyncConnection from sqlalchemy.ext.asyncio import AsyncConnection
from api.db.logic.auth import get_user from api.db.logic.auth import get_user
from api.db.tables.account import AccountStatus
from api.schemas.endpoints.account import AllUser from api.schemas.endpoints.account import AllUser
from api.utils.hasher import hasher from api.utils.hasher import hasher

View File

@@ -1,15 +1,15 @@
from fastapi_jwt_auth import AuthJWT import re
from starlette.middleware.base import BaseHTTPMiddleware from re import escape
from fastapi import ( from fastapi import (
Request, Request,
status, status,
) )
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from api.config import get_settings from fastapi_jwt_auth import AuthJWT
from starlette.middleware.base import BaseHTTPMiddleware
import re from api.config import get_settings
from re import escape
class MiddlewareAccessTokenValidadtion(BaseHTTPMiddleware): class MiddlewareAccessTokenValidadtion(BaseHTTPMiddleware):

View File

@@ -2,8 +2,9 @@ from fastapi import (
HTTPException, HTTPException,
status, status,
) )
from orm.tables.account import AccountRole
from api.db.logic.account import get_user_by_login from api.db.logic.account import get_user_by_login
from api.db.tables.account import AccountRole
async def db_user_role_validation(connection, current_user): async def db_user_role_validation(connection, current_user):

View File

@@ -1,8 +1,9 @@
import asyncio import asyncio
import os import os
from orm.tables.account import account_keyring_table, account_table, AccountRole, KeyStatus, KeyType
from api.db.connection.session import get_connection from api.db.connection.session import get_connection
from api.db.tables.account import account_keyring_table, account_table, AccountRole, KeyStatus, KeyType
from api.utils.hasher import hasher from api.utils.hasher import hasher
from api.utils.key_id_gen import KeyIdGenerator from api.utils.key_id_gen import KeyIdGenerator

153
api/poetry.lock generated
View File

@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. # This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand.
[[package]] [[package]]
name = "aio-pika" name = "aio-pika"
@@ -227,6 +227,25 @@ files = [
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
] ]
[[package]]
name = "core-library"
version = "0.1.0"
description = "Abstract classes library for the Vorkout project"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = []
develop = false
[package.dependencies]
sqlalchemy = ">=2.0.43,<3.0.0"
[package.source]
type = "git"
url = "https://gitea.heado.ru/Vorkout/core.git"
reference = "0.1.0"
resolved_reference = "96ddb52582660600dbacead4919e67f948e96898"
[[package]] [[package]]
name = "cryptography" name = "cryptography"
version = "44.0.2" version = "44.0.2"
@@ -1416,83 +1435,83 @@ files = [
[[package]] [[package]]
name = "sqlalchemy" name = "sqlalchemy"
version = "2.0.39" version = "2.0.43"
description = "Database Abstraction Library" description = "Database Abstraction Library"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
groups = ["main"] groups = ["main"]
files = [ files = [
{file = "SQLAlchemy-2.0.39-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:66a40003bc244e4ad86b72abb9965d304726d05a939e8c09ce844d27af9e6d37"}, {file = "SQLAlchemy-2.0.43-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:21ba7a08a4253c5825d1db389d4299f64a100ef9800e4624c8bf70d8f136e6ed"},
{file = "SQLAlchemy-2.0.39-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67de057fbcb04a066171bd9ee6bcb58738d89378ee3cabff0bffbf343ae1c787"}, {file = "SQLAlchemy-2.0.43-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11b9503fa6f8721bef9b8567730f664c5a5153d25e247aadc69247c4bc605227"},
{file = "SQLAlchemy-2.0.39-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:533e0f66c32093a987a30df3ad6ed21170db9d581d0b38e71396c49718fbb1ca"}, {file = "SQLAlchemy-2.0.43-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07097c0a1886c150ef2adba2ff7437e84d40c0f7dcb44a2c2b9c905ccfc6361c"},
{file = "SQLAlchemy-2.0.39-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7399d45b62d755e9ebba94eb89437f80512c08edde8c63716552a3aade61eb42"}, {file = "SQLAlchemy-2.0.43-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:cdeff998cb294896a34e5b2f00e383e7c5c4ef3b4bfa375d9104723f15186443"},
{file = "SQLAlchemy-2.0.39-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:788b6ff6728072b313802be13e88113c33696a9a1f2f6d634a97c20f7ef5ccce"}, {file = "SQLAlchemy-2.0.43-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:bcf0724a62a5670e5718957e05c56ec2d6850267ea859f8ad2481838f889b42c"},
{file = "SQLAlchemy-2.0.39-cp37-cp37m-win32.whl", hash = "sha256:01da15490c9df352fbc29859d3c7ba9cd1377791faeeb47c100832004c99472c"}, {file = "SQLAlchemy-2.0.43-cp37-cp37m-win32.whl", hash = "sha256:c697575d0e2b0a5f0433f679bda22f63873821d991e95a90e9e52aae517b2e32"},
{file = "SQLAlchemy-2.0.39-cp37-cp37m-win_amd64.whl", hash = "sha256:f2bcb085faffcacf9319b1b1445a7e1cfdc6fb46c03f2dce7bc2d9a4b3c1cdc5"}, {file = "SQLAlchemy-2.0.43-cp37-cp37m-win_amd64.whl", hash = "sha256:d34c0f6dbefd2e816e8f341d0df7d4763d382e3f452423e752ffd1e213da2512"},
{file = "SQLAlchemy-2.0.39-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b761a6847f96fdc2d002e29e9e9ac2439c13b919adfd64e8ef49e75f6355c548"}, {file = "sqlalchemy-2.0.43-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:70322986c0c699dca241418fcf18e637a4369e0ec50540a2b907b184c8bca069"},
{file = "SQLAlchemy-2.0.39-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0d7e3866eb52d914aea50c9be74184a0feb86f9af8aaaa4daefe52b69378db0b"}, {file = "sqlalchemy-2.0.43-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:87accdbba88f33efa7b592dc2e8b2a9c2cdbca73db2f9d5c510790428c09c154"},
{file = "SQLAlchemy-2.0.39-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:995c2bacdddcb640c2ca558e6760383dcdd68830160af92b5c6e6928ffd259b4"}, {file = "sqlalchemy-2.0.43-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c00e7845d2f692ebfc7d5e4ec1a3fd87698e4337d09e58d6749a16aedfdf8612"},
{file = "SQLAlchemy-2.0.39-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:344cd1ec2b3c6bdd5dfde7ba7e3b879e0f8dd44181f16b895940be9b842fd2b6"}, {file = "sqlalchemy-2.0.43-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:022e436a1cb39b13756cf93b48ecce7aa95382b9cfacceb80a7d263129dfd019"},
{file = "SQLAlchemy-2.0.39-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:5dfbc543578058c340360f851ddcecd7a1e26b0d9b5b69259b526da9edfa8875"}, {file = "sqlalchemy-2.0.43-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c5e73ba0d76eefc82ec0219d2301cb33bfe5205ed7a2602523111e2e56ccbd20"},
{file = "SQLAlchemy-2.0.39-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3395e7ed89c6d264d38bea3bfb22ffe868f906a7985d03546ec7dc30221ea980"}, {file = "sqlalchemy-2.0.43-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9c2e02f06c68092b875d5cbe4824238ab93a7fa35d9c38052c033f7ca45daa18"},
{file = "SQLAlchemy-2.0.39-cp38-cp38-win32.whl", hash = "sha256:bf555f3e25ac3a70c67807b2949bfe15f377a40df84b71ab2c58d8593a1e036e"}, {file = "sqlalchemy-2.0.43-cp310-cp310-win32.whl", hash = "sha256:e7a903b5b45b0d9fa03ac6a331e1c1d6b7e0ab41c63b6217b3d10357b83c8b00"},
{file = "SQLAlchemy-2.0.39-cp38-cp38-win_amd64.whl", hash = "sha256:463ecfb907b256e94bfe7bcb31a6d8c7bc96eca7cbe39803e448a58bb9fcad02"}, {file = "sqlalchemy-2.0.43-cp310-cp310-win_amd64.whl", hash = "sha256:4bf0edb24c128b7be0c61cd17eef432e4bef507013292415f3fb7023f02b7d4b"},
{file = "sqlalchemy-2.0.39-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6827f8c1b2f13f1420545bd6d5b3f9e0b85fe750388425be53d23c760dcf176b"}, {file = "sqlalchemy-2.0.43-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:52d9b73b8fb3e9da34c2b31e6d99d60f5f99fd8c1225c9dad24aeb74a91e1d29"},
{file = "sqlalchemy-2.0.39-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9f119e7736967c0ea03aff91ac7d04555ee038caf89bb855d93bbd04ae85b41"}, {file = "sqlalchemy-2.0.43-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f42f23e152e4545157fa367b2435a1ace7571cab016ca26038867eb7df2c3631"},
{file = "sqlalchemy-2.0.39-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4600c7a659d381146e1160235918826c50c80994e07c5b26946a3e7ec6c99249"}, {file = "sqlalchemy-2.0.43-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fb1a8c5438e0c5ea51afe9c6564f951525795cf432bed0c028c1cb081276685"},
{file = "sqlalchemy-2.0.39-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a06e6c8e31c98ddc770734c63903e39f1947c9e3e5e4bef515c5491b7737dde"}, {file = "sqlalchemy-2.0.43-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db691fa174e8f7036afefe3061bc40ac2b770718be2862bfb03aabae09051aca"},
{file = "sqlalchemy-2.0.39-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c4c433f78c2908ae352848f56589c02b982d0e741b7905228fad628999799de4"}, {file = "sqlalchemy-2.0.43-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe2b3b4927d0bc03d02ad883f402d5de201dbc8894ac87d2e981e7d87430e60d"},
{file = "sqlalchemy-2.0.39-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7bd5c5ee1448b6408734eaa29c0d820d061ae18cb17232ce37848376dcfa3e92"}, {file = "sqlalchemy-2.0.43-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4d3d9b904ad4a6b175a2de0738248822f5ac410f52c2fd389ada0b5262d6a1e3"},
{file = "sqlalchemy-2.0.39-cp310-cp310-win32.whl", hash = "sha256:87a1ce1f5e5dc4b6f4e0aac34e7bb535cb23bd4f5d9c799ed1633b65c2bcad8c"}, {file = "sqlalchemy-2.0.43-cp311-cp311-win32.whl", hash = "sha256:5cda6b51faff2639296e276591808c1726c4a77929cfaa0f514f30a5f6156921"},
{file = "sqlalchemy-2.0.39-cp310-cp310-win_amd64.whl", hash = "sha256:871f55e478b5a648c08dd24af44345406d0e636ffe021d64c9b57a4a11518304"}, {file = "sqlalchemy-2.0.43-cp311-cp311-win_amd64.whl", hash = "sha256:c5d1730b25d9a07727d20ad74bc1039bbbb0a6ca24e6769861c1aa5bf2c4c4a8"},
{file = "sqlalchemy-2.0.39-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a28f9c238f1e143ff42ab3ba27990dfb964e5d413c0eb001b88794c5c4a528a9"}, {file = "sqlalchemy-2.0.43-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:20d81fc2736509d7a2bd33292e489b056cbae543661bb7de7ce9f1c0cd6e7f24"},
{file = "sqlalchemy-2.0.39-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:08cf721bbd4391a0e765fe0fe8816e81d9f43cece54fdb5ac465c56efafecb3d"}, {file = "sqlalchemy-2.0.43-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b9fc27650ff5a2c9d490c13c14906b918b0de1f8fcbb4c992712d8caf40e83"},
{file = "sqlalchemy-2.0.39-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a8517b6d4005facdbd7eb4e8cf54797dbca100a7df459fdaff4c5123265c1cd"}, {file = "sqlalchemy-2.0.43-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6772e3ca8a43a65a37c88e2f3e2adfd511b0b1da37ef11ed78dea16aeae85bd9"},
{file = "sqlalchemy-2.0.39-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b2de1523d46e7016afc7e42db239bd41f2163316935de7c84d0e19af7e69538"}, {file = "sqlalchemy-2.0.43-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a113da919c25f7f641ffbd07fbc9077abd4b3b75097c888ab818f962707eb48"},
{file = "sqlalchemy-2.0.39-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:412c6c126369ddae171c13987b38df5122cb92015cba6f9ee1193b867f3f1530"}, {file = "sqlalchemy-2.0.43-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4286a1139f14b7d70141c67a8ae1582fc2b69105f1b09d9573494eb4bb4b2687"},
{file = "sqlalchemy-2.0.39-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b35e07f1d57b79b86a7de8ecdcefb78485dab9851b9638c2c793c50203b2ae8"}, {file = "sqlalchemy-2.0.43-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:529064085be2f4d8a6e5fab12d36ad44f1909a18848fcfbdb59cc6d4bbe48efe"},
{file = "sqlalchemy-2.0.39-cp311-cp311-win32.whl", hash = "sha256:3eb14ba1a9d07c88669b7faf8f589be67871d6409305e73e036321d89f1d904e"}, {file = "sqlalchemy-2.0.43-cp312-cp312-win32.whl", hash = "sha256:b535d35dea8bbb8195e7e2b40059e2253acb2b7579b73c1b432a35363694641d"},
{file = "sqlalchemy-2.0.39-cp311-cp311-win_amd64.whl", hash = "sha256:78f1b79132a69fe8bd6b5d91ef433c8eb40688ba782b26f8c9f3d2d9ca23626f"}, {file = "sqlalchemy-2.0.43-cp312-cp312-win_amd64.whl", hash = "sha256:1c6d85327ca688dbae7e2b06d7d84cfe4f3fffa5b5f9e21bb6ce9d0e1a0e0e0a"},
{file = "sqlalchemy-2.0.39-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c457a38351fb6234781d054260c60e531047e4d07beca1889b558ff73dc2014b"}, {file = "sqlalchemy-2.0.43-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e7c08f57f75a2bb62d7ee80a89686a5e5669f199235c6d1dac75cd59374091c3"},
{file = "sqlalchemy-2.0.39-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:018ee97c558b499b58935c5a152aeabf6d36b3d55d91656abeb6d93d663c0c4c"}, {file = "sqlalchemy-2.0.43-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:14111d22c29efad445cd5021a70a8b42f7d9152d8ba7f73304c4d82460946aaa"},
{file = "sqlalchemy-2.0.39-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5493a8120d6fc185f60e7254fc056a6742f1db68c0f849cfc9ab46163c21df47"}, {file = "sqlalchemy-2.0.43-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21b27b56eb2f82653168cefe6cb8e970cdaf4f3a6cb2c5e3c3c1cf3158968ff9"},
{file = "sqlalchemy-2.0.39-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2cf5b5ddb69142511d5559c427ff00ec8c0919a1e6c09486e9c32636ea2b9dd"}, {file = "sqlalchemy-2.0.43-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c5a9da957c56e43d72126a3f5845603da00e0293720b03bde0aacffcf2dc04f"},
{file = "sqlalchemy-2.0.39-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9f03143f8f851dd8de6b0c10784363712058f38209e926723c80654c1b40327a"}, {file = "sqlalchemy-2.0.43-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d79f9fdc9584ec83d1b3c75e9f4595c49017f5594fee1a2217117647225d738"},
{file = "sqlalchemy-2.0.39-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06205eb98cb3dd52133ca6818bf5542397f1dd1b69f7ea28aa84413897380b06"}, {file = "sqlalchemy-2.0.43-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9df7126fd9db49e3a5a3999442cc67e9ee8971f3cb9644250107d7296cb2a164"},
{file = "sqlalchemy-2.0.39-cp312-cp312-win32.whl", hash = "sha256:7f5243357e6da9a90c56282f64b50d29cba2ee1f745381174caacc50d501b109"}, {file = "sqlalchemy-2.0.43-cp313-cp313-win32.whl", hash = "sha256:7f1ac7828857fcedb0361b48b9ac4821469f7694089d15550bbcf9ab22564a1d"},
{file = "sqlalchemy-2.0.39-cp312-cp312-win_amd64.whl", hash = "sha256:2ed107331d188a286611cea9022de0afc437dd2d3c168e368169f27aa0f61338"}, {file = "sqlalchemy-2.0.43-cp313-cp313-win_amd64.whl", hash = "sha256:971ba928fcde01869361f504fcff3b7143b47d30de188b11c6357c0505824197"},
{file = "sqlalchemy-2.0.39-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fe193d3ae297c423e0e567e240b4324d6b6c280a048e64c77a3ea6886cc2aa87"}, {file = "sqlalchemy-2.0.43-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4e6aeb2e0932f32950cf56a8b4813cb15ff792fc0c9b3752eaf067cfe298496a"},
{file = "sqlalchemy-2.0.39-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:79f4f502125a41b1b3b34449e747a6abfd52a709d539ea7769101696bdca6716"}, {file = "sqlalchemy-2.0.43-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:61f964a05356f4bca4112e6334ed7c208174511bd56e6b8fc86dad4d024d4185"},
{file = "sqlalchemy-2.0.39-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a10ca7f8a1ea0fd5630f02feb055b0f5cdfcd07bb3715fc1b6f8cb72bf114e4"}, {file = "sqlalchemy-2.0.43-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46293c39252f93ea0910aababa8752ad628bcce3a10d3f260648dd472256983f"},
{file = "sqlalchemy-2.0.39-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e6b0a1c7ed54a5361aaebb910c1fa864bae34273662bb4ff788a527eafd6e14d"}, {file = "sqlalchemy-2.0.43-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:136063a68644eca9339d02e6693932116f6a8591ac013b0014479a1de664e40a"},
{file = "sqlalchemy-2.0.39-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52607d0ebea43cf214e2ee84a6a76bc774176f97c5a774ce33277514875a718e"}, {file = "sqlalchemy-2.0.43-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:6e2bf13d9256398d037fef09fd8bf9b0bf77876e22647d10761d35593b9ac547"},
{file = "sqlalchemy-2.0.39-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c08a972cbac2a14810463aec3a47ff218bb00c1a607e6689b531a7c589c50723"}, {file = "sqlalchemy-2.0.43-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:44337823462291f17f994d64282a71c51d738fc9ef561bf265f1d0fd9116a782"},
{file = "sqlalchemy-2.0.39-cp313-cp313-win32.whl", hash = "sha256:23c5aa33c01bd898f879db158537d7e7568b503b15aad60ea0c8da8109adf3e7"}, {file = "sqlalchemy-2.0.43-cp38-cp38-win32.whl", hash = "sha256:13194276e69bb2af56198fef7909d48fd34820de01d9c92711a5fa45497cc7ed"},
{file = "sqlalchemy-2.0.39-cp313-cp313-win_amd64.whl", hash = "sha256:4dabd775fd66cf17f31f8625fc0e4cfc5765f7982f94dc09b9e5868182cb71c0"}, {file = "sqlalchemy-2.0.43-cp38-cp38-win_amd64.whl", hash = "sha256:334f41fa28de9f9be4b78445e68530da3c5fa054c907176460c81494f4ae1f5e"},
{file = "sqlalchemy-2.0.39-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2600a50d590c22d99c424c394236899ba72f849a02b10e65b4c70149606408b5"}, {file = "sqlalchemy-2.0.43-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ceb5c832cc30663aeaf5e39657712f4c4241ad1f638d487ef7216258f6d41fe7"},
{file = "sqlalchemy-2.0.39-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4eff9c270afd23e2746e921e80182872058a7a592017b2713f33f96cc5f82e32"}, {file = "sqlalchemy-2.0.43-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:11f43c39b4b2ec755573952bbcc58d976779d482f6f832d7f33a8d869ae891bf"},
{file = "sqlalchemy-2.0.39-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7332868ce891eda48896131991f7f2be572d65b41a4050957242f8e935d5d7"}, {file = "sqlalchemy-2.0.43-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:413391b2239db55be14fa4223034d7e13325a1812c8396ecd4f2c08696d5ccad"},
{file = "sqlalchemy-2.0.39-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:125a7763b263218a80759ad9ae2f3610aaf2c2fbbd78fff088d584edf81f3782"}, {file = "sqlalchemy-2.0.43-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c379e37b08c6c527181a397212346be39319fb64323741d23e46abd97a400d34"},
{file = "sqlalchemy-2.0.39-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:04545042969833cb92e13b0a3019549d284fd2423f318b6ba10e7aa687690a3c"}, {file = "sqlalchemy-2.0.43-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:03d73ab2a37d9e40dec4984d1813d7878e01dbdc742448d44a7341b7a9f408c7"},
{file = "sqlalchemy-2.0.39-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:805cb481474e111ee3687c9047c5f3286e62496f09c0e82e8853338aaaa348f8"}, {file = "sqlalchemy-2.0.43-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8cee08f15d9e238ede42e9bbc1d6e7158d0ca4f176e4eab21f88ac819ae3bd7b"},
{file = "sqlalchemy-2.0.39-cp39-cp39-win32.whl", hash = "sha256:34d5c49f18778a3665d707e6286545a30339ad545950773d43977e504815fa70"}, {file = "sqlalchemy-2.0.43-cp39-cp39-win32.whl", hash = "sha256:b3edaec7e8b6dc5cd94523c6df4f294014df67097c8217a89929c99975811414"},
{file = "sqlalchemy-2.0.39-cp39-cp39-win_amd64.whl", hash = "sha256:35e72518615aa5384ef4fae828e3af1b43102458b74a8c481f69af8abf7e802a"}, {file = "sqlalchemy-2.0.43-cp39-cp39-win_amd64.whl", hash = "sha256:227119ce0a89e762ecd882dc661e0aa677a690c914e358f0dd8932a2e8b2765b"},
{file = "sqlalchemy-2.0.39-py3-none-any.whl", hash = "sha256:a1c6b0a5e3e326a466d809b651c63f278b1256146a377a528b6938a279da334f"}, {file = "sqlalchemy-2.0.43-py3-none-any.whl", hash = "sha256:1681c21dd2ccee222c2fe0bef671d1aef7c504087c9c4e800371cfcc8ac966fc"},
{file = "sqlalchemy-2.0.39.tar.gz", hash = "sha256:5d2d1fe548def3267b4c70a8568f108d1fed7cbbeccb9cc166e05af2abc25c22"}, {file = "sqlalchemy-2.0.43.tar.gz", hash = "sha256:788bfcef6787a7764169cfe9859fe425bf44559619e1d9f56f5bddf2ebf6f417"},
] ]
[package.dependencies] [package.dependencies]
aiomysql = {version = ">=0.2.0", optional = true, markers = "extra == \"aiomysql\""} aiomysql = {version = ">=0.2.0", optional = true, markers = "extra == \"aiomysql\""}
greenlet = {version = "!=0.4.17", optional = true, markers = "python_version < \"3.14\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\") or extra == \"aiomysql\""} greenlet = {version = ">=1", optional = true, markers = "python_version < \"3.14\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\") or extra == \"aiomysql\""}
pymysql = {version = "*", optional = true, markers = "extra == \"pymysql\""} pymysql = {version = "*", optional = true, markers = "extra == \"pymysql\""}
typing-extensions = ">=4.6.0" typing-extensions = ">=4.6.0"
[package.extras] [package.extras]
aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] aiomysql = ["aiomysql (>=0.2.0)", "greenlet (>=1)"]
aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] aioodbc = ["aioodbc", "greenlet (>=1)"]
aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] aiosqlite = ["aiosqlite", "greenlet (>=1)", "typing_extensions (!=3.10.0.1)"]
asyncio = ["greenlet (!=0.4.17)"] asyncio = ["greenlet (>=1)"]
asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (>=1)"]
mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5,!=1.1.10)"] mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5,!=1.1.10)"]
mssql = ["pyodbc"] mssql = ["pyodbc"]
mssql-pymssql = ["pymssql"] mssql-pymssql = ["pymssql"]
@@ -1503,7 +1522,7 @@ mysql-connector = ["mysql-connector-python"]
oracle = ["cx_oracle (>=8)"] oracle = ["cx_oracle (>=8)"]
oracle-oracledb = ["oracledb (>=1.0.1)"] oracle-oracledb = ["oracledb (>=1.0.1)"]
postgresql = ["psycopg2 (>=2.7)"] postgresql = ["psycopg2 (>=2.7)"]
postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] postgresql-asyncpg = ["asyncpg", "greenlet (>=1)"]
postgresql-pg8000 = ["pg8000 (>=1.29.1)"] postgresql-pg8000 = ["pg8000 (>=1.29.1)"]
postgresql-psycopg = ["psycopg (>=3.0.7)"] postgresql-psycopg = ["psycopg (>=3.0.7)"]
postgresql-psycopg2binary = ["psycopg2-binary"] postgresql-psycopg2binary = ["psycopg2-binary"]
@@ -1933,4 +1952,4 @@ propcache = ">=0.2.0"
[metadata] [metadata]
lock-version = "2.1" lock-version = "2.1"
python-versions = ">=3.11,<4.0" python-versions = ">=3.11,<4.0"
content-hash = "5ed129fde2c5d7b3518fbb2fe2ce79f0bad2aa1060304d45a7bc26d35f7ab46b" content-hash = "0ac41110571b3bba38672fceef1c107864b8582378c6afba6418e05f99a75da1"

View File

@@ -17,6 +17,7 @@ dependencies = [
"pydantic[email] (>=2.11.3,<3.0.0)", "pydantic[email] (>=2.11.3,<3.0.0)",
"python-multipart (>=0.0.20,<0.0.21)", "python-multipart (>=0.0.20,<0.0.21)",
"fastapi-jwt-auth @ git+https://github.com/vvpreo/fastapi-jwt-auth", "fastapi-jwt-auth @ git+https://github.com/vvpreo/fastapi-jwt-auth",
"core-library @ git+https://gitea.heado.ru/Vorkout/core.git@0.1.0",
] ]

234
client/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "client", "name": "client",
"version": "0.0.5", "version": "0.0.3",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "client", "name": "client",
"version": "0.0.5", "version": "0.0.3",
"dependencies": { "dependencies": {
"@ant-design/icons": "^5.6.1", "@ant-design/icons": "^5.6.1",
"@testing-library/dom": "^10.4.0", "@testing-library/dom": "^10.4.0",
@@ -16,7 +16,6 @@
"@types/jest": "^27.5.2", "@types/jest": "^27.5.2",
"@types/react": "^19.0.11", "@types/react": "^19.0.11",
"@types/react-dom": "^19.0.4", "@types/react-dom": "^19.0.4",
"@xyflow/react": "^12.8.1",
"antd": "^5.24.7", "antd": "^5.24.7",
"axios": "^1.9.0", "axios": "^1.9.0",
"axios-retry": "^4.5.0", "axios-retry": "^4.5.0",
@@ -1568,55 +1567,6 @@
"@babel/types": "^7.20.7" "@babel/types": "^7.20.7"
} }
}, },
"node_modules/@types/d3-color": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
"license": "MIT"
},
"node_modules/@types/d3-drag": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz",
"integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",
"license": "MIT",
"dependencies": {
"@types/d3-selection": "*"
}
},
"node_modules/@types/d3-interpolate": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
"license": "MIT",
"dependencies": {
"@types/d3-color": "*"
}
},
"node_modules/@types/d3-selection": {
"version": "3.0.11",
"resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz",
"integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==",
"license": "MIT"
},
"node_modules/@types/d3-transition": {
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz",
"integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==",
"license": "MIT",
"dependencies": {
"@types/d3-selection": "*"
}
},
"node_modules/@types/d3-zoom": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz",
"integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==",
"license": "MIT",
"dependencies": {
"@types/d3-interpolate": "*",
"@types/d3-selection": "*"
}
},
"node_modules/@types/estree": { "node_modules/@types/estree": {
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
@@ -1683,66 +1633,6 @@
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0"
} }
}, },
"node_modules/@xyflow/react": {
"version": "12.8.1",
"resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.8.1.tgz",
"integrity": "sha512-t5Rame4Gc/540VcOZd28yFe9Xd8lyjKUX+VTiyb1x4ykNXZH5zyDmsu+lj9je2O/jGBVb0pj1Vjcxrxyn+Xk2g==",
"license": "MIT",
"dependencies": {
"@xyflow/system": "0.0.65",
"classcat": "^5.0.3",
"zustand": "^4.4.0"
},
"peerDependencies": {
"react": ">=17",
"react-dom": ">=17"
}
},
"node_modules/@xyflow/react/node_modules/zustand": {
"version": "4.5.7",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz",
"integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==",
"license": "MIT",
"dependencies": {
"use-sync-external-store": "^1.2.2"
},
"engines": {
"node": ">=12.7.0"
},
"peerDependencies": {
"@types/react": ">=16.8",
"immer": ">=9.0.6",
"react": ">=16.8"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"immer": {
"optional": true
},
"react": {
"optional": true
}
}
},
"node_modules/@xyflow/system": {
"version": "0.0.65",
"resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.65.tgz",
"integrity": "sha512-AliQPQeurQMoNlOdySnRoDQl9yDSA/1Lqi47Eo0m98lHcfrTdD9jK75H0tiGj+0qRC10SKNUXyMkT0KL0opg4g==",
"license": "MIT",
"dependencies": {
"@types/d3-drag": "^3.0.7",
"@types/d3-interpolate": "^3.0.4",
"@types/d3-selection": "^3.0.10",
"@types/d3-transition": "^3.0.8",
"@types/d3-zoom": "^3.0.8",
"d3-drag": "^3.0.0",
"d3-interpolate": "^3.0.1",
"d3-selection": "^3.0.0",
"d3-zoom": "^3.0.0"
}
},
"node_modules/ansi-regex": { "node_modules/ansi-regex": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
@@ -2270,12 +2160,6 @@
"node": ">= 0.10" "node": ">= 0.10"
} }
}, },
"node_modules/classcat": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz",
"integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==",
"license": "MIT"
},
"node_modules/classnames": { "node_modules/classnames": {
"version": "2.5.1", "version": "2.5.1",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
@@ -2456,111 +2340,6 @@
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/d3-color": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-dispatch": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
"integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-drag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
"integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
"license": "ISC",
"dependencies": {
"d3-dispatch": "1 - 3",
"d3-selection": "3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-ease": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-interpolate": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
"license": "ISC",
"dependencies": {
"d3-color": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-selection": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-timer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-transition": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
"integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
"license": "ISC",
"dependencies": {
"d3-color": "1 - 3",
"d3-dispatch": "1 - 3",
"d3-ease": "1 - 3",
"d3-interpolate": "1 - 3",
"d3-timer": "1 - 3"
},
"engines": {
"node": ">=12"
},
"peerDependencies": {
"d3-selection": "2 - 3"
}
},
"node_modules/d3-zoom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
"integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
"license": "ISC",
"dependencies": {
"d3-dispatch": "1 - 3",
"d3-drag": "2 - 3",
"d3-interpolate": "1 - 3",
"d3-selection": "2 - 3",
"d3-transition": "2 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/dayjs": { "node_modules/dayjs": {
"version": "1.11.13", "version": "1.11.13",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
@@ -5328,15 +5107,6 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/use-sync-external-store": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
"integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
"license": "MIT",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/util": { "node_modules/util": {
"version": "0.12.5", "version": "0.12.5",
"resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",

View File

@@ -11,7 +11,6 @@
"@types/jest": "^27.5.2", "@types/jest": "^27.5.2",
"@types/react": "^19.0.11", "@types/react": "^19.0.11",
"@types/react-dom": "^19.0.4", "@types/react-dom": "^19.0.4",
"@xyflow/react": "^12.8.1",
"antd": "^5.24.7", "antd": "^5.24.7",
"axios": "^1.9.0", "axios": "^1.9.0",
"axios-retry": "^4.5.0", "axios-retry": "^4.5.0",

View File

@@ -1,10 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="mask0_381_806" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="24" height="24">
<rect width="24" height="24" fill="#D9D9D9"/>
</mask>
<g mask="url(#mask0_381_806)">
<path d="M12 13.75H17V12.25H12V13.75ZM12 11.25H17V9.75H12V11.25ZM5 21C4.45 21 3.97917 20.8042 3.5875 20.4125C3.19583 20.0208 3 19.55 3 19V5C3 4.45 3.19583 3.97917 3.5875 3.5875C3.97917 3.19583 4.45 3 5 3H19C19.55 3 20.0208 3.19583 20.4125 3.5875C20.8042 3.97917 21 4.45 21 5V19C21 19.55 20.8042 20.0208 20.4125 20.4125C20.0208 20.8042 19.55 21 19 21H5ZM5 19H19V5H5V19Z" fill="#606060"/>
<path d="M9.75 14.0179H7.75V12.5179H9.75V14.0179Z" fill="#606060"/>
<path d="M9.75 11.5179H7.75V10.0179H9.75V11.5179Z" fill="#606060"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 828 B

View File

@@ -1,3 +0,0 @@
<svg width="16" height="18" viewBox="0 0 16 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 16H5V10H11V16H14V7L8 2.5L2 7V16ZM0 18V6L8 0L16 6V18H9V12H7V18H0Z" fill="#606060"/>
</svg>

Before

Width:  |  Height:  |  Size: 198 B

View File

@@ -1,8 +0,0 @@
<svg width="28" height="31" viewBox="0 0 28 31" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 28.6L1.4 30L3 28.425L4.575 30L6 28.6L4.4 27L6 25.425L4.575 24L3 25.6L1.4 24L0 25.425L1.575 27L0 28.6Z" fill="#606060"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 0C14.1 0 15.0419 0.391472 15.8252 1.1748C16.6085 1.95814 17 2.9 17 4C17 5.1 16.6085 6.04186 15.8252 6.8252C15.0419 7.60853 14.1 8 13 8C11.9 8 10.9581 7.60853 10.1748 6.8252C9.39147 6.04186 9 5.1 9 4C9 2.9 9.39147 1.95814 10.1748 1.1748C10.9581 0.391471 11.9 0 13 0ZM13 2C12.45 2 11.9796 2.19622 11.5879 2.58789C11.1962 2.97956 11 3.45 11 4C11 4.53333 11.1962 5.00039 11.5879 5.40039C11.9795 5.80013 12.4502 6 13 6C13.5498 6 14.0205 5.80013 14.4121 5.40039C14.8038 5.00039 15 4.53333 15 4C15 3.45 14.8038 2.97956 14.4121 2.58789C14.0204 2.19622 13.55 2 13 2Z" fill="#606060"/>
<path d="M11.8 9H13.8V14H11.8V9Z" fill="#606060"/>
<path d="M20 27.55L22.825 30.375L27.775 25.425L26.35 24L22.825 27.55L21.4 26.125L20 27.55Z" fill="#606060"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.167 18.417L12.75 24.834L7.25005 19.334H4.00005L4 23H2L2.00005 17.5H7.25005L12.75 12L19.167 18.417ZM8.9229 18.417L12.75 22.2441L16.5772 18.417L12.75 14.5898L8.9229 18.417Z" fill="#606060"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.5 19.334L22 19.334V23H24V17.5L17.5 17.5V19.334Z" fill="#606060"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,175 @@
import { Drawer } from 'antd';
import { useEffect, useState } from 'react';
import { Avatar, Typography } from 'antd';
import { useTranslation } from 'react-i18next';
import { useUserSelector } from '@/store/userStore';
interface ContentDrawerProps {
open: boolean;
closeDrawer: () => void;
children: React.ReactNode;
type: 'create' | 'edit';
login?: string;
name?: string;
email?: string | null;
}
export default function ContentDrawer({
open,
closeDrawer,
children,
type,
login,
name,
email,
}: ContentDrawerProps) {
const user = useUserSelector();
const { t } = useTranslation();
const [width, setWidth] = useState<number | string>('30%');
const calculateWidths = () => {
const windowWidth = window.innerWidth;
const expanded = Math.max(windowWidth * 0.3, 300);
setWidth(expanded);
};
useEffect(() => {
calculateWidths();
window.addEventListener('resize', calculateWidths);
return () => window.removeEventListener('resize', calculateWidths);
}, []);
console.log(login, user?.login, login === user?.login);
const editDrawerTitle = (
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
gap: 12,
}}
>
<div
onClick={closeDrawer}
style={{
display: 'flex',
alignItems: 'center',
height: '24px',
width: '24px',
cursor: 'pointer',
}}
>
<img
src="./icons/drawer/arrow_back.svg"
alt="close_drawer"
style={{ height: '16px', width: '16px' }}
/>
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: 12, flex: 1 }}>
<Avatar
src={
login ? `https://gamma.heado.ru/go/ava?name=${login}` : undefined
}
size={40}
style={{ flexShrink: 0 }}
/>
<div>
<Typography.Text
strong
style={{ display: 'block', fontSize: '20px' }}
>
{name} {login === user?.login ? t('you') : ''}
</Typography.Text>
<Typography.Text type="secondary" style={{ fontSize: 14 }}>
{email}
</Typography.Text>
</div>
</div>
<div
style={{
display: 'flex',
alignItems: 'center',
height: '24px',
width: '24px',
}}
>
<img
src="./icons/drawer/delete.svg"
alt="delete"
style={{ height: '18px', width: '16px' }}
/>
</div>
</div>
);
const createDrawerTitle = (
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
gap: 12,
}}
>
<div
onClick={closeDrawer}
style={{
display: 'flex',
alignItems: 'center',
height: '24px',
width: '24px',
cursor: 'pointer',
}}
>
<img
src="./icons/drawer/arrow_back.svg"
alt="close_drawer"
style={{ height: '16px', width: '16px' }}
/>
</div>
<div
style={{
display: 'flex',
alignItems: 'center',
gap: 12,
flex: 1,
fontSize: '20px',
}}
>
{t('newAccount')}
</div>
<div
style={{
display: 'flex',
alignItems: 'center',
height: '24px',
width: '24px',
}}
onClick={closeDrawer}
>
<img
src="./icons/drawer/delete.svg"
alt="delete"
style={{ height: '18px', width: '16px' }}
/>
</div>
</div>
);
return (
<Drawer
title={type === 'create' ? createDrawerTitle : editDrawerTitle}
placement="right"
open={open}
width={width}
destroyOnHidden={true}
closable={false}
>
{children}
</Drawer>
);
}

View File

@@ -2,8 +2,8 @@ import { useUserSelector } from '@/store/userStore';
import { Avatar } from 'antd'; import { Avatar } from 'antd';
import Title from 'antd/es/typography/Title'; import Title from 'antd/es/typography/Title';
import { useState } from 'react'; import { useState } from 'react';
import ContentDrawer from './drawers/ContentDrawer'; import ContentDrawer from './ContentDrawer';
import UserEdit from './drawers/users/UserEdit'; import UserEdit from './UserEdit';
interface HeaderProps { interface HeaderProps {
title: string; title: string;
@@ -68,7 +68,7 @@ export default function Header({ title, additionalContent }: HeaderProps) {
email={user?.email} email={user?.email}
open={openEdit} open={openEdit}
closeDrawer={closeEditDrawer} closeDrawer={closeEditDrawer}
type="userEdit" type="edit"
> >
{user?.id && <UserEdit closeDrawer={closeEditDrawer} userId={user?.id} />} {user?.id && <UserEdit closeDrawer={closeEditDrawer} userId={user?.id} />}
</ContentDrawer> </ContentDrawer>

View File

@@ -1,281 +0,0 @@
import {
ReactFlow,
Background,
Controls,
useNodesState,
useEdgesState,
Node,
Edge,
} from "@xyflow/react";
import { useMemo, useState } from "react";
import customEdgeStyle from "./edges/defaultEdgeStyle";
import { Dropdown, DropdownProps } from "antd";
import { useTranslation } from "react-i18next";
import { edgeTitleGenerator } from "@/utils/edge";
import IfElseNode from "./nodes/IfElseNode";
import AppropriationNode from "./nodes/AppropriationNode";
import StartNode from "./nodes/StartNode";
const initialNodes: Node[] = [
{
id: "1",
type: "startNode",
position: { x: 100, y: 0 },
data: {},
},
{
id: "2",
type: "ifElse",
position: { x: 100, y: 200 },
data: { condition: "B=2" },
},
{
id: "3",
type: "appropriation",
position: { x: 100, y: 400 },
data: { value: "Выбрать {{account.email}}" },
},
{
id: "4",
type: "appropriation",
position: { x: 400, y: 400 },
data: { value: "Выбрать {{account.role}}" },
},
];
const initialEdges: Edge[] = [
// {
// id: "e1-3",
// source: "1",
// sourceHandle: "1",
// target: "3",
// targetHandle: null,
// label: "A1",
// ...customEdgeStyle,
// },
// {
// id: "e1-2",
// source: "1",
// sourceHandle: "2",
// target: "2",
// label: "B1",
// ...customEdgeStyle,
// },
];
interface ReactFlowDrawerProps {
showDrawer: () => void;
}
export default function ReactFlowDrawer({ showDrawer }: ReactFlowDrawerProps) {
const { t } = useTranslation();
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const [menuVisible, setMenuVisible] = useState(false);
const [selectedHandleId, setSelectedHandleId] = useState<string | null>(null);
const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });
const handleOpenChange: DropdownProps["onOpenChange"] = (nextOpen, info) => {
if (info.source === "trigger" || nextOpen) {
setMenuVisible(nextOpen);
}
};
const handleClick = (e: React.MouseEvent, node: Node) => {
e.stopPropagation();
const target = e.target as HTMLElement;
const handleElement = target.closest(".react-flow__handle") as HTMLElement;
if (!handleElement) return;
const handleId = handleElement.getAttribute("data-handleid");
if (!handleId) return;
const handlePos = handleElement.getAttribute("data-handlepos");
if (handlePos === "top") return;
setSelectedHandleId(`${node.id}-${handleId}`);
const flowWrapper = document.querySelector(".react-flow") as HTMLElement;
if (flowWrapper) {
const wrapperRect = flowWrapper.getBoundingClientRect();
const handleRect = handleElement.getBoundingClientRect();
setMenuPosition({
x: handleRect.right - wrapperRect.left,
y: handleRect.top - wrapperRect.top + 20,
});
}
setMenuVisible(true);
};
const handleMenuItemClick = (targetNodeId: string) => {
if (!selectedHandleId) return;
const [sourceNodeId, sourceHandleId] = selectedHandleId.split("-");
const label = edgeTitleGenerator(edges.length + 1);
const newEdge: Edge = {
id: `e${sourceNodeId}-${sourceHandleId}-${targetNodeId}:${label}`,
source: sourceNodeId,
sourceHandle: sourceHandleId,
target: targetNodeId,
label,
...customEdgeStyle,
};
setEdges((eds) => [...eds, newEdge]);
setMenuVisible(false);
setSelectedHandleId(null);
};
const handleCreateNode = (type: string) => {
if (!selectedHandleId) return;
const [sourceNodeId, sourceHandleId] = selectedHandleId.split("-");
const sourceNode = nodes.find((n) => n.id === sourceNodeId);
const newId = (
Math.max(...nodes.map((n) => Number(n.id) || 0)) + 1
).toString();
let newNode;
if (type === "ifElse") {
newNode = {
id: newId,
type: "ifElse",
position: {
x: (sourceNode?.position.x || 0) + 200,
y: (sourceNode?.position.y || 0) + 100,
},
data: { condition: "" },
};
} else if (type === "appropriation") {
newNode = {
id: newId,
type: "appropriation",
position: {
x: (sourceNode?.position.x || 0) + 200,
y: (sourceNode?.position.y || 0) + 100,
},
data: { value: "" },
};
}
if (newNode) {
setNodes((nds) => [...nds, newNode]);
const label = edgeTitleGenerator(edges.length + 1);
const newEdge = {
id: `e${sourceNodeId}-${sourceHandleId}-${newId}:${label}`,
source: sourceNodeId,
sourceHandle: sourceHandleId,
target: newId,
label,
...customEdgeStyle,
};
setEdges((eds) => [...eds, newEdge]);
setMenuVisible(false);
setSelectedHandleId(null);
}
};
const newNodeTypes = [
{ key: "ifElse", label: t("ifElseNode") },
{
key: "appropriation",
label: t("appropriationNode"),
},
];
const existingNodes = nodes
.filter((node) => node.id !== selectedHandleId?.split("-")[0])
.filter((node) => {
if (!selectedHandleId || node.type === "startNode") return false;
const [sourceNodeId, sourceHandleId] = selectedHandleId.split("-");
return !edges.some(
(edge) =>
edge.source === sourceNodeId &&
edge.sourceHandle === sourceHandleId &&
edge.target === node.id
);
});
const menuItems = [
{
key: "connectToExisting",
label: t("connectToExisting"),
children: existingNodes.map((node) => ({
key: node.id,
label: t("connectTo", { nodeId: node.id }),
onClick: () => handleMenuItemClick(node.id),
})),
},
...newNodeTypes.map((nt) => ({
key: `createNew-${nt.key}`,
label: nt.label,
onClick: () => handleCreateNode(nt.key),
})),
];
const nodeTypes = useMemo(
() => ({
startNode: (props: any) => <StartNode {...props} edges={edges} />,
ifElse: (props: any) => <IfElseNode {...props} edges={edges} />,
appropriation: (props: any) => (
<AppropriationNode {...props} edges={edges} />
),
}),
[edges]
);
return (
<ReactFlow
nodes={nodes}
edges={edges}
nodesDraggable={false}
elementsSelectable={false}
nodesConnectable={false}
onNodeClick={(event, node) => {
const target = event.target as HTMLElement;
if (!target.closest(".react-flow__handle")) {
console.log("node clicked");
showDrawer();
} else {
handleClick(event, node);
}
}}
nodeTypes={nodeTypes}
fitView
minZoom={0.5}
maxZoom={1.0}
>
<Background color="#F2F2F2" />
<Controls
position="bottom-center"
orientation="horizontal"
showInteractive={false}
/>
{menuVisible && (
<div
style={{
position: "absolute",
left: `${menuPosition.x}px`,
top: `${menuPosition.y}px`,
zIndex: 9999,
}}
>
<Dropdown
menu={{ items: menuItems }}
open={menuVisible}
onOpenChange={handleOpenChange}
placement="bottom"
>
<div style={{ width: 1, height: 1 }} />
</Dropdown>
</div>
)}
</ReactFlow>
);
}

View File

@@ -1,245 +0,0 @@
import { Drawer } from "antd";
import { useEffect, useState } from "react";
import { Avatar, Typography } from "antd";
import { useTranslation } from "react-i18next";
import { useUserSelector } from "@/store/userStore";
interface ContentDrawerProps {
open: boolean;
closeDrawer: () => void;
children: React.ReactNode;
type: "userCreate" | "userEdit" | "nodeEdit";
login?: string;
name?: string;
email?: string | null;
}
export default function ContentDrawer({
open,
closeDrawer,
children,
type,
login,
name,
email,
}: ContentDrawerProps) {
const user = useUserSelector();
const { t } = useTranslation();
const [width, setWidth] = useState<number | string>("30%");
const calculateWidths = () => {
const windowWidth = window.innerWidth;
const expanded = Math.max(windowWidth * 0.3, 300);
setWidth(expanded);
};
useEffect(() => {
calculateWidths();
window.addEventListener("resize", calculateWidths);
return () => window.removeEventListener("resize", calculateWidths);
}, []);
console.log(login, user?.login, login === user?.login);
const userEditDrawerTitle = (
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
gap: 12,
}}
>
<div
onClick={closeDrawer}
style={{
display: "flex",
alignItems: "center",
height: "24px",
width: "24px",
cursor: "pointer",
}}
>
<img
src="./icons/drawer/arrow_back.svg"
alt="close_drawer"
style={{ height: "16px", width: "16px" }}
/>
</div>
<div style={{ display: "flex", alignItems: "center", gap: 12, flex: 1 }}>
<Avatar
src={
login ? `https://gamma.heado.ru/go/ava?name=${login}` : undefined
}
size={40}
style={{ flexShrink: 0 }}
/>
<div>
<Typography.Text
strong
style={{ display: "block", fontSize: "20px" }}
>
{name} {login === user?.login ? t("you") : ""}
</Typography.Text>
<Typography.Text type="secondary" style={{ fontSize: 14 }}>
{email}
</Typography.Text>
</div>
</div>
<div
style={{
display: "flex",
alignItems: "center",
height: "24px",
width: "24px",
}}
>
<img
src="./icons/drawer/delete.svg"
alt="delete"
style={{ height: "18px", width: "16px" }}
/>
</div>
</div>
);
const userCreateDrawerTitle = (
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
gap: 12,
}}
>
<div
onClick={closeDrawer}
style={{
display: "flex",
alignItems: "center",
height: "24px",
width: "24px",
cursor: "pointer",
}}
>
<img
src="./icons/drawer/arrow_back.svg"
alt="close_drawer"
style={{ height: "16px", width: "16px" }}
/>
</div>
<div
style={{
display: "flex",
alignItems: "center",
gap: 12,
flex: 1,
fontSize: "20px",
}}
>
{t("newAccount")}
</div>
<div
style={{
display: "flex",
alignItems: "center",
height: "24px",
width: "24px",
}}
onClick={closeDrawer}
>
<img
src="./icons/drawer/delete.svg"
alt="delete"
style={{ height: "18px", width: "16px" }}
/>
</div>
</div>
);
const nodeEditDrawerTitle = (
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
gap: 12,
}}
>
<div
onClick={closeDrawer}
style={{
display: "flex",
alignItems: "center",
height: "24px",
width: "24px",
cursor: "pointer",
}}
>
<img
src="./icons/drawer/arrow_back.svg"
alt="close_drawer"
style={{ height: "16px", width: "16px" }}
/>
</div>
<div
style={{
display: "flex",
alignItems: "center",
gap: 12,
flex: 1,
fontSize: "20px",
}}
>
Редактирование блока
</div>
<div
style={{
display: "flex",
alignItems: "center",
height: "24px",
width: "24px",
}}
onClick={closeDrawer}
>
<img
src="./icons/drawer/delete.svg"
alt="delete"
style={{ height: "18px", width: "16px" }}
/>
</div>
</div>
);
return (
<Drawer
// title={
// type === "userCreate" ? userCreateDrawerTitle : userEditDrawerTitle
// }
title={(() => {
switch (type) {
case "userCreate":
return userCreateDrawerTitle;
case "userEdit":
return userEditDrawerTitle;
case "nodeEdit":
return nodeEditDrawerTitle;
default:
return null;
}
})()}
placement="right"
open={open}
width={width}
destroyOnHidden={true}
closable={false}
>
{children}
</Drawer>
);
}

View File

@@ -1,213 +0,0 @@
import { User } from "@/types/user";
import { Avatar, Typography } from "antd";
import { TFunction } from "i18next";
interface DrawerTitleProps {
closeDrawer: () => void;
t: TFunction;
}
interface UserEditDrawerTitleProps extends DrawerTitleProps {
login?: string;
name?: string;
email?: string | null;
user: User | null;
}
interface UserCreateDrawerTitleProps extends DrawerTitleProps {}
interface NodeEditDrawerTitleProps extends DrawerTitleProps {}
const UserEditDrawerTitle = ({
closeDrawer,
login,
name,
email,
user,
t,
}: UserEditDrawerTitleProps) => {
return (
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
gap: 12,
}}
>
<div
onClick={closeDrawer}
style={{
display: "flex",
alignItems: "center",
height: "24px",
width: "24px",
cursor: "pointer",
}}
>
<img
src="./icons/drawer/arrow_back.svg"
alt="close_drawer"
style={{ height: "16px", width: "16px" }}
/>
</div>
<div style={{ display: "flex", alignItems: "center", gap: 12, flex: 1 }}>
<Avatar
src={
login ? `https://gamma.heado.ru/go/ava?name=${login}` : undefined
}
size={40}
style={{ flexShrink: 0 }}
/>
<div>
<Typography.Text
strong
style={{ display: "block", fontSize: "20px" }}
>
{name} {login === user?.login ? t("you") : ""}
</Typography.Text>
<Typography.Text type="secondary" style={{ fontSize: 14 }}>
{email}
</Typography.Text>
</div>
</div>
<div
style={{
display: "flex",
alignItems: "center",
height: "24px",
width: "24px",
}}
>
<img
src="./icons/drawer/delete.svg"
alt="delete"
style={{ height: "18px", width: "16px" }}
/>
</div>
</div>
);
};
const UserCreateDrawerTitle = ({
closeDrawer,
t,
}: UserCreateDrawerTitleProps) => {
return (
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
gap: 12,
}}
>
<div
onClick={closeDrawer}
style={{
display: "flex",
alignItems: "center",
height: "24px",
width: "24px",
cursor: "pointer",
}}
>
<img
src="./icons/drawer/arrow_back.svg"
alt="close_drawer"
style={{ height: "16px", width: "16px" }}
/>
</div>
<div
style={{
display: "flex",
alignItems: "center",
gap: "12px",
flex: 1,
fontSize: "20px",
}}
>
{t("newAccount")}
</div>
<div
style={{
display: "flex",
alignItems: "center",
height: "24px",
width: "24px",
}}
onClick={closeDrawer}
>
<img
src="./icons/drawer/delete.svg"
alt="delete"
style={{ height: "18px", width: "16px" }}
/>
</div>
</div>
);
};
const NodeEditDrawerTitle = ({ closeDrawer, t }: NodeEditDrawerTitleProps) => {
return (
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
gap: "12px",
}}
>
<div
onClick={closeDrawer}
style={{
display: "flex",
alignItems: "center",
height: "24px",
width: "24px",
cursor: "pointer",
}}
>
<img
src="./icons/drawer/arrow_back.svg"
alt="close_drawer"
style={{ height: "16px", width: "16px" }}
/>
</div>
<div
style={{
display: "flex",
alignItems: "center",
gap: "12px",
flex: 1,
fontSize: "20px",
}}
>
{t("editNode")}
</div>
<div
style={{
display: "flex",
alignItems: "center",
height: "24px",
width: "24px",
}}
onClick={closeDrawer}
>
<img
src="./icons/drawer/delete.svg"
alt="delete"
style={{ height: "18px", width: "16px" }}
/>
</div>
</div>
);
};
export { UserEditDrawerTitle, UserCreateDrawerTitle, NodeEditDrawerTitle };

View File

@@ -1,17 +0,0 @@
import { MarkerType } from "@xyflow/react";
const customEdgeStyle = {
markerEnd: {
type: MarkerType.Arrow,
width: 20,
height: 20,
color: "#BDBDBD",
},
style: {
strokeWidth: 2,
stroke: "#BDBDBD",
},
type: "step",
};
export default customEdgeStyle;

View File

@@ -1,63 +0,0 @@
import { Handle, Node, NodeProps, Position, Edge } from "@xyflow/react";
import { HANDLE_STYLE_CONNECTED } from "./defaultHandleStyle";
import { useTranslation } from "react-i18next";
type AppropriationNodeData = { value: string; edges?: Edge[] };
export default function AppropriationNode({
data,
id,
edges = [],
}: NodeProps<Node & AppropriationNodeData> & { edges?: Edge[] }) {
const { t } = useTranslation();
return (
<div
style={{
border: "0px solid",
borderRadius: 8,
backgroundColor: "white",
width: "248px",
}}
>
<div
style={{
display: "flex",
alignItems: "center",
paddingLeft: "12px",
height: "48px",
fontWeight: "600px",
fontSize: "16px",
gap: "12px",
}}
>
<img
style={{ height: "24px", width: "24px" }}
src="/icons/node/calculate.svg"
alt="appropriation logo"
/>
{t("appropriationNode")}
</div>
<div style={{ height: "1px", backgroundColor: "#E2E2E2" }}></div>
<div
style={{
display: "flex",
alignItems: "center",
padding: "12px",
fontSize: "14px",
minHeight: "48px",
}}
>
{data.value as string}
</div>
<Handle
style={{
...HANDLE_STYLE_CONNECTED,
}}
type="target"
position={Position.Top}
id="input"
/>
</div>
);
}

View File

@@ -1,119 +0,0 @@
import { Handle, NodeProps, Position, Node, Edge } from "@xyflow/react";
import {
HANDLE_STYLE_CONNECTED,
HANDLE_STYLE_CONNECTED_V,
HANDLE_STYLE_DISCONNECTED,
HANDLE_STYLE_DISCONNECTED_V,
} from "./defaultHandleStyle";
import { useTranslation } from "react-i18next";
import { useState } from "react";
interface IfElseNodeProps extends Node {
condition: string;
edges?: Edge[];
}
export default function IfElseNode({
id,
data,
edges = [],
}: NodeProps<IfElseNodeProps> & { edges?: Edge[] }) {
const { t } = useTranslation();
const isHandle1Connected = edges.some(
(e: Edge) => e.source === id && e.sourceHandle === "1"
);
const isHandle2Connected = edges.some(
(e: Edge) => e.source === id && e.sourceHandle === "2"
);
return (
<>
<div
style={{
border: "0px solid",
borderRadius: 8,
backgroundColor: "white",
width: "248px",
minHeight: "144px",
position: "relative",
}}
>
<div
style={{
display: "flex",
alignItems: "center",
paddingLeft: "12px",
height: "48px",
fontWeight: "600px",
fontSize: "16px",
gap: "12px",
}}
>
<img
style={{ height: "24px", width: "24px" }}
src="/icons/node/ifElse.svg"
alt="if else logo"
/>
{t("ifElseNode")}
</div>
<div style={{ height: "1px", backgroundColor: "#E2E2E2" }}></div>
<div
style={{
display: "flex",
alignItems: "center",
padding: "12px",
fontSize: "14px",
minHeight: "48px",
position: "relative",
}}
>
{t("conditionIf", { condition: data.condition })}
<Handle
type="source"
position={Position.Right}
id="1"
style={{
...(isHandle1Connected
? { ...HANDLE_STYLE_CONNECTED_V }
: { ...HANDLE_STYLE_DISCONNECTED_V }),
position: "absolute",
top: "50%",
transform: "translateY(-50%)",
}}
/>
</div>
<div style={{ height: "1px", backgroundColor: "#E2E2E2" }}></div>
<div
style={{
display: "flex",
alignItems: "center",
paddingLeft: "12px",
fontSize: "14px",
height: "48px",
}}
>
{t("conditionElse")}
</div>
<Handle
type="target"
position={Position.Top}
id="input"
style={{ ...HANDLE_STYLE_CONNECTED }}
/>
<Handle
type="source"
position={Position.Bottom}
id="2"
style={
isHandle2Connected
? { ...HANDLE_STYLE_CONNECTED }
: { ...HANDLE_STYLE_DISCONNECTED }
}
/>
</div>
</>
);
}

View File

@@ -1,83 +0,0 @@
import { Edge, Handle, Node, NodeProps, Position } from "@xyflow/react";
import { useTranslation } from "react-i18next";
import {
HANDLE_STYLE_CONNECTED,
HANDLE_STYLE_DISCONNECTED,
} from "./defaultHandleStyle";
import { useState } from "react";
interface StartNodeProps extends Node {
edges?: Edge[];
}
export default function StartNode({
id,
edges = [],
}: NodeProps<StartNodeProps> & { edges?: Edge[] }) {
const { t } = useTranslation();
const isHandleConnected = edges.some(
(e: Edge) => e.source === id && e.sourceHandle === "1"
);
return (
<div
style={{
border: "0px solid",
borderRadius: 8,
backgroundColor: "white",
width: "248px",
}}
>
<div
style={{
display: "flex",
alignItems: "center",
paddingLeft: "12px",
height: "40px",
fontWeight: "600px",
fontSize: "16px",
gap: "12px",
backgroundColor: "#D4E0BD",
borderRadius: "8px 8px 0 0",
}}
>
<div
style={{
height: "24px",
width: "24px",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<img
style={{ height: "16px", width: "18px" }}
src="/icons/node/home.svg"
alt="start logo"
/>
</div>
{t("startNode")}
</div>
<div style={{ height: "1px", backgroundColor: "#E2E2E2" }}></div>
<div
style={{
display: "flex",
alignItems: "center",
padding: "12px",
fontSize: "14px",
}}
>
{t("startNodeDescription")}
</div>
<Handle
type="source"
position={Position.Bottom}
id="1"
style={
isHandleConnected
? { ...HANDLE_STYLE_CONNECTED }
: { ...HANDLE_STYLE_DISCONNECTED }
}
/>
</div>
);
}

View File

@@ -1,34 +0,0 @@
// horizontal
const HANDLE_STYLE_CONNECTED = {
width: 8,
height: 8,
backgroundColor: "#BDBDBD",
};
// horizontal
const HANDLE_STYLE_DISCONNECTED = {
width: 20,
height: 20,
backgroundColor: "#C7D95A",
borderColor: "#606060",
borderWidth: 2,
};
// vertical
const HANDLE_STYLE_CONNECTED_V = {
...HANDLE_STYLE_CONNECTED,
right: "-4px",
};
// vertical
const HANDLE_STYLE_DISCONNECTED_V = {
...HANDLE_STYLE_DISCONNECTED,
right: "-10px",
};
export {
HANDLE_STYLE_CONNECTED,
HANDLE_STYLE_DISCONNECTED,
HANDLE_STYLE_CONNECTED_V,
HANDLE_STYLE_DISCONNECTED_V,
};

View File

@@ -1,119 +1,99 @@
import i18n from "i18next"; import i18n from 'i18next';
import { initReactI18next } from "react-i18next"; import { initReactI18next } from 'react-i18next';
import LanguageDetector from "i18next-browser-languagedetector"; import LanguageDetector from 'i18next-browser-languagedetector';
i18n i18n
.use(LanguageDetector) .use(LanguageDetector)
.use(initReactI18next) .use(initReactI18next)
.init({ .init({
fallbackLng: "en", fallbackLng: 'en',
supportedLngs: ["en", "ru"], supportedLngs: ['en', 'ru'],
interpolation: { escapeValue: false }, interpolation: { escapeValue: false },
resources: { resources: {
en: { en: {
translation: { translation: {
accounts: "Accounts", accounts: 'Accounts',
processDiagrams: "Process diagrams", processDiagrams: 'Process diagrams',
runningProcesses: "Running processes", runningProcesses: 'Running processes',
settings: "Settings", settings: 'Settings',
eventsList: "Events list", eventsList: 'Events list',
configuration: "Configuration", configuration: 'Configuration',
selectPhoto: "Select photo", selectPhoto: 'Select photo',
name: "Name", name: 'Name',
login: "Login", login: 'Login',
password: "Password", password: 'Password',
email: "Email", email: 'Email',
tenant: "Tenant", tenant: 'Tenant',
role: "Role", role: 'Role',
status: "Status", status: 'Status',
nameMessage: "Enter name", nameMessage: 'Enter name',
loginMessage: "Enter login", loginMessage: 'Enter login',
passwordMessage: "Enter password", passwordMessage: 'Enter password',
emailMessage: "Enter email", emailMessage: 'Enter email',
emailErrorMessage: "Incorrect email", emailErrorMessage: 'Incorrect email',
tenantMessage: "Enter tenant", tenantMessage: 'Enter tenant',
roleMessage: "Choose role", roleMessage: 'Choose role',
statusMessage: "Choose status", statusMessage: 'Choose status',
addAccount: "Add account", addAccount: 'Add account',
save: "Save changes", save: 'Save changes',
newAccount: "New account", newAccount: 'New account',
ACTIVE: "Active", ACTIVE: 'Active',
DISABLED: "Disabled", DISABLED: 'Disabled',
BLOCKED: "Blocked", BLOCKED: 'Blocked',
DELETED: "Deleted", DELETED: 'Deleted',
OWNER: "Owner", OWNER: 'Owner',
ADMIN: "Admin", ADMIN: 'Admin',
EDITOR: "Editor", EDITOR: 'Editor',
VIEWER: "Viewer", VIEWER: 'Viewer',
nameLogin: "Name, login", nameLogin: 'Name, login',
createdAt: "Created", createdAt: 'Created',
saving: "Saving...", saving: 'Saving...',
createdAccountMessage: "User successfully created!", createdAccountMessage: 'User successfully created!',
editAccountMessage: "User successfully updated!", editAccountMessage: 'User successfully updated!',
you: "(You)", you: '(You)',
// nodes
startNode: "Start",
startNodeDescription: "Start",
connectToExisting: "Connect to existing",
editNode: "Editing a block",
connectTo: "Connect to {{nodeId}}",
ifElseNode: "IF - ELSE",
conditionIf: "If {{condition}}, then",
conditionElse: "Else",
appropriationNode: "Appropriation",
}, },
}, },
ru: { ru: {
translation: { translation: {
accounts: "Учетные записи", accounts: 'Учетные записи',
processDiagrams: "Схемы процессов", processDiagrams: 'Схемы процессов',
runningProcesses: "Запущенные процессы", runningProcesses: 'Запущенные процессы',
settings: "Настройки", settings: 'Настройки',
eventsList: "Справочкин событий", eventsList: 'Справочкин событий',
configuration: "Конфигурация", configuration: 'Конфигурация',
selectPhoto: "Выбрать фото", selectPhoto: 'Выбрать фото',
name: "Имя", name: 'Имя',
login: "Логин", login: 'Логин',
password: "Пароль", password: 'Пароль',
email: "Имейл", email: 'Имейл',
tenant: "Привязка", tenant: 'Привязка',
role: "Роль", role: 'Роль',
status: "Статус", status: 'Статус',
nameMessage: "Введите имя", nameMessage: 'Введите имя',
loginMessage: "Введите логин", loginMessage: 'Введите логин',
passwordMessage: "Введите пароль", passwordMessage: 'Введите пароль',
emailMessage: "Введите имейл", emailMessage: 'Введите имейл',
emailErrorMessage: "Некорректный имейл", emailErrorMessage: 'Некорректный имейл',
tenantMessage: "Введите привязку", tenantMessage: 'Введите привязку',
roleMessage: "Выберите роль", roleMessage: 'Выберите роль',
statusMessage: "Выберите статус", statusMessage: 'Выберите статус',
addAccount: "Добавить аккаунт", addAccount: 'Добавить аккаунт',
save: "Сохранить изменения", save: 'Сохранить изменения',
newAccount: "Новая учетная запись", newAccount: 'Новая учетная запись',
ACTIVE: "Активен", ACTIVE: 'Активен',
DISABLED: "Выключен", DISABLED: 'Выключен',
BLOCKED: "Заблокирован", BLOCKED: 'Заблокирован',
DELETED: "Удален", DELETED: 'Удален',
OWNER: "Владелец", OWNER: 'Владелец',
ADMIN: "Админ", ADMIN: 'Админ',
EDITOR: "Редактор", EDITOR: 'Редактор',
VIEWER: "Наблюдатель", VIEWER: 'Наблюдатель',
nameLogin: "Имя, Логин", nameLogin: 'Имя, Логин',
createdAt: "Создано", createdAt: 'Создано',
saving: "Сохранение...", saving: 'Сохранение...',
createdAccountMessage: "Пользователь успешно создан!", createdAccountMessage: 'Пользователь успешно создан!',
editAccountMessage: "Пользователь успешно обновлен!", editAccountMessage: 'Пользователь успешно обновлен!',
you: "(Вы)", you: '(Вы)',
// nodes
startNode: "Запуск",
startNodeDescription: "Включение",
connectToExisting: "Подключить к существующей",
editNode: "Редактирование блока",
connectTo: `Подключить к {{nodeId}}`,
ifElseNode: "ЕСЛИ - ТО",
conditionIf: "Если {{condition}}, то",
conditionElse: "Иначе",
appropriationNode: "Присвоение",
}, },
}, },
}, },

View File

@@ -1,12 +1,11 @@
import React from "react"; import React from 'react';
import ReactDOM from "react-dom/client"; import ReactDOM from 'react-dom/client';
import "@/index.css"; import '@/index.css';
import App from "@/App"; import App from '@/App';
import AppWrapper from "@/config/AppWrapper"; import AppWrapper from '@/config/AppWrapper';
import "@xyflow/react/dist/style.css";
const root = ReactDOM.createRoot( const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement document.getElementById('root') as HTMLElement
); );
root.render( root.render(

View File

@@ -2,12 +2,12 @@ import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { AccountStatus, AllUser, AllUserResponse } from "@/types/user"; import { AccountStatus, AllUser, AllUserResponse } from "@/types/user";
import Header from "@/components/Header"; import Header from "@/components/Header";
import ContentDrawer from "@/components/drawers/ContentDrawer"; import ContentDrawer from "@/components/ContentDrawer";
import UserCreate from "@/components/drawers/users/UserCreate"; import UserCreate from "@/components/UserCreate";
import { Avatar, Table } from "antd"; import { Avatar, Table } from "antd";
import { TableProps } from "antd/lib"; import { TableProps } from "antd/lib";
import { UserService } from "@/services/userService"; import { UserService } from "@/services/userService";
import UserEdit from "@/components/drawers/users/UserEdit"; import UserEdit from "@/components/UserEdit";
import { useSearchParams } from "react-router-dom"; import { useSearchParams } from "react-router-dom";
export default function AccountsPage() { export default function AccountsPage() {
@@ -201,7 +201,7 @@ export default function AccountsPage() {
<ContentDrawer <ContentDrawer
open={openCreate} open={openCreate}
closeDrawer={closeCreateDrawer} closeDrawer={closeCreateDrawer}
type="userCreate" type="create"
> >
<UserCreate getUsers={getUsers} closeDrawer={closeCreateDrawer} /> <UserCreate getUsers={getUsers} closeDrawer={closeCreateDrawer} />
</ContentDrawer> </ContentDrawer>
@@ -211,7 +211,7 @@ export default function AccountsPage() {
email={activeAccount?.email} email={activeAccount?.email}
open={openEdit} open={openEdit}
closeDrawer={closeEditDrawer} closeDrawer={closeEditDrawer}
type="userEdit" type="edit"
> >
<UserEdit userId={activeAccount?.id} closeDrawer={closeEditDrawer} /> <UserEdit userId={activeAccount?.id} closeDrawer={closeEditDrawer} />
</ContentDrawer> </ContentDrawer>

View File

@@ -1,22 +1,22 @@
/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable react-hooks/exhaustive-deps */
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from 'react';
import { Layout } from "antd"; import { Layout } from 'antd';
import Sider from "antd/es/layout/Sider"; import Sider from 'antd/es/layout/Sider';
import { Route, Routes, useLocation, useNavigate } from "react-router-dom"; import { Route, Routes, useLocation, useNavigate } from 'react-router-dom';
import SiderMenu from "@/components/SiderMenu"; import SiderMenu from '@/components/SiderMenu';
import ProcessDiagramPage from "./ProcessDiagramPage"; import ProcessDiagramPage from './ProcessDiagramPage';
import AccountsPage from "./AccountsPage"; import RunningProcessesPage from './RunningProcessesPage';
import ConfigurationPage from "./ConfigurationPage"; import AccountsPage from './AccountsPage';
import EventsListPage from "./EventsListPage"; import EventsListPage from './EventsListPage';
import RunningProcessesPage from "./RunningProcessesPage"; import ConfigurationPage from './ConfigurationPage';
export default function MainLayout() { export default function MainLayout() {
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const [collapsed, setCollapsed] = useState(false); const [collapsed, setCollapsed] = useState(false);
const [selectedKey, setSelectedKey] = useState("1"); const [selectedKey, setSelectedKey] = useState('1');
const [width, setWidth] = useState<number | string>("15%"); const [width, setWidth] = useState<number | string>('15%');
const [collapsedWidth, setCollapsedWidth] = useState(50); const [collapsedWidth, setCollapsedWidth] = useState(50);
const calculateWidths = () => { const calculateWidths = () => {
@@ -29,24 +29,24 @@ export default function MainLayout() {
useEffect(() => { useEffect(() => {
calculateWidths(); calculateWidths();
window.addEventListener("resize", calculateWidths); window.addEventListener('resize', calculateWidths);
return () => window.removeEventListener("resize", calculateWidths); return () => window.removeEventListener('resize', calculateWidths);
}, []); }, []);
useEffect(() => { useEffect(() => {
if (location.pathname === "/") { if (location.pathname === '/') {
navigate("/process-diagram"); navigate('/process-diagram');
} }
setSelectedKey(location.pathname); setSelectedKey(location.pathname);
}, [location.pathname]); }, [location.pathname]);
function hangleMenuClick(e: any) { function hangleMenuClick(e: any) {
const key = e.key; const key = e.key;
if (key === "toggle") { if (key === 'toggle') {
setCollapsed(!collapsed); setCollapsed(!collapsed);
return; return;
} }
if (key === "divider") { if (key === 'divider') {
return; return;
} }
@@ -55,7 +55,7 @@ export default function MainLayout() {
} }
return ( return (
<Layout style={{ minHeight: "100vh" }}> <Layout style={{ minHeight: '100vh' }}>
<Sider <Sider
className="sider" className="sider"
collapsible collapsible

View File

@@ -1,33 +1,11 @@
import Header from "@/components/Header"; import Header from '@/components/Header';
import { useTranslation } from "react-i18next"; import { useTranslation } from 'react-i18next';
import ReactFlowDrawer from "@/components/ReactFlowDrawer";
import { useState } from "react";
import ContentDrawer from "@/components/drawers/ContentDrawer";
export default function ProcessDiagramPage() { export default function ProcessDiagramPage() {
const { t } = useTranslation(); const { t } = useTranslation();
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const showDrawer = () => setIsDrawerOpen(true);
const closeDrawer = () => {
setIsDrawerOpen(false);
};
return ( return (
<> <>
<Header title={t("processDiagrams")} /> <Header title={t('processDiagrams')} />
<div style={{ width: "100%", height: "100%" }}>
<ReactFlowDrawer showDrawer={showDrawer} />
</div>
<ContentDrawer
open={isDrawerOpen}
closeDrawer={closeDrawer}
children={undefined}
type={"nodeEdit"}
></ContentDrawer>
</> </>
); );
} }

View File

@@ -1,25 +0,0 @@
import { Edge, Node } from "@xyflow/react";
import { create } from "zustand";
import { devtools } from "zustand/middleware";
type NodeStoreState = {
node: Node;
edge: Edge;
loading: boolean;
};
type NodeStoreActions = {
addNode: (node: Node) => void;
addEdge: (edge: Edge) => void;
removeNode: (nodeId: string) => void;
removeEdge: (edgeId: string) => void;
};
type NodeStore = NodeStoreState & NodeStoreActions;
export const useNodeStore = create<NodeStore>()(
devtools((set) => ({
nodes: [],
edges: [],
}))
);

View File

@@ -1,8 +0,0 @@
function edgeTitleGenerator(counter: number) {
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const num = Math.ceil(counter / chars.length);
const letterIndex = (counter - 1) % chars.length;
return `${chars[letterIndex]}${num}`;
}
export { edgeTitleGenerator };