feat: add settings module

This commit is contained in:
fatelei 2023-08-23 17:57:23 +08:00
parent 709d8a5615
commit 79392a2f70
21 changed files with 187 additions and 29 deletions

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
__pycache__
.idea
logs
.venv
.vscode

2
Makefile Normal file
View file

@ -0,0 +1,2 @@
test:
python -m unitest test/settings/test_settings.py

0
appmanage/__init__.py Normal file
View file

View file

View file

@ -2,4 +2,9 @@ class CommandException(Exception):
def __init__(self, code, message, detail): def __init__(self, code, message, detail):
self.code = code self.code = code
self.message = message self.message = message
self.detail = detail self.detail = detail
class MissingConfigException(CommandException):
pass

View file

View file

View file

View file

@ -0,0 +1,50 @@
from api.utils.helper import Singleton
__all__ = ['settings']
class Settings(object):
__metaclass__ = Singleton
def __init__(self):
self._config = {}
self.config_file = '/usr/src/app/config/settings.conf'
def init_config_from_file(self, config_file: str=None):
if config_file:
self.config_file = config_file
try:
with open(config_file, 'r') as f:
data = f.readlines()
except Exception:
data = []
for i in data:
i = i.replace('\n', '').replace('\r\n', '')
key, value = i.split('=')
if self._config.get(key) != value:
self._config[key] = value
def update_setting(self, key: str, value: str):
self._config[key] = value
self.flush_config()
def get_setting(self, key: str, default=None):
return self._config.get(key, default)
def list_all_settings(self) -> list:
return self._config
def delete_setting(self, key: str, value: str):
if key in self._config:
del self._config[key]
def flush_config(self):
with open(self.config_file, 'w') as f:
for key, value in self._config.items():
f.write(f'{key}={value}\n')
settings = Settings()

View file

View file

