Origin commit
This commit is contained in:
commit
0974a12f10
|
@ -0,0 +1,4 @@
|
||||||
|
**/__pycache__
|
||||||
|
config.toml
|
||||||
|
debug_cmd.lst
|
||||||
|
venv
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
// 使用 IntelliSense 了解相关属性。
|
||||||
|
// 悬停以查看现有属性的描述。
|
||||||
|
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Python 调试程序: 当前文件",
|
||||||
|
"type": "debugpy",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "main.py",
|
||||||
|
"console": "integratedTerminal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"python.analysis.autoImportCompletions": true,
|
||||||
|
"python.analysis.typeCheckingMode": "off"
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
runmode = "webapi"
|
||||||
|
|
||||||
|
[webapi]
|
||||||
|
host = "127.0.0.1"
|
||||||
|
port = 3032
|
||||||
|
|
||||||
|
[console]
|
||||||
|
emacsmode = true
|
||||||
|
softstop = true
|
||||||
|
|
||||||
|
[db]
|
||||||
|
host = "127.0.0.1"
|
||||||
|
port = 3306
|
||||||
|
user = "root"
|
||||||
|
passwd = "passwd"
|
||||||
|
dbname = "myjsonDB"
|
||||||
|
|
||||||
|
[log]
|
||||||
|
path = "log.log"
|
||||||
|
level = "info"
|
||||||
|
color = true
|
|
@ -0,0 +1,7 @@
|
||||||
|
[2024-07-14 20:01:59,825][INFO] Server start
|
||||||
|
[2024-07-14 20:01:59,825][INFO] Connect database
|
||||||
|
[2024-07-14 20:01:59,854][INFO] Server version:8.2.0
|
||||||
|
[2024-07-14 20:01:59,858][INFO] Use database
|
||||||
|
[2024-07-14 20:01:59,861][INFO] start webapi
|
||||||
|
[2024-07-14 20:01:59,862][INFO] Search web plugins
|
||||||
|
[2024-07-14 20:01:59,862][INFO] + Load ezapi.py
|
|
@ -0,0 +1,35 @@
|
||||||
|
from src import log
|
||||||
|
from src import core
|
||||||
|
from src import term
|
||||||
|
from src import config
|
||||||
|
from src import webcore
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
log.log_init()
|
||||||
|
|
||||||
|
|
||||||
|
loop = asyncio.new_event_loop()
|
||||||
|
|
||||||
|
|
||||||
|
async def start_cli():
|
||||||
|
await log.info("start cli")
|
||||||
|
await term.debug_term()
|
||||||
|
|
||||||
|
|
||||||
|
async def start_api():
|
||||||
|
await log.info("start webapi")
|
||||||
|
await webcore.start()
|
||||||
|
|
||||||
|
|
||||||
|
async def init():
|
||||||
|
await log.info("Server start")
|
||||||
|
await core.connect_realdb()
|
||||||
|
if (config.RUN_MODE == "console"):
|
||||||
|
await start_cli()
|
||||||
|
elif (config.RUN_MODE == "webapi"):
|
||||||
|
await start_api()
|
||||||
|
else:
|
||||||
|
await log.err("Unknown run mode: "+str(config.RUN_MODE))
|
||||||
|
|
||||||
|
|
||||||
|
loop.run_until_complete(init())
|
|
@ -0,0 +1,24 @@
|
||||||
|
import tomllib
|
||||||
|
|
||||||
|
with open("config.toml", 'rb') as file:
|
||||||
|
configs: dict = tomllib.load(file)
|
||||||
|
|
||||||
|
RUN_MODE: bool = configs.get("runmode", "webapi")
|
||||||
|
|
||||||
|
LOGPATH: str = configs.get("log", {}).get("path", "log.log")
|
||||||
|
LOGLEVEL: str = configs.get("log", {}).get("level", "info")
|
||||||
|
if (LOGLEVEL not in ["debug", "info", "warning", "err"]):
|
||||||
|
LOGLEVEL = "info"
|
||||||
|
OUTPUT_COLORFUL: bool = configs.get("log", {}).get("color", True)
|
||||||
|
|
||||||
|
DB_SERVER: str = configs.get("db", {}).get("host", "127.0.0.1")
|
||||||
|
DB_PORT: int = configs.get("db", {}).get("port", 3306)
|
||||||
|
DB_USER: str = configs.get("db", {}).get("user", "root")
|
||||||
|
DB_PASSWD: str = configs.get("db", {}).get("passwd", "<no password>")
|
||||||
|
DB_NAME: str = configs.get("db", {}).get("dbname", "myjsonDB")
|
||||||
|
|
||||||
|
USE_EMACE_MOD: bool = configs.get("console", {}).get("emacsmode", True)
|
||||||
|
SIGTERM_CMD: bool = configs.get("console", {}).get("softstop", True)
|
||||||
|
|
||||||
|
API_HOST: bool = configs.get("webapi", {}).get("host", "127.0.0.1")
|
||||||
|
API_PORT: bool = configs.get("webapi", {}).get("port", 3032)
|
|
@ -0,0 +1,557 @@
|
||||||
|
from src import log
|
||||||
|
from src import sqllink
|
||||||
|
from src import config
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import pickle
|
||||||
|
from typing import Any, Tuple
|
||||||
|
|
||||||
|
|
||||||
|
async def connect_realdb() -> None:
|
||||||
|
"""
|
||||||
|
Connect to a mysql database and save connect.
|
||||||
|
"""
|
||||||
|
global connects
|
||||||
|
await log.info("Connect database")
|
||||||
|
try:
|
||||||
|
connects = await sqllink.connect_db()
|
||||||
|
except:
|
||||||
|
await log.break_err("Connect error")
|
||||||
|
sys.exit(2)
|
||||||
|
async with connects.acquire() as conn:
|
||||||
|
await log.info("Server version:"+str(conn.server_version))
|
||||||
|
async with conn.cursor() as cursor:
|
||||||
|
await cursor.execute("SHOW TABLES LIKE 'sys_db_list';")
|
||||||
|
result = await cursor.fetchall()
|
||||||
|
if (len(result) == 0):
|
||||||
|
await log.info("Init database")
|
||||||
|
await cursor.execute("""
|
||||||
|
CREATE TABLE sys_db_list(
|
||||||
|
name VARCHAR(50) PRIMARY KEY NOT NULL
|
||||||
|
);
|
||||||
|
""")
|
||||||
|
await cursor.execute("""
|
||||||
|
CREATE TABLE sys_config(
|
||||||
|
name VARCHAR(50) PRIMARY KEY NOT NULL,
|
||||||
|
val VARCHAR(200) NOT NULL
|
||||||
|
);
|
||||||
|
""")
|
||||||
|
else:
|
||||||
|
await log.info("Use database")
|
||||||
|
|
||||||
|
|
||||||
|
async def check_jsondb(name: str) -> bool | None:
|
||||||
|
"""
|
||||||
|
Check a jsondb if it has been created.
|
||||||
|
@return: True(created) | False(not create) | None(error)
|
||||||
|
"""
|
||||||
|
await log.info("Check jsondb: "+str(name))
|
||||||
|
res = re.search(r'[0-9A-Za-z_]{2,20}', name)
|
||||||
|
if res is None or res.group() != name:
|
||||||
|
await log.warn("Check database name error: "+str(name))
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def callback(result):
|
||||||
|
if (len(result) == 0):
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
async def err_callback(err_info):
|
||||||
|
await log.warn("Other error on create database: "+str(err_info))
|
||||||
|
return None
|
||||||
|
return await sqllink.execute_cmd(connects, command="SELECT * FROM sys_db_list WHERE name='"+name+"';", callback=callback, err_callback=err_callback)
|
||||||
|
|
||||||
|
|
||||||
|
async def list_jsondb() -> Tuple[str] | None:
|
||||||
|
"""
|
||||||
|
## List all jsondb(s).
|
||||||
|
@return: `tuple | None(error)`
|
||||||
|
"""
|
||||||
|
await log.info("list jsondb")
|
||||||
|
|
||||||
|
async def callback(result):
|
||||||
|
return [i[0] for i in result]
|
||||||
|
|
||||||
|
async def err_callback(err_info):
|
||||||
|
await log.warn("Other error on list databases: "+str(err_info))
|
||||||
|
return None
|
||||||
|
return await sqllink.execute_cmd(connects, command="SELECT * FROM sys_db_list;", callback=callback, err_callback=err_callback)
|
||||||
|
|
||||||
|
|
||||||
|
async def create_jsondb(name: str) -> tuple[int, str | None]:
|
||||||
|
"""
|
||||||
|
## Create a jsondb.
|
||||||
|
@return: `tuple[errorlevel:int, errormsg:str|None]`
|
||||||
|
"""
|
||||||
|
await log.info("Create jsondb: "+str(name))
|
||||||
|
res = re.search(r'[0-9A-Za-z_]{2,20}', name)
|
||||||
|
if res is None or res.group() != name:
|
||||||
|
await log.warn("Create database name error: "+str(name))
|
||||||
|
return (1, "Name error: "+str(name))
|
||||||
|
|
||||||
|
async def callback():
|
||||||
|
return (0, None)
|
||||||
|
|
||||||
|
async def err_callback(err_info):
|
||||||
|
if ("sys_db_list.PRIMARY" in err_info):
|
||||||
|
await log.warn("Duplicate database names: "+str(name))
|
||||||
|
return (1, "Duplicate database names: "+str(name))
|
||||||
|
else:
|
||||||
|
await log.warn("Other error on create database: "+str(err_info))
|
||||||
|
return (2, "Other error: "+str(err_info))
|
||||||
|
return await sqllink.execute_cmd(connects, command="INSERT INTO sys_db_list (name) VALUES ('"+name+"');", commit=True, callback=callback, err_callback=err_callback)
|
||||||
|
|
||||||
|
|
||||||
|
async def create_table(dbname: str, name: str) -> tuple[int, str | None]:
|
||||||
|
"""
|
||||||
|
## Create a table in a jsondb.
|
||||||
|
@return: `tuple[errorlevel:int, errormsg:str|None]`
|
||||||
|
"""
|
||||||
|
await log.info("Create table '"+str(name)+"' in '"+str(dbname)+"'")
|
||||||
|
|
||||||
|
res = re.search(r'[0-9A-Za-z_]{2,20}', dbname)
|
||||||
|
if res is None or res.group() != dbname:
|
||||||
|
await log.warn("Database name error: "+str(dbname))
|
||||||
|
return (1, "DB name error"+str(dbname))
|
||||||
|
|
||||||
|
res = re.search(r'[0-9A-Za-z_]{2,20}', name)
|
||||||
|
if res is None or res.group() != name:
|
||||||
|
await log.warn("Create table database name error: "+str(name))
|
||||||
|
return (2, "Name error: "+str(name))
|
||||||
|
|
||||||
|
checks = await check_jsondb(dbname)
|
||||||
|
if checks == None:
|
||||||
|
return (3, "Other check error")
|
||||||
|
elif checks == False:
|
||||||
|
await log.warn("Create table cannot find database: "+str(dbname))
|
||||||
|
return (3, "Cannot find database: "+str(dbname))
|
||||||
|
|
||||||
|
async def callback():
|
||||||
|
return (0, None)
|
||||||
|
|
||||||
|
async def err_callback(err_info):
|
||||||
|
if ("sys_db_list.PRIMARY" in err_info):
|
||||||
|
await log.warn("Duplicate database names: "+str(name))
|
||||||
|
return (4, "Duplicate database names: "+str(name))
|
||||||
|
else:
|
||||||
|
await log.warn("Other error on create table: "+str(err_info))
|
||||||
|
return (4, "Other error: "+str(err_info))
|
||||||
|
|
||||||
|
return await sqllink.execute_cmd(connects,
|
||||||
|
command=f"""
|
||||||
|
CREATE TABLE `user_{dbname.replace("_", "__")}_{name.replace("_", "__")}`(
|
||||||
|
`key` VARCHAR(40) PRIMARY KEY NOT NULL,
|
||||||
|
`value` MEDIUMBLOB
|
||||||
|
);""",
|
||||||
|
commit=True, callback=callback, err_callback=err_callback)
|
||||||
|
|
||||||
|
|
||||||
|
async def list_table(dbname: str) -> Tuple[str] | None:
|
||||||
|
"""
|
||||||
|
## List all tables from a jsondb.
|
||||||
|
@return: `tuple | None(error)`
|
||||||
|
"""
|
||||||
|
await log.info("List table in: "+str(dbname))
|
||||||
|
|
||||||
|
res = re.search(r'[0-9A-Za-z_]{2,20}', dbname)
|
||||||
|
if res is None or res.group() != dbname:
|
||||||
|
await log.warn("Database name error: "+str(dbname))
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def callback(result):
|
||||||
|
return (i[0][len(f"user_{dbname.replace("_", "__")}_"):] for i in result)
|
||||||
|
|
||||||
|
async def err_callback(err_info):
|
||||||
|
await log.warn("Other error on list database: "+str(err_info))
|
||||||
|
return None
|
||||||
|
|
||||||
|
return await sqllink.execute_cmd(connects,
|
||||||
|
command=f"SHOW TABLES LIKE 'user_{
|
||||||
|
dbname.replace("_", "__")}_%%';",
|
||||||
|
callback=callback, err_callback=err_callback)
|
||||||
|
|
||||||
|
|
||||||
|
async def rm_jsondb(dbname: str) -> tuple[int, str | None]:
|
||||||
|
"""
|
||||||
|
## Remove a jsondb and all tables in it.
|
||||||
|
@return: `tuple[errorlevel:int, errormsg:str|None]`
|
||||||
|
"""
|
||||||
|
await log.info("Remove database in: "+str(dbname))
|
||||||
|
|
||||||
|
res = re.search(r'[0-9A-Za-z_]{2,20}', dbname)
|
||||||
|
if res is None or res.group() != dbname:
|
||||||
|
await log.warn("Database name error: "+str(dbname))
|
||||||
|
return (1, "DB name error"+str(dbname))
|
||||||
|
|
||||||
|
checks = await check_jsondb(dbname)
|
||||||
|
if checks == None:
|
||||||
|
return (2, "Other check error")
|
||||||
|
elif checks == False:
|
||||||
|
await log.warn("Create table cannot find database: "+str(dbname))
|
||||||
|
return (2, "Cannot find database: "+str(dbname))
|
||||||
|
|
||||||
|
async def callback() -> tuple[int, str | None]:
|
||||||
|
return (0, None)
|
||||||
|
|
||||||
|
async def err_callback(err_info: str) -> tuple[int, str | None]:
|
||||||
|
await log.warn("Other error on list database: "+str(err_info))
|
||||||
|
return (3, str(err_info))
|
||||||
|
|
||||||
|
await log.debug("mysql remove db")
|
||||||
|
try:
|
||||||
|
async with connects.acquire() as conn:
|
||||||
|
async with conn.cursor() as cursor: # thank for chatglm
|
||||||
|
await cursor.execute(f"""
|
||||||
|
SELECT *
|
||||||
|
FROM information_schema.tables
|
||||||
|
WHERE table_schema = '{config.DB_NAME}' AND table_name LIKE 'user_{dbname.replace("_", "__")}_%%'
|
||||||
|
""")
|
||||||
|
tables_to_drop = await cursor.fetchall()
|
||||||
|
for table_info in tables_to_drop:
|
||||||
|
await cursor.execute(f"DROP TABLE {table_info[2]};")
|
||||||
|
await conn.commit()
|
||||||
|
except Exception as exp:
|
||||||
|
return await err_callback(repr(exp))
|
||||||
|
|
||||||
|
return await sqllink.execute_cmd(connects,
|
||||||
|
command=f"DELETE FROM sys_db_list WHERE `name`='{
|
||||||
|
dbname}';",
|
||||||
|
commit=True, callback=callback, err_callback=err_callback)
|
||||||
|
|
||||||
|
|
||||||
|
async def rm_table(dbname: str, name: str) -> tuple[int, str | None]:
|
||||||
|
"""
|
||||||
|
## Remove a table.
|
||||||
|
@return: `tuple[errorlevel:int, errormsg:str|None]`
|
||||||
|
"""
|
||||||
|
await log.info("Remove table '"+str(name)+"' in '"+str(dbname)+"'")
|
||||||
|
|
||||||
|
res = re.search(r'[0-9A-Za-z_]{2,20}', dbname)
|
||||||
|
if res is None or res.group() != dbname:
|
||||||
|
await log.warn("Database name error: "+str(dbname))
|
||||||
|
return (1, "DB name error"+str(dbname))
|
||||||
|
|
||||||
|
res = re.search(r'[0-9A-Za-z_]{2,20}', name)
|
||||||
|
if res is None or res.group() != name:
|
||||||
|
await log.warn("Create table database name error: "+str(name))
|
||||||
|
return (2, "Name error: "+str(name))
|
||||||
|
|
||||||
|
checks = await check_jsondb(dbname)
|
||||||
|
if checks == None:
|
||||||
|
return (3, "Other check error")
|
||||||
|
elif checks == False:
|
||||||
|
await log.warn("Create table cannot find database: "+str(dbname))
|
||||||
|
return (3, "Cannot find database: "+str(dbname))
|
||||||
|
|
||||||
|
async def callback():
|
||||||
|
return (0, None)
|
||||||
|
|
||||||
|
async def err_callback(err_info):
|
||||||
|
if ("Unknown table" in err_info):
|
||||||
|
await log.warn("Unknown table name: "+str(name))
|
||||||
|
return (4, "Unknown table name: "+str(name))
|
||||||
|
else:
|
||||||
|
await log.warn("Other error on create table: "+str(err_info))
|
||||||
|
return (4, "Other error: "+str(err_info))
|
||||||
|
|
||||||
|
return await sqllink.execute_cmd(connects,
|
||||||
|
command=f"DROP TABLE `user_" +
|
||||||
|
f"{dbname.replace("_", "__")}_" +
|
||||||
|
f"{name.replace("_", "__")}`;",
|
||||||
|
commit=True,
|
||||||
|
callback=callback,
|
||||||
|
err_callback=err_callback)
|
||||||
|
|
||||||
|
|
||||||
|
async def check_table(dbname: str, name: str) -> bool | None:
|
||||||
|
"""
|
||||||
|
## Check a table if it has been created.
|
||||||
|
@return: True(created) | False(not create) | None(error)
|
||||||
|
"""
|
||||||
|
await log.info("Check table '"+str(name)+"' in '"+str(dbname)+"'")
|
||||||
|
|
||||||
|
res = re.search(r'[0-9A-Za-z_]{2,20}', dbname)
|
||||||
|
if res is None or res.group() != dbname:
|
||||||
|
await log.warn("Database name error: "+str(dbname))
|
||||||
|
return None
|
||||||
|
|
||||||
|
res = re.search(r'[0-9A-Za-z_]{2,20}', name)
|
||||||
|
if res is None or res.group() != name:
|
||||||
|
await log.warn("Create table database name error: "+str(name))
|
||||||
|
return None
|
||||||
|
|
||||||
|
checks = await check_jsondb(dbname)
|
||||||
|
if checks == None:
|
||||||
|
return None
|
||||||
|
elif checks == False:
|
||||||
|
await log.warn("Create table cannot find database: "+str(dbname))
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def callback(result):
|
||||||
|
if (len(result) > 0):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def err_callback(err_info):
|
||||||
|
await log.warn("Other error on check table: "+str(err_info))
|
||||||
|
return None
|
||||||
|
return await sqllink.execute_cmd(connects,
|
||||||
|
command=f"SHOW TABLES LIKE 'user_" +
|
||||||
|
f"{dbname.replace("_", "__")}_" +
|
||||||
|
f"{name.replace("_", "__")}';",
|
||||||
|
callback=callback,
|
||||||
|
err_callback=err_callback)
|
||||||
|
|
||||||
|
|
||||||
|
async def create_key(dbname: str, name: str, key: str | int | tuple, vl: Any) -> tuple[int, str | None]:
|
||||||
|
"""
|
||||||
|
## Create a key.
|
||||||
|
@return: `tuple[errorlevel:int, errormsg:str|None]`
|
||||||
|
"""
|
||||||
|
await log.info("Create key in '"+str(name)+"."+str(dbname)+"'")
|
||||||
|
|
||||||
|
if (vl is None):
|
||||||
|
await log.warn("Value is None")
|
||||||
|
return (6, "Value is None")
|
||||||
|
|
||||||
|
res = re.search(r'[0-9A-Za-z_]{2,20}', dbname)
|
||||||
|
if res is None or res.group() != dbname:
|
||||||
|
await log.warn("Database name error: "+str(dbname))
|
||||||
|
return (1, "Database name error: "+str(dbname))
|
||||||
|
|
||||||
|
res = re.search(r'[0-9A-Za-z_]{2,20}', name)
|
||||||
|
if res is None or res.group() != name:
|
||||||
|
await log.warn("Create key table name error: "+str(name))
|
||||||
|
return (2, "Create key table name error: "+str(name))
|
||||||
|
|
||||||
|
checks = await check_table(dbname, name)
|
||||||
|
if checks == None:
|
||||||
|
return (3, "Other error")
|
||||||
|
elif checks == False:
|
||||||
|
await log.warn("Unknown table: "+str(name))
|
||||||
|
return (4, "Unknown table: "+str(name))
|
||||||
|
|
||||||
|
async def callback():
|
||||||
|
return (0, None)
|
||||||
|
|
||||||
|
async def err_callback(err_info):
|
||||||
|
await log.warn("Other error on create key: "+str(err_info))
|
||||||
|
return (5, "Other error on create key: "+str(err_info))
|
||||||
|
return await sqllink.execute_cmd(connects,
|
||||||
|
command=f"""
|
||||||
|
INSERT INTO `user_{dbname.replace("_", "__")}_{name.replace("_", "__")}`(
|
||||||
|
`key`,
|
||||||
|
`value`
|
||||||
|
) VALUE (
|
||||||
|
%s,
|
||||||
|
%s
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
args=(str(key), pickle.dumps(vl)),
|
||||||
|
commit=True,
|
||||||
|
callback=callback,
|
||||||
|
err_callback=err_callback)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_key(dbname: str, name: str, key: str | int | tuple) -> Any | None:
|
||||||
|
"""
|
||||||
|
## Get a key.
|
||||||
|
@return: `Any | None(error)`
|
||||||
|
"""
|
||||||
|
await log.info("Get key in '"+str(name)+"."+str(dbname)+"'")
|
||||||
|
|
||||||
|
res = re.search(r'[0-9A-Za-z_]{2,20}', dbname)
|
||||||
|
if res is None or res.group() != dbname:
|
||||||
|
await log.warn("Database name error: "+str(dbname))
|
||||||
|
return None
|
||||||
|
|
||||||
|
res = re.search(r'[0-9A-Za-z_]{2,20}', name)
|
||||||
|
if res is None or res.group() != name:
|
||||||
|
await log.warn("Get key table name error: "+str(name))
|
||||||
|
return None
|
||||||
|
|
||||||
|
checks = await check_table(dbname, name)
|
||||||
|
if checks == None:
|
||||||
|
await log.warn("Other error")
|
||||||
|
return None
|
||||||
|
elif checks == False:
|
||||||
|
await log.warn("Unknown table: "+str(name))
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def callback(result):
|
||||||
|
if (len(result) > 0):
|
||||||
|
return pickle.loads(result[0][1])
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def err_callback(err_info):
|
||||||
|
await log.warn("Other error on get key: "+str(err_info))
|
||||||
|
return None
|
||||||
|
return await sqllink.execute_cmd(connects,
|
||||||
|
command=f"""
|
||||||
|
SELECT * FROM `user_{dbname.replace("_", "__")}_{name.replace("_", "__")}` WHERE `key`=%s;""",
|
||||||
|
args=(str(key),),
|
||||||
|
callback=callback,
|
||||||
|
err_callback=err_callback)
|
||||||
|
|
||||||
|
|
||||||
|
async def check_key(dbname: str, name: str, key: str | int | tuple) -> Any | None:
|
||||||
|
"""
|
||||||
|
## Check a key.
|
||||||
|
@return: True(created) | False(not create) | None(error)
|
||||||
|
"""
|
||||||
|
await log.info("Check key in '"+str(name)+"."+str(dbname)+"'")
|
||||||
|
|
||||||
|
res = re.search(r'[0-9A-Za-z_]{2,20}', dbname)
|
||||||
|
if res is None or res.group() != dbname:
|
||||||
|
await log.warn("Database name error: "+str(dbname))
|
||||||
|
return None
|
||||||
|
|
||||||
|
res = re.search(r'[0-9A-Za-z_]{2,20}', name)
|
||||||
|
if res is None or res.group() != name:
|
||||||
|
await log.warn("Check key table name error: "+str(name))
|
||||||
|
return None
|
||||||
|
|
||||||
|
checks = await check_table(dbname, name)
|
||||||
|
if checks == None:
|
||||||
|
await log.warn("Other error")
|
||||||
|
return None
|
||||||
|
elif checks == False:
|
||||||
|
await log.warn("Unknown table: "+str(name))
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def callback(result):
|
||||||
|
if (len(result) > 0):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def err_callback(err_info):
|
||||||
|
await log.warn("Other error on check key: "+str(err_info))
|
||||||
|
return None
|
||||||
|
return await sqllink.execute_cmd(connects,
|
||||||
|
command=f"""
|
||||||
|
SELECT * FROM `user_{dbname.replace("_", "__")}_{name.replace("_", "__")}` WHERE `key`=%s;""",
|
||||||
|
args=(str(key),),
|
||||||
|
callback=callback,
|
||||||
|
err_callback=err_callback)
|
||||||
|
|
||||||
|
|
||||||
|
async def remove_key(dbname: str, name: str, key: str | int | tuple) -> Any | None:
|
||||||
|
"""
|
||||||
|
## Remove a key.
|
||||||
|
@return: True(created) | False(not create) | None(error)
|
||||||
|
"""
|
||||||
|
await log.info("Remove key in '"+str(name)+"."+str(dbname)+"'")
|
||||||
|
|
||||||
|
res = re.search(r'[0-9A-Za-z_]{2,20}', dbname)
|
||||||
|
if res is None or res.group() != dbname:
|
||||||
|
await log.warn("Database name error: "+str(dbname))
|
||||||
|
return (1, "Database name error: "+str(dbname))
|
||||||
|
|
||||||
|
res = re.search(r'[0-9A-Za-z_]{2,20}', name)
|
||||||
|
if res is None or res.group() != name:
|
||||||
|
await log.warn("Remove key table name error: "+str(name))
|
||||||
|
return (2, "Remove key table name error: "+str(name))
|
||||||
|
|
||||||
|
checks = await check_key(dbname, name, key)
|
||||||
|
if checks == None:
|
||||||
|
await log.warn("Other error")
|
||||||
|
return (3, "Other error")
|
||||||
|
elif checks == False:
|
||||||
|
await log.warn("Unknown key: "+str(key))
|
||||||
|
return (4, "Unknown key: "+str(key))
|
||||||
|
|
||||||
|
async def callback():
|
||||||
|
return (0, None)
|
||||||
|
|
||||||
|
async def err_callback(err_info):
|
||||||
|
await log.warn("Other error on remove key: "+str(err_info))
|
||||||
|
return (5, "Other error on remove key: "+str(err_info))
|
||||||
|
return await sqllink.execute_cmd(connects,
|
||||||
|
command=f"""
|
||||||
|
DELETE FROM `user_{dbname.replace("_", "__")}_{name.replace("_", "__")}` WHERE `key`=%s;""",
|
||||||
|
args=(str(key),),
|
||||||
|
commit=True,
|
||||||
|
callback=callback,
|
||||||
|
err_callback=err_callback)
|
||||||
|
|
||||||
|
|
||||||
|
async def change_key(dbname: str, name: str, key: str | int | tuple, vl: Any) -> Any | None:
|
||||||
|
"""
|
||||||
|
## Change a key.
|
||||||
|
@return: True(created) | False(not create) | None(error)
|
||||||
|
"""
|
||||||
|
await log.info("Change key in '"+str(name)+"."+str(dbname)+"'")
|
||||||
|
|
||||||
|
res = re.search(r'[0-9A-Za-z_]{2,20}', dbname)
|
||||||
|
if res is None or res.group() != dbname:
|
||||||
|
await log.warn("Database name error: "+str(dbname))
|
||||||
|
return (1, "Database name error: "+str(dbname))
|
||||||
|
|
||||||
|
res = re.search(r'[0-9A-Za-z_]{2,20}', name)
|
||||||
|
if res is None or res.group() != name:
|
||||||
|
await log.warn("Change key table name error: "+str(name))
|
||||||
|
return (2, "Change key table name error: "+str(name))
|
||||||
|
|
||||||
|
checks = await check_key(dbname, name, key)
|
||||||
|
if checks == None:
|
||||||
|
await log.warn("Other error")
|
||||||
|
return (3, "Other error")
|
||||||
|
elif checks == False:
|
||||||
|
await log.warn("Unknown key: "+str(name))
|
||||||
|
return (4, "Unknown key: "+str(name))
|
||||||
|
|
||||||
|
async def callback():
|
||||||
|
return (0, None)
|
||||||
|
|
||||||
|
async def err_callback(err_info):
|
||||||
|
await log.warn("Other error on change key: "+str(err_info))
|
||||||
|
return (5, "Other error on change key: "+str(err_info))
|
||||||
|
return await sqllink.execute_cmd(connects,
|
||||||
|
command=f"""
|
||||||
|
UPDATE `user_{dbname.replace("_", "__")}_{name.replace("_", "__")}` SET `value`=%s WHERE `key`=%s;""",
|
||||||
|
args=(pickle.dumps(vl), str(key)),
|
||||||
|
commit=True,
|
||||||
|
callback=callback,
|
||||||
|
err_callback=err_callback)
|
||||||
|
|
||||||
|
|
||||||
|
async def list_key(dbname: str, name: str) -> Tuple[tuple[str, Any]] | None:
|
||||||
|
"""
|
||||||
|
## List keys.
|
||||||
|
@return: `tuple | None(error)`
|
||||||
|
"""
|
||||||
|
await log.info("List keys in '"+str(name)+"."+str(dbname)+"'")
|
||||||
|
|
||||||
|
res = re.search(r'[0-9A-Za-z_]{2,20}', dbname)
|
||||||
|
if res is None or res.group() != dbname:
|
||||||
|
await log.warn("Database name error: "+str(dbname))
|
||||||
|
return None
|
||||||
|
|
||||||
|
res = re.search(r'[0-9A-Za-z_]{2,20}', name)
|
||||||
|
if res is None or res.group() != name:
|
||||||
|
await log.warn("List key table name error: "+str(name))
|
||||||
|
return None
|
||||||
|
|
||||||
|
checks = await check_table(dbname, name)
|
||||||
|
if checks == None:
|
||||||
|
await log.warn("Other error")
|
||||||
|
return None
|
||||||
|
elif checks == False:
|
||||||
|
await log.warn("Unknown table: "+str(name))
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def callback(result):
|
||||||
|
return ((i[0], pickle.loads(i[1])) for i in result)
|
||||||
|
|
||||||
|
async def err_callback(err_info):
|
||||||
|
await log.warn("Other error on list key: "+str(err_info))
|
||||||
|
return None
|
||||||
|
return await sqllink.execute_cmd(connects,
|
||||||
|
command=f"""
|
||||||
|
SELECT * FROM `user_{dbname.replace("_", "__")}_{name.replace("_", "__")}`""",
|
||||||
|
callback=callback,
|
||||||
|
err_callback=err_callback)
|
|
@ -0,0 +1,71 @@
|
||||||
|
# ------ cxykevin log moudle ------
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
from src import config
|
||||||
|
levels = {
|
||||||
|
"debug": logging.DEBUG,
|
||||||
|
'info': logging.INFO,
|
||||||
|
'warning': logging.WARN,
|
||||||
|
'err': logging.ERROR
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def log_init() -> None:
|
||||||
|
logging.basicConfig(filename=config.LOGPATH,
|
||||||
|
format='[%(asctime)s][%(levelname)s] %(message)s',
|
||||||
|
level=levels[config.LOGLEVEL], filemode='w')
|
||||||
|
|
||||||
|
|
||||||
|
async def info(msg: str) -> None:
|
||||||
|
if (20 >= levels[config.LOGLEVEL]):
|
||||||
|
sys.stdout.write(
|
||||||
|
("[\033[34mINFO\033[0m]" if config.OUTPUT_COLORFUL else "[INFO]") + msg + "\n")
|
||||||
|
logging.info(msg)
|
||||||
|
|
||||||
|
|
||||||
|
async def info(msg: str) -> None:
|
||||||
|
if (20 >= levels[config.LOGLEVEL]):
|
||||||
|
sys.stdout.write(
|
||||||
|
("[\033[34mINFO\033[0m]" if config.OUTPUT_COLORFUL else "[INFO]") + msg + "\n")
|
||||||
|
logging.info(msg)
|
||||||
|
|
||||||
|
|
||||||
|
async def warn(msg: str) -> None:
|
||||||
|
if (30 >= levels[config.LOGLEVEL]):
|
||||||
|
sys.stdout.write(
|
||||||
|
("[\033[33mWARNING\033[0m]" if config.OUTPUT_COLORFUL else "[WARNING]") + msg + "\n")
|
||||||
|
logging.warn(msg)
|
||||||
|
|
||||||
|
|
||||||
|
async def err(msg: str) -> None:
|
||||||
|
if (40 >= levels[config.LOGLEVEL]):
|
||||||
|
sys.stdout.write(
|
||||||
|
("[\033[31mERROR\033[0m]" if config.OUTPUT_COLORFUL else "[ERROR]") + msg + "\n")
|
||||||
|
logging.error(msg)
|
||||||
|
|
||||||
|
|
||||||
|
async def break_err(msg: str) -> None:
|
||||||
|
if (40 >= levels[config.LOGLEVEL]):
|
||||||
|
sys.stdout.write(
|
||||||
|
("[\033[31mERROR\033[0m]" if config.OUTPUT_COLORFUL else "[ERROR]") + msg + "\n")
|
||||||
|
logging.error(msg)
|
||||||
|
|
||||||
|
|
||||||
|
async def debug(*args, end="\n") -> None:
|
||||||
|
msg = ' '.join(map(str, args))
|
||||||
|
if (10 >= levels[config.LOGLEVEL]):
|
||||||
|
sys.stdout.write(
|
||||||
|
("[\033[32mDEBUG\033[0m]" if config.OUTPUT_COLORFUL else "[ERROR]") + msg + end)
|
||||||
|
logging.debug(msg)
|
||||||
|
|
||||||
|
|
||||||
|
async def print(*args, end="\n") -> None:
|
||||||
|
msg = ' '.join(map(str, args))
|
||||||
|
if (10 >= levels[config.LOGLEVEL]):
|
||||||
|
sys.stdout.write(msg + end)
|
||||||
|
logging.debug(msg)
|
||||||
|
|
||||||
|
|
||||||
|
async def trace_sys(msg: str) -> None:
|
||||||
|
logging.info(msg)
|
|
@ -0,0 +1,54 @@
|
||||||
|
from src import log
|
||||||
|
from src import config
|
||||||
|
import aiomysql
|
||||||
|
import traceback
|
||||||
|
import textwrap
|
||||||
|
from typing import Any, Callable
|
||||||
|
|
||||||
|
|
||||||
|
async def connect_db() -> aiomysql.pool.Pool:
|
||||||
|
"""
|
||||||
|
Connect to a mysql database.
|
||||||
|
@return: Pool(aiomysql.pool.Pool)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
connects = await aiomysql.create_pool(
|
||||||
|
host=config.DB_SERVER,
|
||||||
|
port=config.DB_PORT,
|
||||||
|
user=config.DB_USER,
|
||||||
|
password=config.DB_PASSWD,
|
||||||
|
db=config.DB_NAME
|
||||||
|
)
|
||||||
|
except Exception as exp:
|
||||||
|
await log.err("Connect MYSQL server error")
|
||||||
|
await log.err(repr(exp))
|
||||||
|
for i in traceback.format_exc().split("\n"):
|
||||||
|
firstlen_flag = "* "
|
||||||
|
lastlen = ""
|
||||||
|
for j in textwrap.wrap(i, width=60):
|
||||||
|
await log.err(" "+firstlen_flag+" " *
|
||||||
|
(len(lastlen)-len(lastlen.lstrip()))+j)
|
||||||
|
lastlen = " " * (len(lastlen)-len(lastlen.lstrip()))+j
|
||||||
|
firstlen_flag = " "
|
||||||
|
raise
|
||||||
|
return connects
|
||||||
|
|
||||||
|
|
||||||
|
async def execute_cmd(connects: aiomysql.pool.Pool, command: str, callback: Callable[..., Any] = lambda: None, commit=False, args: list | tuple = [], err_callback: Callable[[str], Any] = lambda err: log.warn("Default execute error: "+str(err))) -> Any:
|
||||||
|
"""
|
||||||
|
Execute a sql command on mysql database.
|
||||||
|
Return from callback function.
|
||||||
|
"""
|
||||||
|
await log.debug("mysql> "+command+" "+str(args))
|
||||||
|
try:
|
||||||
|
async with connects.acquire() as conn:
|
||||||
|
async with conn.cursor() as cursor:
|
||||||
|
await cursor.execute(command, args)
|
||||||
|
if (commit):
|
||||||
|
await conn.commit()
|
||||||
|
return await callback()
|
||||||
|
else:
|
||||||
|
result = await cursor.fetchall()
|
||||||
|
return await callback(result)
|
||||||
|
except Exception as exp:
|
||||||
|
return await err_callback(repr(exp))
|
|
@ -0,0 +1,147 @@
|
||||||
|
from src import core
|
||||||
|
import sys
|
||||||
|
import readline
|
||||||
|
import asyncio
|
||||||
|
from src import config
|
||||||
|
import signal
|
||||||
|
|
||||||
|
|
||||||
|
async def do_cmd(cmd: str) -> None:
|
||||||
|
"""
|
||||||
|
Do a command.
|
||||||
|
Only for debug.
|
||||||
|
"""
|
||||||
|
args = cmd.split(" ")
|
||||||
|
match args[0]:
|
||||||
|
case "mkdb":
|
||||||
|
if (len(args) != 2):
|
||||||
|
sys.stderr.write("* dbname\n")
|
||||||
|
else:
|
||||||
|
ret = await core.create_jsondb(args[1])
|
||||||
|
if (ret[0] != 0):
|
||||||
|
sys.stderr.write("err: "+str(ret[1])+'\n')
|
||||||
|
case "checkdb":
|
||||||
|
if (len(args) != 2):
|
||||||
|
sys.stderr.write("* dbname\n")
|
||||||
|
else:
|
||||||
|
ret = await core.check_jsondb(args[1])
|
||||||
|
sys.stdout.write(str(ret)+"\n")
|
||||||
|
case "lsdb":
|
||||||
|
if (len(args) != 1):
|
||||||
|
sys.stderr.write("no args\n")
|
||||||
|
else:
|
||||||
|
ret = await core.list_jsondb()
|
||||||
|
if (ret is not None):
|
||||||
|
for i in ret:
|
||||||
|
sys.stdout.write("+ "+str(i)+"\n")
|
||||||
|
case "rmdb":
|
||||||
|
if (len(args) != 2):
|
||||||
|
sys.stderr.write("* dbname\n")
|
||||||
|
else:
|
||||||
|
ret = await core.rm_jsondb(args[1])
|
||||||
|
if (ret[0] != 0):
|
||||||
|
sys.stderr.write("err: "+str(ret[1])+'\n')
|
||||||
|
case "mktable":
|
||||||
|
if (len(args) != 3):
|
||||||
|
sys.stderr.write("* dbname\n* tablename\n")
|
||||||
|
else:
|
||||||
|
ret = await core.create_table(args[1], args[2])
|
||||||
|
if (ret[0] != 0):
|
||||||
|
sys.stderr.write("err: "+str(ret[1])+'\n')
|
||||||
|
case "lstable":
|
||||||
|
if (len(args) != 2):
|
||||||
|
sys.stderr.write("* dbname\n")
|
||||||
|
else:
|
||||||
|
ret = await core.list_table(args[1])
|
||||||
|
if (ret is not None):
|
||||||
|
for i in ret:
|
||||||
|
sys.stdout.write("+ "+str(i)+"\n")
|
||||||
|
case "rmtable":
|
||||||
|
if (len(args) != 3):
|
||||||
|
sys.stderr.write("* dbname\n* tablename\n")
|
||||||
|
else:
|
||||||
|
ret = await core.rm_table(args[1], args[2])
|
||||||
|
if (ret[0] != 0):
|
||||||
|
sys.stderr.write("err: "+str(ret[1])+'\n')
|
||||||
|
case "checktable":
|
||||||
|
if (len(args) != 3):
|
||||||
|
sys.stderr.write("* dbname\n* tablename\n")
|
||||||
|
else:
|
||||||
|
ret = await core.check_table(args[1], args[2])
|
||||||
|
sys.stdout.write(str(ret)+"\n")
|
||||||
|
case "ckey":
|
||||||
|
if (len(args) != 5):
|
||||||
|
sys.stderr.write(
|
||||||
|
"* dbname\n* tablename\n* key\n* value(eval)\n")
|
||||||
|
else:
|
||||||
|
ret = await core.create_key(args[1], args[2], args[3], eval(args[4]))
|
||||||
|
if (ret[0] != 0):
|
||||||
|
sys.stderr.write("err: "+str(ret[1])+'\n')
|
||||||
|
case "gkey":
|
||||||
|
if (len(args) != 4):
|
||||||
|
sys.stderr.write("* dbname\n* tablename\n* key\n")
|
||||||
|
else:
|
||||||
|
ret = await core.get_key(args[1], args[2], args[3])
|
||||||
|
sys.stdout.write(repr(ret)+"\n")
|
||||||
|
case "ikey":
|
||||||
|
if (len(args) != 4):
|
||||||
|
sys.stderr.write("* dbname\n* tablename\n* key\n")
|
||||||
|
else:
|
||||||
|
ret = await core.check_key(args[1], args[2], args[3])
|
||||||
|
sys.stdout.write(repr(ret)+"\n")
|
||||||
|
case "rkey":
|
||||||
|
if (len(args) != 4):
|
||||||
|
sys.stderr.write("* dbname\n* tablename\n* key\n")
|
||||||
|
else:
|
||||||
|
ret = await core.remove_key(args[1], args[2], args[3])
|
||||||
|
if (ret[0] != 0):
|
||||||
|
sys.stderr.write("err: "+str(ret[1])+'\n')
|
||||||
|
case "ekey":
|
||||||
|
if (len(args) != 5):
|
||||||
|
sys.stderr.write(
|
||||||
|
"* dbname\n* tablename\n* key\n* value(eval)\n")
|
||||||
|
else:
|
||||||
|
ret = await core.change_key(args[1], args[2], args[3], eval(args[4]))
|
||||||
|
if (ret[0] != 0):
|
||||||
|
sys.stderr.write("err: "+str(ret[1])+'\n')
|
||||||
|
case "lskey":
|
||||||
|
if (len(args) != 3):
|
||||||
|
sys.stderr.write("* dbname\n* tablename\n")
|
||||||
|
else:
|
||||||
|
ret = await core.list_key(args[1], args[2])
|
||||||
|
if (ret is not None):
|
||||||
|
for i in ret:
|
||||||
|
sys.stdout.write("+ "+str(i[0])+"="+str(i[1])+"\n")
|
||||||
|
case "help":
|
||||||
|
if (len(args) != 1):
|
||||||
|
sys.stderr.write("no args\n")
|
||||||
|
else:
|
||||||
|
sys.stderr.write(
|
||||||
|
"mkdb lsdb checkdb rmdb mktable lstable rmtable checktable ckey gkey rkey ekey lskey\n")
|
||||||
|
case "exit":
|
||||||
|
sys.stderr.write("Bye\n")
|
||||||
|
sys.exit(0)
|
||||||
|
case _:
|
||||||
|
sys.stderr.write("Unknown command\n")
|
||||||
|
|
||||||
|
|
||||||
|
async def debug_term() -> None:
|
||||||
|
"""
|
||||||
|
Start a command line.
|
||||||
|
Only for debug.
|
||||||
|
"""
|
||||||
|
if (config.USE_EMACE_MOD):
|
||||||
|
readline.parse_and_bind('tab: complete')
|
||||||
|
readline.parse_and_bind('set editing-mode emacs')
|
||||||
|
|
||||||
|
if (config.SIGTERM_CMD):
|
||||||
|
def exit(signum, frame):
|
||||||
|
sys.stderr.write("\nBye\n")
|
||||||
|
sys.exit()
|
||||||
|
signal.signal(signal.SIGINT, exit)
|
||||||
|
signal.signal(signal.SIGTERM, exit)
|
||||||
|
sys.stderr.write(
|
||||||
|
" -- Welcome to the cli tools. -- \nType 'exit' to quit.\n")
|
||||||
|
while (1):
|
||||||
|
inp = await asyncio.get_event_loop().run_in_executor(None, input, "db> ")
|
||||||
|
await do_cmd(inp)
|
|
@ -0,0 +1,21 @@
|
||||||
|
from src.webcore import app
|
||||||
|
from src import core
|
||||||
|
from fastapi import Response
|
||||||
|
|
||||||
|
version = "v1"
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/api/"+version+"/database/{database_name}", status_code=201)
|
||||||
|
async def create_database(database_name: str):
|
||||||
|
ret = await core.create_jsondb(database_name)
|
||||||
|
if (ret[0] == 0):
|
||||||
|
return {"status": 200}
|
||||||
|
return {"status": 400+ret[0], "errormsg": ret[1]}
|
||||||
|
|
||||||
|
|
||||||
|
@app.delete("/api/"+version+"/database/{database_name}", status_code=200)
|
||||||
|
async def create_database(database_name: str):
|
||||||
|
ret = await core.rm_jsondb(database_name)
|
||||||
|
if (ret[0] == 0):
|
||||||
|
return {"status": 200}
|
||||||
|
return {"status": 400+ret[0], "errormsg": ret[1]}
|
|
@ -0,0 +1,29 @@
|
||||||
|
from fastapi import FastAPI
|
||||||
|
import uvicorn
|
||||||
|
from src import config
|
||||||
|
from src import log
|
||||||
|
import os
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
async def root_hello():
|
||||||
|
return "Welcome to myjsonDB!"
|
||||||
|
|
||||||
|
|
||||||
|
async def start():
|
||||||
|
await log.info("Search web plugins")
|
||||||
|
|
||||||
|
plugs = []
|
||||||
|
for i in os.listdir("src"+os.sep+"web"):
|
||||||
|
if (i.split(".")[-1] != "py"):
|
||||||
|
continue
|
||||||
|
await log.info(" + Load "+i)
|
||||||
|
plugs.append(__import__("src.web."+i.split(".")[0]))
|
||||||
|
|
||||||
|
sconfig = uvicorn.Config(
|
||||||
|
app, loop="none", host=config.API_HOST, port=config.API_PORT)
|
||||||
|
server = uvicorn.Server(sconfig)
|
||||||
|
|
||||||
|
await server.serve()
|
Loading…
Reference in New Issue