Merge branch 'main' into Owner-of-the-car-enhance

This commit is contained in:
2025-10-03 00:09:50 +08:00
21 changed files with 514 additions and 141 deletions

8
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

14
.idea/Findreve.iml generated Normal file
View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.12 (Findreve)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
</module>

13
.idea/dataSources.xml generated Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="data.db" uuid="c73289bb-3157-4ec3-b3f4-744da38bc64a">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<remarks>Findreve Data</remarks>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/data.db</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

View File

@@ -0,0 +1,29 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyInterpreterInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PyMissingTypeHintsInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PyPep8Inspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="E402" />
<option value="E713" />
<option value="E271" />
<option value="E302" />
<option value="E265" />
<option value="W292" />
</list>
</option>
</inspection_tool>
<inspection_tool class="PyPep8NamingInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false">
<option name="ignoredErrors">
<list>
<option value="N802" />
<option value="N806" />
</list>
</option>
</inspection_tool>
<inspection_tool class="Stylelint" enabled="true" level="ERROR" enabled_by_default="true" />
</profile>
</component>

View File

@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

17
.idea/material_theme_project_new.xml generated Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MaterialThemeProjectNewConfig">
<option name="metadata">
<MTProjectMetadataState>
<option name="migrated" value="true" />
<option name="pristineConfig" value="false" />
<option name="userId" value="4dc9a07a:18f958e7499:-7ffe" />
</MTProjectMetadataState>
</option>
<option name="titleBarState">
<MTProjectTitleBarConfigState>
<option name="overrideColor" value="false" />
</MTProjectTitleBarConfigState>
</option>
</component>
</project>

7
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.12 (Findreve)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (Findreve)" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Findreve.iml" filepath="$PROJECT_DIR$/.idea/Findreve.iml" />
</modules>
</component>
</project>

26
.idea/runConfigurations/Findreve.xml generated Normal file
View File

@@ -0,0 +1,26 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="启动Findreve" type="PythonConfigurationType" factoryName="Python">
<module name="Findreve" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="SDK_NAME" value="Python 3.12 (Findreve)" />
<option name="WORKING_DIRECTORY" value="C:\Users\admin\Documents\Code\Findreve" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/main.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
</component>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@@ -8,6 +8,9 @@
},
{
"path": "../Findreve-NiceGUI"
},
{
"path": "../vein-based-iov-simulator-main"
}
],
"settings": {}

View File

