Skip to content

Commit 79042df

Browse files
authored
Merge pull request #110 from katagomo/main
更新配置文件读取和调用方法
2 parents c5546d3 + d15d799 commit 79042df

File tree

7 files changed

+108
-86
lines changed

7 files changed

+108
-86
lines changed

Dockerfile

+24-19
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,37 @@
1-
# 第一阶段:安装GCC
2-
FROM python:3.12.1-alpine as gcc_installer
3-
4-
# 安装GCC及其他依赖
5-
RUN apk add --no-cache gcc musl-dev jpeg-dev zlib-dev libjpeg
6-
7-
# 第二阶段:安装Python依赖
8-
FROM gcc_installer as requirements_installer
1+
# 使用更小的基础镜像
2+
FROM python:3.12.1-slim as builder
93

104
# 设置工作目录
115
WORKDIR /app
126

13-
# 只复制 requirements.txt,充分利用 Docker 缓存层
14-
COPY ./requirements.txt /app/
7+
# 安装必要的编译依赖
8+
RUN apt-get update && apt-get install -y --no-install-recommends \
9+
gcc \
10+
libjpeg-dev \
11+
zlib1g-dev \
12+
&& rm -rf /var/lib/apt/lists/*
1513

16-
# 安装Python依赖
17-
RUN pip install --no-user --prefix=/install -r requirements.txt
14+
# 复制依赖文件
15+
COPY requirements.txt .
1816

19-
# 第三阶段:运行环境
20-
FROM python:3.12.1-alpine
17+
# 安装依赖到指定目录
18+
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
19+
20+
# 最终阶段
21+
FROM python:3.12.1-slim
2122

22-
# 设置工作目录
2323
WORKDIR /app
2424

25-
# 复制Python依赖
26-
COPY --from=requirements_installer /install /usr/local
25+
# 只复制必要的Python包
26+
COPY --from=builder /install /usr/local
27+
28+
# 复制应用代码
29+
COPY . .
2730

28-
# 复制项目代码
29-
COPY ./ /app
31+
# 清理不必要的文件(如果有的话)
32+
RUN rm -rf __pycache__ \
33+
&& rm -rf *.pyc \
34+
&& find . -type d -name __pycache__ -exec rm -r {} +
3035

3136
# 设置启动命令
3237
CMD ["python", "/app/app.py"]

api/__init__.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010
logger = logging.getLogger(__name__)
1111

1212
v1_bp = Blueprint('v1', __name__, url_prefix='/api/v1')
13-
# Blueprint直接复制app配置项
1413
v1_bp.config = app.config.copy()
1514

15+
navidrome_bp = Blueprint('navidrome', __name__, url_prefix='/api/navidrome')
16+
navidrome_bp.config = app.config.copy()
17+
1618

1719
# 缓存逻辑
1820
cache_dir = './flask_cache'

api/login.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44

55
from mod.auth import webui, cookie
66
from mod.auth.authentication import require_auth
7-
from mod.args import GlobalArgs
8-
9-
args = GlobalArgs()
7+
from mod.args import args
108

119

1210
@app.route('/login')
@@ -16,7 +14,7 @@ def login_check():
1614
未登录时返回页面,已登录时重定向至主页
1715
:return:
1816
"""
19-
if require_auth(request=request) < 0 and args.auth:
17+
if require_auth(request=request) < 0 and args("auth"):
2018
return render_template_string(webui.html_login())
2119

2220
return redirect('/src')
@@ -33,7 +31,7 @@ def login_api():
3331
data = request.get_json()
3432
if 'password' in data:
3533
pwd = data['password']
36-
if args.valid(pwd):
34+
if pwd in args("auth"):
3735
logger.info("user login")
3836
response = make_response(jsonify(success=True))
3937
response.set_cookie('api_auth_token', cookie.set_cookie(pwd))

app.py

+5-7
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,22 @@
44
from waitress import serve
55

66
from mod import check_update
7-
from mod.args import GlobalArgs
7+
from mod.args import args
88
from mod.dev.debugger import debugger
99
from api import *
1010
from api import __import__
1111

12-
args = GlobalArgs()
13-
1412

1513
def run_server(debug=False):
1614
if not debug:
1715
# Waitress WSGI 服务器
18-
serve(app, host=args.ip, port=args.port, threads=32, channel_timeout=30)
16+
serve(app, host=args("server", "ip"), port=args("server", "port"), threads=32, channel_timeout=30)
1917
else:
2018
debugger.debug = True
2119
debugger.log("info", "Debug模式已开启")
2220
debugger.log("info", f"Version: {args.version}")
23-
if args.auth:
24-
debugger.log("info", f"Auth: {args.auth}")
25-
app.run(host='*', port=args.port, debug=True)
21+
debugger.log("info", f"Auth: {args('auth')}")
22+
app.run(host='0.0.0.0', port=args("server", "port"), debug=True)
2623

2724

2825
if __name__ == '__main__':
@@ -41,3 +38,4 @@ def run_server(debug=False):
4138
app.register_blueprint(v1_bp)
4239
# 启动
4340
run_server(args.debug)
41+

buildup.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
subprocess.run("pip install -r requirements.txt", shell=True)
1111
subprocess.run("pip install pyinstaller", shell=True)
1212

13-
from mod.args import GlobalArgs
13+
from mod.args import args
1414

1515
sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach())
1616

@@ -22,7 +22,7 @@
2222
if (ARCHITECTURE == "x86_64") & (platform.architecture()[0] == "32bit"):
2323
ARCHITECTURE = "i386"
2424
APP_NAME = "lrcapi"
25-
APP_VERSION = GlobalArgs().version
25+
APP_VERSION = args.version
2626
PACK_NAME = f"{APP_NAME}-{APP_VERSION}-{PLATFORM}-{ARCHITECTURE}{'.exe' if PLATFORM == 'Windows' else ''}"
2727

2828
# 打包

mod/args/__init__.py

+68-47
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
parser.add_argument('--port', type=int, default=28883, help='应用的运行端口,默认28883')
1313
parser.add_argument('--auth', type=str, default='', help='用于验证Header.Authentication字段,建议纯ASCII字符')
1414
parser.add_argument("--debug", action="store_true", help="Enable debug mode")
15+
parser.add_argument('--ip', type=str, default='*', help='服务器监听IP,默认*')
16+
parser.add_argument('--token', type=str, default='', help='用于翻译歌词的API Token')
1517
kw_args, unknown_args = parser.parse_known_args()
1618
arg_auths: dict = {kw_args.auth: "rwd"} if kw_args.auth else None
1719

@@ -70,6 +72,7 @@ def __init__(self):
7072
self.auth = os.environ.get('API_AUTH', None)
7173
self.port = os.environ.get('API_PORT', None)
7274
self.auths = None
75+
self.token = os.environ.get('API_TOKEN', None)
7376
if self.auth:
7477
self.auths: dict = {
7578
self.auth: "all"
@@ -120,48 +123,31 @@ class Args():
120123
def __init__(self, data=None, default=None):
121124
self.__data: dict = data
122125
self.__default: dict = default or {}
126+
self.version = "1.5.7"
127+
self.debug = kw_args.debug
123128

124129
def __invert__(self):
125-
return self.__data
126-
127-
def __get(self, key: str) -> 'Args':
128-
# 如果不是字典,则返回Args包装的None
129-
if not isinstance(self.__data, dict):
130-
return Args()
131-
else:
132-
# 如果key不存在,则返回Args包装的None
133-
if key not in self.__data.keys():
134-
return Args()
135-
# 如果key对应的值是字典,则返回Args包装的字典,默认值从__default中获取
136-
if isinstance(self.__data[key], dict):
137-
return Args(data=self.__data[key], default=self.__default.get(key, {}))
138-
# 如果key对应的值不是字典,则返回Args包装的值
139-
return Args(data=self.__data[key])
140-
141-
def __set_default(self, default_data: dict):
142-
self.__default = default_data
143-
144-
def __call__(self):
145130
"""
146131
JSON: config/config.json
147132
YAML: config/config.yaml
148133
149134
default: YAML
150135
"""
136+
# 1. 首先用默认值初始化
137+
self.__data = self.__default.copy()
138+
139+
# 2. 加载配置文件,使用update而不是直接赋值
151140
for loader in (self.__load_json, self.__load_yaml):
152141
data = loader()
153142
if isinstance(data, dict):
154-
self.__data = data
155-
return
156-
# 如果没有读取到有效数据,则使用默认数据
157-
self.__data = self.__default
158-
# 写入默认数据
159-
directory = os.path.join(os.getcwd(), "config")
160-
if not os.path.exists(directory):
161-
os.makedirs(directory)
162-
file_path = os.path.join(directory, "config.yaml")
163-
with open(file_path, "w+") as yaml_file:
164-
yaml.dump(DEFAULT_DATA, yaml_file)
143+
self.__data.update(data)
144+
break
145+
146+
# 3. 加载环境变量
147+
self.__load_env()
148+
149+
# 4. 最后加载命令行参数(最高优先级)
150+
self.__load_arg()
165151

166152
@staticmethod
167153
def __load_json() -> dict|None:
@@ -181,17 +167,55 @@ def __load_yaml() -> dict|None:
181167
return yaml.safe_load(yaml_file)
182168
except (yaml.YAMLError, FileNotFoundError):
183169
return None
184-
185-
def __getattribute__(self, item):
186-
if item.startswith('_'):
187-
return super().__getattribute__(item)
188-
return self.__get(item)
189-
190-
def __str__(self):
191-
return str({
192-
"data": self.__data,
193-
"default": self.__default
194-
})
170+
171+
def __load_env(self):
172+
auth = os.environ.get('API_AUTH', None)
173+
port = os.environ.get('API_PORT', None)
174+
token = os.environ.get('API_TOKEN', None)
175+
if auth:
176+
self.__data["auth"] = {auth: "all"}
177+
if port:
178+
# 确保 server 是一个字典
179+
if not isinstance(self.__data.get("server"), dict):
180+
self.__data["server"] = {"ip": "*"}
181+
self.__data["server"]["port"] = port
182+
if token:
183+
self.__data["token"] = token
184+
185+
def __load_arg(self):
186+
auth = kw_args.auth
187+
port = kw_args.port
188+
ip = kw_args.ip
189+
token = kw_args.token
190+
logger.info(f"Auth: {auth}; Port: {port}; IP: {ip}")
191+
if auth:
192+
self.__data["auth"] = {auth: "all"}
193+
if port:
194+
if not isinstance(self.__data.get("server"), dict):
195+
self.__data["server"] = {"ip": "*"}
196+
self.__data["server"]["port"] = port
197+
if ip:
198+
if not isinstance(self.__data.get("server"), dict):
199+
self.__data["server"] = {"ip": "*"}
200+
self.__data["server"]["ip"] = ip
201+
if token:
202+
self.__data["token"] = token
203+
#logger.info(f"Final config data: {self.__data}")
204+
205+
def __call__(self, *args):
206+
data = self.__data
207+
default = self.__default
208+
for key in args:
209+
if key in data:
210+
data = data[key]
211+
elif key in default:
212+
data = default[key]
213+
else:
214+
return None
215+
return data
216+
217+
args = Args(default=DEFAULT_DATA)
218+
~args # 初始化配置
195219

196220
if __name__ == '__main__':
197221
default: dict = {
@@ -202,8 +226,5 @@ def __str__(self):
202226
"auth": {}
203227
}
204228
config = Args(default=default)
205-
config()
206-
print(~config.server)
207-
print(~config.server.port)
208-
print(~config.auth)
209-
print(~config.auth.admin)
229+
~config
230+
print(config("server", "port"))

mod/auth/authentication.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import flask
22

33
from . import cookie
4-
from mod.args import GlobalArgs
5-
6-
args = GlobalArgs()
4+
from mod.args import args
75

86

97
def require_auth(request: flask.request, permission: str = 'r'):
@@ -19,7 +17,7 @@ def require_auth(request: flask.request, permission: str = 'r'):
1917
cookie_permission: bool = has_permission(get_permission(cookie_key), permission)
2018
header_permission: bool = has_permission(get_permission(auth_header), permission)
2119

22-
if not args.auth:
20+
if not args("auth"):
2321
if permission == 'r':
2422
return 1
2523
else:
@@ -38,7 +36,7 @@ def get_permission(name: str) -> str:
3836
"""
3937
if not name:
4038
return ''
41-
auth_dict: dict = args.auth
39+
auth_dict: dict = args("auth")
4240
return auth_dict.get(name, '')
4341

4442

0 commit comments

Comments
 (0)