@ -1,25 +1,26 @@
# 所有常量统一定义区 # 所有常量统一定义区
# 错误代码定义 # 错误代码定义
ERROR_CLIENT_PARAM_BLANK="Client.Parameter.Blank.Error" ERROR_CLIENT_PARAM_BLANK = "Client.Parameter.Blank.Error"
ERROR_CLIENT_PARAM_Format="Client.Parameter.Format.Error" ERROR_CLIENT_PARAM_Format = "Client.Parameter.Format.Error"
ERROR_CLIENT_PARAM_NOTEXIST="Client.Parameter.Value.NotExist.Error" ERROR_CLIENT_PARAM_NOTEXIST = "Client.Parameter.Value.NotExist.Error"
ERROR_CLIENT_PARAM_REPEAT="Client.Parameter.Value.Repeat.Error" ERROR_CLIENT_PARAM_REPEAT = "Client.Parameter.Value.Repeat.Error"
ERROR_CONFIG_NGINX="Nginx.Configure.Error" ERROR_CONFIG_NGINX = "Nginx.Configure.Error"
ERROR_SERVER_COMMAND="Server.Container.Error" ERROR_SERVER_COMMAND = "Server.Container.Error"
ERROR_SERVER_SYSTEM="Server.SystemError" ERROR_SERVER_SYSTEM = "Server.SystemError"
ERROR_SERVER_RESOURCE="Server.ResourceError" ERROR_SERVER_RESOURCE = "Server.ResourceError"
ERROR_SERVER_CONFIG_MISSING = "Server.Config.NotFound"
# 错误信息定义 # 错误信息定义
ERRORMESSAGE_CLIENT_PARAM_BLANK="Client.Parameter.Blank.Error" ERRORMESSAGE_CLIENT_PARAM_BLANK = "Client.Parameter.Blank.Error"
ERRORMESSAGE_CLIENT_PARAM_Format="Client.Parameter.Format.Error" ERRORMESSAGE_CLIENT_PARAM_Format = "Client.Parameter.Format.Error"
ERRORMESSAGE_CLIENT_PARAM_NOTEXIST="Client.Parameter.Value.NotExist.Error" ERRORMESSAGE_CLIENT_PARAM_NOTEXIST = "Client.Parameter.Value.NotExist.Error"
ERRORMESSAGE_CLIENT_PARAM_REPEAT="Client.Parameter.Value.Repeat.Error" ERRORMESSAGE_CLIENT_PARAM_REPEAT = "Client.Parameter.Value.Repeat.Error"
ERRORMESSAGE_SERVER_COMMAND="Server.Container.Error" ERRORMESSAGE_SERVER_COMMAND = "Server.Container.Error"
ERRORMESSAGE_SERVER_SYSTEM="Server.SystemError" ERRORMESSAGE_SERVER_SYSTEM = "Server.SystemError"
ERRORMESSAGE_SERVER_RESOURCE="Server.ResourceError" ERRORMESSAGE_SERVER_RESOURCE = "Server.ResourceError"
ERRORMESSAGE_SERVER_VERSION_NOTSUPPORT="Server.Version.NotSupport" ERRORMESSAGE_SERVER_VERSION_NOTSUPPORT = "Server.Version.NotSupport"
ERRORMESSAGE_SERVER_VERSION_NEEDUPGRADE="Server.Version.NeedUpgradeCore" ERRORMESSAGE_SERVER_VERSION_NEEDUPGRADE = "Server.Version.NeedUpgradeCore"
# 应用状态定义 # 应用状态定义
# 应用启动中 installing # 应用启动中 installing
@ -33,7 +34,7 @@ APP_STATUS_RESTARTING = "restarting"
# 应用错误 failed # 应用错误 failed
APP_STATUS_FAILED = "failed" APP_STATUS_FAILED = "failed"
NGINX_URL="http://websoft9-nginxproxymanager:81" NGINX_URL = "http://websoft9-nginxproxymanager:81"
#ARTIFACT_URL="https://artifact.azureedge.net/release/websoft9" # ARTIFACT_URL="https://artifact.azureedge.net/release/websoft9"
ARTIFACT_URL="https://w9artifact.blob.core.windows.net/release/websoft9" ARTIFACT_URL = "https://w9artifact.blob.core.windows.net/release/websoft9"
ARTIFACT_URL_DEV="https://w9artifact.blob.core.windows.net/dev/websoft9" ARTIFACT_URL_DEV = "https://w9artifact.blob.core.windows.net/dev/websoft9"

View file

@ -0,0 +1,6 @@
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]

View file

View file

@ -1,6 +1,6 @@
from fastapi import APIRouter from fastapi import APIRouter
from api.v1.routers import health,apps from api.v1.routers import health, apps
def get_api(): def get_api():

View file

View file