@@ -67,6 +67,9 @@ Upon launch, Findreve will create a SQLite database in the project's root direct
display the administrator's account and password in the console.
## 构建
> 当前版本的 Findreve Core 无法正常工作,因为我们正在尝试[重构数据库组件以使用ORM](https://github.com/Findreve/Findreve/issues/8)
你需要安装Python 3.8 以上的版本。然后clone 本仓库到您的服务器并解压,然后安装下面的依赖:
You need to have Python 3.8 or higher installed on your server. Then, clone this repository

20
app.py
View File

@@ -5,21 +5,11 @@ from contextlib import asynccontextmanager
from routes import (session, admin, object)
import model.database
import os, asyncio
import pkg.conf
# 初始化数据库
asyncio.run(model.database.Database().init_db())
# 定义程序参数
APP_NAME: str = 'Findreve'
VERSION: str = '2.0.0-ootc'
summary='标记、追踪与找回 —— 就这么简单。'
description='Findreve 是一款强大且直观的解决方案,旨在帮助您管理个人物品,'\
'并确保丢失后能够安全找回。每个物品都会被分配一个 唯一 ID '\
'并生成一个 安全链接 ,可轻松嵌入到 二维码 或 NFC 标签 中。'\
'当扫描该代码时,会将拾得者引导至一个专门的网页,上面显示物品详情和您的联系信息,'\
'既保障隐私又便于沟通。无论您是在管理个人物品还是专业资产,'\
'Findreve 都能以高效、简便的方式弥合丢失与找回之间的距离。'
# Findreve 的生命周期
@asynccontextmanager
async def lifespan(app: FastAPI):
@@ -28,10 +18,10 @@ async def lifespan(app: FastAPI):
# 定义 Findreve 服务器
app = FastAPI(
title=APP_NAME,
version=VERSION,
summary=summary,
description=description,
title=pkg.conf.APP_NAME,
version=pkg.conf.VERSION,
summary=pkg.conf.summary,
description=pkg.conf.description,
lifespan=lifespan
)

View File

@@ -1 +1,3 @@
from . import token
from . import token
from .setting import Setting
from .object import Object

151
model/base.py Normal file
View File

@@ -0,0 +1,151 @@
# model/base.py
from datetime import datetime, timezone
from typing import Optional, Type, TypeVar, Union, Literal, List
from sqlalchemy import DateTime, BinaryExpression, ClauseElement
from sqlalchemy.orm import selectinload
from sqlalchemy.ext.asyncio.session import AsyncSession
from sqlalchemy.ext.asyncio import AsyncAttrs
from sqlmodel import SQLModel, Field, select, Relationship
from sqlalchemy.sql._typing import _OnClauseArgument
B = TypeVar('B', bound='TableBase')
M = TypeVar('M', bound='SQLModel')
utcnow = lambda: datetime.now(tz=timezone.utc)
class TableBase(AsyncAttrs, SQLModel):
__abstract__ = True
created_at: datetime = Field(
default_factory=utcnow,
description="创建时间",
)
updated_at: datetime = Field(
sa_type=DateTime,
description="更新时间",
sa_column_kwargs={"default": utcnow, "onupdate": utcnow},
default_factory=utcnow
)
deleted_at: Optional[datetime] = Field(
default=None,
description="删除时间",
sa_column={"nullable": True}
)
@classmethod
async def add(
cls: Type[B],
session: AsyncSession,
instances: B | List[B],
refresh: bool = True
) -> B | List[B]:
is_list = isinstance(instances, list)
if is_list:
session.add_all(instances)
else:
session.add(instances)
await session.commit()
if refresh:
if is_list:
for i in instances:
await session.refresh(i)
else:
await session.refresh(instances)
return instances
async def save(
self: B,
session: AsyncSession,
load: Union[Relationship, None] = None, # 设默认值,避免必须传
):
session.add(self)
await session.commit()
if load is not None:
cls = type(self)
return await cls.get(session, cls.id == self.id, load=load) # 若该模型没有 id请别用 load 模式
else:
await session.refresh(self)
return self
async def update(
self: B,
session: AsyncSession,
other: M,
extra_data: dict = None,
exclude_unset: bool = True,
) -> B:
self.sqlmodel_update(
other.model_dump(exclude_unset=exclude_unset),
update=extra_data
)
session.add(self)
await session.commit()
await session.refresh(self)
return self
@classmethod
async def delete(
cls: Type[B],
session: AsyncSession,
instance: B | list[B],
) -> None:
if isinstance(instance, list):
for inst in instance:
await session.delete(inst)
else:
await session.delete(instance)
await session.commit()
@classmethod
async def get(
cls: Type[B],
session: AsyncSession,
condition: BinaryExpression | ClauseElement | None,
*,
offset: int | None = None,
limit: int | None = None,
fetch_mode: Literal["one", "first", "all"] = "first",
join: Type[B] | tuple[Type[B], _OnClauseArgument] | None = None,
options: list | None = None,
load: Union[Relationship, None] = None,
order_by: list[ClauseElement] | None = None
) -> B | List[B] | None:
statement = select(cls)
if condition is not None:
statement = statement.where(condition)
if join is not None:
statement = statement.join(*join)
if options:
statement = statement.options(*options)
if load:
statement = statement.options(selectinload(load))
if order_by is not None:
statement = statement.order_by(*order_by)
if offset:
statement = statement.offset(offset)
if limit:
statement = statement.limit(limit)
result = await session.exec(statement)
if fetch_mode == "one":
return result.one()
elif fetch_mode == "first":
return result.first()
elif fetch_mode == "all":
return list(result.all())
else:
raise ValueError(f"无效的 fetch_mode: {fetch_mode}")
@classmethod
async def get_exist_one(cls: Type[B], session: AsyncSession, id: int, load: Union[Relationship, None] = None) -> B:
instance = await cls.get(session, cls.id == id, load=load)
if not instance:
from fastapi import HTTPException
raise HTTPException(status_code=404, detail="Not found")
return instance
# 需要“自增 id 主键”的模型才混入它Setting 不混入
class IdMixin(SQLModel):
id: Optional[int] = Field(default=None, primary_key=True, description="主键ID")

View File

@@ -1,19 +1,31 @@
'''
Author: 于小丘 海枫
Date: 2024-10-02 15:23:34
LastEditors: Yuerchu admin@yuxiaoqiu.cn
LastEditTime: 2024-11-29 20:05:03
FilePath: /Findreve/model.py
Description: Findreve 数据库组件 model
Copyright (c) 2018-2024 by 于小丘Yuerchu, All Rights Reserved.
'''
from contextlib import asynccontextmanager
import aiosqlite
from datetime import datetime
import tool
import logging
from typing import Literal, Optional
from typing import Optional
from sqlmodel import SQLModel
from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
from sqlmodel.ext.asyncio.session import AsyncSession
from sqlalchemy.orm import sessionmaker
from typing import AsyncGenerator
import warnings
from .migration import migration
ASYNC_DATABASE_URL = "sqlite+aiosqlite:///data.db"
engine: AsyncEngine = create_async_engine(
ASYNC_DATABASE_URL,
echo=True,
connect_args={
"check_same_thread": False
} if ASYNC_DATABASE_URL.startswith("sqlite") else None,
future=True,
# pool_size=POOL_SIZE,
# max_overflow=64,
)
_async_session_factory = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
# 数据库类
class Database:
@@ -24,104 +36,28 @@ class Database:
db_path: str = "data.db" # db_path 数据库文件路径,默认为 data.db
):
self.db_path = db_path
async def init_db(self):
"""初始化数据库和表"""
logging.info("开始初始化数据库和表")
create_objects_table = """
CREATE TABLE IF NOT EXISTS fr_objects (
id INTEGER PRIMARY KEY AUTOINCREMENT,
type TEXT NOT NULL,
key TEXT NOT NULL,
name TEXT NOT NULL,
icon TEXT,
status TEXT,
phone TEXT,
context TEXT,
find_ip TEXT,
create_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
lost_at TIMESTAMP
)
"""
create_settings_table = """
CREATE TABLE IF NOT EXISTS fr_settings (
type TEXT,
name TEXT PRIMARY KEY,
value TEXT
)
"""
async with aiosqlite.connect(self.db_path) as db:
logging.info("连接到数据库")
await db.execute(create_objects_table)
logging.info("创建或验证fr_objects表")
await db.execute(create_settings_table)
logging.info("创建或验证fr_settings表")
# 初始化设置表数据
async with db.execute("SELECT name FROM fr_settings WHERE name = 'version'") as cursor:
if not await cursor.fetchone():
await db.execute(
"INSERT INTO fr_settings (type, name, value) VALUES (?, ?, ?)",
('string', 'version', '1.0.0')
)
logging.info("插入初始版本信息: version 1.0.0")
async with db.execute("SELECT name FROM fr_settings WHERE name = 'ver'") as cursor:
if not await cursor.fetchone():
await db.execute(
"INSERT INTO fr_settings (type, name, value) VALUES (?, ?, ?)",
('int', 'ver', '1')
)
logging.info("插入初始版本号: ver 1")
async with db.execute("SELECT name FROM fr_settings WHERE name = 'account'") as cursor:
if not await cursor.fetchone():
account = 'admin@yuxiaoqiu.cn'
await db.execute(
"INSERT INTO fr_settings (type, name, value) VALUES (?, ?, ?)",
('string', 'account', account)
)
logging.info(f"插入初始账号信息: {account}")
print(f"账号: {account}")
async with db.execute("SELECT name FROM fr_settings WHERE name = 'password'") as cursor:
if not await cursor.fetchone():
password = tool.generate_password()
hashed_password = tool.hash_password(password)
await db.execute(
"INSERT INTO fr_settings (type, name, value) VALUES (?, ?, ?)",
('string', 'password', hashed_password)
)
logging.info("插入初始密码信息")
print(f"密码(请牢记,后续不再显示): {password}")
async with db.execute("SELECT name FROM fr_settings WHERE name = 'SECRET_KEY'") as cursor:
if not await cursor.fetchone():
secret_key = tool.generate_password(64)
await db.execute(
"INSERT INTO fr_settings (type, name, value) VALUES (?, ?, ?)",
('string', 'SECRET_KEY', secret_key)
)
logging.info("插入初始密钥信息")
await db.commit()
logging.info("数据库初始化完成并提交更改")
async def add_object(
self,
key: str,
type: Literal['normal', 'car'],
name: str,
icon: str = None,
phone: str = None,
):
@staticmethod
@asynccontextmanager
async def get_session() -> AsyncGenerator[AsyncSession, None]:
async with _async_session_factory() as session:
yield session
async def init_db(
self,
url: str = ASYNC_DATABASE_URL
):
"""创建数据库结构"""
async with engine.begin() as conn:
await conn.run_sync(SQLModel.metadata.create_all)
async with self.get_session() as session:
await migration(session) # 执行迁移脚本
async def add_object(self, key: str, name: str, icon: str = None, phone: str = None):
"""
添加新对象
:param type: 对象类型
:param key: 序列号
:param name: 名称
:param icon: 图标
@@ -135,15 +71,14 @@ class Database:
now = datetime.now()
now = now.strftime("%Y-%m-%d %H:%M:%S")
await db.execute(
"INSERT INTO fr_objects (key, name, icon, phone, create_at, status, type) VALUES (?, ?, ?, ?, ?, 'ok', ?)",
(key, name, icon, phone, now, type)
"INSERT INTO fr_objects (key, name, icon, phone, create_at, status) VALUES (?, ?, ?, ?, ?, 'ok')",
(key, name, icon, phone, now)
)
await db.commit()
async def update_object(
self,
id: int,
type: Literal['normal', 'car'] = None,
key: str = None,
name: str = None,
icon: str = None,
@@ -151,13 +86,11 @@ class Database:
phone: int = None,
lost_description: Optional[str] = None,
find_ip: Optional[str] = None,
lost_time: Optional[str] = None
):
lost_time: Optional[str] = None):
"""
更新对象信息
:param id: 对象ID
:param type: 对象类型
:param key: 序列号
:param name: 名称
:param icon: 图标
@@ -185,18 +118,13 @@ class Database:
f"phone = COALESCE(?, phone), "
f"context = COALESCE(?, context), "
f"find_ip = COALESCE(?, find_ip), "
f"lost_at = COALESCE(?, lost_at), "
f"type = COALESCE(?, type) "
f"lost_at = COALESCE(?, lost_at) "
f"WHERE id = ?",
(key, name, icon, status, phone, lost_description, find_ip, lost_time, type, id)
(key, name, icon, status, phone, lost_description, find_ip, lost_time, id)
)
await db.commit()
async def get_object(
self,
id: int = None,
key: str = None
):
async def get_object(self, id: int = None, key: str = None):
"""
获取对象

84
model/migration.py Normal file
View File

@@ -0,0 +1,84 @@
from typing import Sequence
from sqlmodel import select
from .setting import Setting
import tool
default_settings: list[Setting] = [
Setting(type='string', name='version', value='1.0.0'),
Setting(type='int', name='ver', value='1'),
Setting(type='string', name='account', value='admin@yuxiaoqiu.cn'),
]
async def migration(session):
# 先准备基础配置
settings: list[Setting] = default_settings.copy()
# 生成初始密码与密钥
admin_password = tool.generate_password()
print(f"密码(请牢记,后续不再显示): {admin_password}")
settings.append(Setting(type='string', name='password', value=tool.hash_password(admin_password)))
settings.append(Setting(type='string', name='SECRET_KEY', value=tool.generate_password(64)))
# 读取库里已存在的 name避免主键冲突
names = [s.name for s in settings]
exist_stmt = select(Setting.name).where(Setting.name.in_(names))
exist_rs = await session.exec(exist_stmt)
existed: set[str] = set(exist_rs.all())
to_insert = [s for s in settings if s.name not in existed]
if to_insert:
# 使用你写好的通用新增方法(是类方法),并传入会话
await Setting.add(session, to_insert, refresh=False)
"""
# 初始化设置表数据
async with db.execute("SELECT name FROM fr_settings WHERE name = 'version'") as cursor:
if not await cursor.fetchone():
await db.execute(
"INSERT INTO fr_settings (type, name, value) VALUES (?, ?, ?)",
('string', 'version', '1.0.0')
)
logging.info("插入初始版本信息: version 1.0.0")
async with db.execute("SELECT name FROM fr_settings WHERE name = 'ver'") as cursor:
if not await cursor.fetchone():
await db.execute(
"INSERT INTO fr_settings (type, name, value) VALUES (?, ?, ?)",
('int', 'ver', '1')
)
logging.info("插入初始版本号: ver 1")
async with db.execute("SELECT name FROM fr_settings WHERE name = 'account'") as cursor:
if not await cursor.fetchone():
account = 'admin@yuxiaoqiu.cn'
await db.execute(
"INSERT INTO fr_settings (type, name, value) VALUES (?, ?, ?)",
('string', 'account', account)
)
logging.info(f"插入初始账号信息: {account}")
print(f"账号: {account}")
async with db.execute("SELECT name FROM fr_settings WHERE name = 'password'") as cursor:
if not await cursor.fetchone():
password = tool.generate_password()
hashed_password = tool.hash_password(password)
await db.execute(
"INSERT INTO fr_settings (type, name, value) VALUES (?, ?, ?)",
('string', 'password', hashed_password)
)
logging.info("插入初始密码信息")
print(f"密码(请牢记,后续不再显示): {password}")
async with db.execute("SELECT name FROM fr_settings WHERE name = 'SECRET_KEY'") as cursor:
if not await cursor.fetchone():
secret_key = tool.generate_password(64)
await db.execute(
"INSERT INTO fr_settings (type, name, value) VALUES (?, ?, ?)",
('string', 'SECRET_KEY', secret_key)
)
logging.info("插入初始密钥信息")
await db.commit()
logging.info("数据库初始化完成并提交更改")
"""

46
model/object.py Normal file
View File

@@ -0,0 +1,46 @@
# my_project/models/download.py
from typing import Literal, Optional, TYPE_CHECKING
from sqlmodel import Field, Column, SQLModel, String, DateTime
from .base import TableBase, IdMixin
from datetime import datetime
"""
原建表语句:
CREATE TABLE IF NOT EXISTS fr_objects (
id INTEGER PRIMARY KEY AUTOINCREMENT,
key TEXT NOT NULL,
name TEXT NOT NULL,
icon TEXT,
status TEXT,
phone TEXT,
context TEXT,
find_ip TEXT,
create_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
lost_at TIMESTAMP
"""
if TYPE_CHECKING:
pass
class Object(IdMixin, TableBase, table=True):
__tablename__ = 'fr_objects'
key: str = Field(index=True, nullable=False, description="物品外部ID")
type: Literal['object', 'box'] = Field(
default='object',
description="物品类型",
sa_column=Column(String, default='object', nullable=False)
)
name: str = Field(nullable=False, description="物品名称")
icon: Optional[str] = Field(default=None, description="物品图标")
status: Optional[str] = Field(default=None, description="物品状态")
phone: Optional[str] = Field(default=None, description="联系电话")
context: Optional[str] = Field(default=None, description="物品描述")
find_ip: Optional[str] = Field(default=None, description="最后一次发现的IP地址")
lost_at: Optional[datetime] = Field(
default=None,
description="物品标记为丢失的时间",
sa_column=Column(DateTime, nullable=True)
)

23
model/setting.py Normal file
View File

@@ -0,0 +1,23 @@
# model/setting.py
from typing import TYPE_CHECKING, Optional
from sqlmodel import Field
from .base import TableBase
"""
原表:
CREATE TABLE IF NOT EXISTS fr_settings (
type TEXT,
name TEXT PRIMARY KEY,
value TEXT
)
"""
if TYPE_CHECKING:
pass
class Setting(TableBase, table=True):
__tablename__ = 'fr_settings'
type: str = Field(index=True, nullable=False, description="设置类型")
name: str = Field(primary_key=True, nullable=False, description="设置名称") # name 为唯一主键
value: Optional[str] = Field(description="设置值")

9
pkg/conf.py Normal file
View File

@@ -0,0 +1,9 @@
APP_NAME: str = 'Findreve'
VERSION: str = '2.0.0'
summary='标记、追踪与找回 —— 就这么简单。'
description='Findreve 是一款强大且直观的解决方案,旨在帮助您管理个人物品,'\
'并确保丢失后能够安全找回。每个物品都会被分配一个 唯一 ID '\
'并生成一个 安全链接 ,可轻松嵌入到 二维码 或 NFC 标签 中。'\
'当扫描该代码时,会将拾得者引导至一个专门的网页,上面显示物品详情和您的联系信息,'\
'既保障隐私又便于沟通。无论您是在管理个人物品还是专业资产,'\
'Findreve 都能以高效、简便的方式弥合丢失与找回之间的距离。'

View File

@@ -7,7 +7,7 @@ from fastapi import APIRouter
import jwt, JWT
from model.token import Token
from model import database
from model import Setting, database
from tool import verify_password
Router = APIRouter(tags=["令牌 session"])
@@ -26,8 +26,8 @@ def create_access_token(data: dict, expires_delta: timedelta | None = None):
# 验证账号密码
async def authenticate_user(username: str, password: str):
# 验证账号和密码
account = await database.Database().get_setting('account')
stored_password = await database.Database().get_setting('password')
account = await Setting.get('setting', 'account')
stored_password = await Setting.get('setting', 'password')
if account != username or not verify_password(stored_password, password):
return False