@ -11,6 +11,7 @@ from api.service import manage, db
from api.utils import shell_execute, const from api.utils import shell_execute, const
from api.utils.common_log import myLogger from api.utils.common_log import myLogger
from api.exception.command_exception import CommandException from api.exception.command_exception import CommandException
from api.settings.settings import settings
router = APIRouter() router = APIRouter()
@ -76,10 +77,17 @@ rd_appstore = rd_s + appstore_update + rd_e
rd_auto_list = rd_s + auto + rd_e rd_auto_list = rd_s + auto + rd_e
rd_user_list = rd_s + user + rd_e rd_user_list = rd_s + user + rd_e
rd_updateuser_list=rd_s + updateuser + rd_e rd_updateuser_list=rd_s + updateuser + rd_e
class SettingItem(BaseModel):
key: str = Field(description="配置项")
value: str = Field(description="配置项的取值")
@router.api_route("/AppStatus", methods=["GET", "POST"], summary="获取指定APP的信息", @router.api_route("/AppStatus", methods=["GET", "POST"], summary="获取指定APP的信息",
response_description=rd_status, response_description=rd_status,
response_model=Response) response_model=Response)
def AppStatus(request: Request,app_id: Optional[str] = Query(default=None, description="应用ID")): def AppStatus(request: Request, app_id: Optional[str] = Query(default=None, description="应用ID")):
try: try:
myLogger.info_logger("Receive request: /AppStatus") myLogger.info_logger("Receive request: /AppStatus")
get_headers(request) get_headers(request)
@ -526,6 +534,27 @@ def AppUpdateUser(request: Request,user_name: Optional[str] = Query(default=None
return response return response
@router.api_route("/AppListSettings", methods=['GET', 'POST'], summary="获取配置信息")
def list_settings():
items = settings.list_all_settings()
return [{
'key': key,
'value': value
} for key, value in items.items()]
@router.api_route("/AppUpdateSettings", methods=['GET', 'POST'], summary="创建或者更新配置信息")
def create_or_update_settings(item: SettingItem):
settings.update_setting(item.key, item.value)
@router.api_route("/AppDeleteSettings", methods=['GET', 'POST'], summary="删除配置信息")
def delete_settings(item: SettingItem):
settings.delete_setting(item.key, item.value)
def get_headers(request): def get_headers(request):
headers = request.headers headers = request.headers
try: try:

View file

@ -1,7 +1,6 @@
import api.v1.api as api_router_v1 import argparse
import uvicorn import uvicorn
from api.utils.common_log import myLogger
from api.utils import shell_execute
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
@ -11,10 +10,19 @@ from fastapi.openapi.docs import (
get_swagger_ui_oauth2_redirect_html, get_swagger_ui_oauth2_redirect_html,
) )
import api.v1.api as api_router_v1
from api.utils.common_log import myLogger
from api.utils import shell_execute
from api.settings.settings import settings
myLogger.info_logger("Start server...") myLogger.info_logger("Start server...")
app = FastAPI(docs_url=None, redoc_url=None, openapi_url="/") app = FastAPI(docs_url=None, redoc_url=None, openapi_url="/")
def get_app():
def get_app():
settings.init_config_from_file()
origins = [ origins = [
"http://localhost", "http://localhost",
"http://localhost:9090", "http://localhost:9090",
@ -45,6 +53,7 @@ async def custom_swagger_ui_html():
async def swagger_ui_redirect(): async def swagger_ui_redirect():
return get_swagger_ui_oauth2_redirect_html() return get_swagger_ui_oauth2_redirect_html()
@app.get("/redoc", include_in_schema=False) @app.get("/redoc", include_in_schema=False)
async def redoc_html(): async def redoc_html():
return get_redoc_html( return get_redoc_html(
@ -54,4 +63,9 @@ async def redoc_html():
) )
if __name__ == "__main__": if __name__ == "__main__":
uvicorn.run("main:get_app", host='0.0.0.0', port=5000, reload=True) parser = argparse.ArgumentParser(description='websoft9')
parser.add_argument("--port", type=int, dest='port', default=5000, metavar="port")
parser.add_argument("--config", type=str, dest="config_file", required=True)
args = parser.parse_args()
settings.init_config_from_file(config_file=args.config_file)
uvicorn.run("main:get_app", host='0.0.0.0', port=args.port, reload=True)

0
test/__init__.py Normal file
View file

View file

View file

@ -0,0 +1,46 @@
import os
import tempfile
import unittest
from appmanage.api.settings.settings import settings
class TestSettings(unittest.TestCase):
fd = None
@classmethod
def setUpClass(cls):
fd = tempfile.NamedTemporaryFile("w")
print(fd.name)
fd.write("a=b\nc=d\n")
fd.flush()
settings.init_config_from_file(fd.name)
def test_get_config(self):
self.assertEqual(settings.get_setting("a"), "b")
self.assertTrue(settings.get_setting("e") is None)
def test_update_config(self):
self.assertEqual(settings.get_setting("a"), "b")
settings.update_setting("a", "i")
self.assertEqual(settings.get_setting("a"), "i")
def test_list_settings(self):
data = settings.list_all_settings()
self.assertTrue(data is not None)
def test_delete_config(self):
settings.update_setting("x", "y")
v = settings.get_setting("x")
self.assertTrue(v is not None)
settings.delete_setting("x", v)
self.assertTrue(settings.get_setting("x") is None)
@classmethod
def tearDownClass(cls):
if cls.fd:
cls.fd.close()
if __name__ == '__main__':
unittest.main()