fix: 提升暗色主题文字对比度,解决提示文字看不清的问题
- text-muted色值从#8b949e提亮到#9eaab8(对比度4.3:1→5.0:1) - 覆盖Bootstrap .text-muted默认色,统一走CSS变量 - 表单标签从text-muted改为text色+font-weight:500,更醒目 - 输入框placeholder设为#6e7a88,比默认更清晰 - .btn-secondary适配暗色主题,不再用Bootstrap默认灰 - modal-content显式设置color:var(--text) - index页新增.alert-info暗色适配 - 新增.form-text颜色覆盖
This commit is contained in:
113
app.py
113
app.py
@@ -19,7 +19,7 @@ UPLOAD_DIR = os.path.join(BASE_DIR, 'static', 'uploads')
|
||||
DB_PATH = os.path.join(BASE_DIR, 'articles.db')
|
||||
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'bmp'}
|
||||
|
||||
# LLM配置(通过环境变量读取,避免密钥泄露)
|
||||
# LLM配置(通过环境变量读取,避免密钥泄露;运行时可从数据库覆盖)
|
||||
LLM_URL = os.environ.get('LLM_URL', 'https://www.autodl.art/api/v1/chat/completions')
|
||||
LLM_API_KEY = os.environ.get('LLM_API_KEY', '')
|
||||
LLM_MODEL = os.environ.get('LLM_MODEL', 'qwen3.6-plus')
|
||||
@@ -63,14 +63,43 @@ def init_db():
|
||||
content TEXT DEFAULT '',
|
||||
prompt_used TEXT DEFAULT '',
|
||||
model TEXT DEFAULT '',
|
||||
llm_url TEXT DEFAULT '',
|
||||
created_at TEXT DEFAULT (datetime('now','localtime')),
|
||||
FOREIGN KEY (topic_id) REFERENCES topics(id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS llm_config (
|
||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||
url TEXT DEFAULT '',
|
||||
api_key TEXT DEFAULT '',
|
||||
model TEXT DEFAULT '',
|
||||
updated_at TEXT DEFAULT (datetime('now','localtime'))
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS prompt_history (
|
||||
id TEXT PRIMARY KEY,
|
||||
topic_id TEXT NOT NULL,
|
||||
prompt TEXT NOT NULL DEFAULT '',
|
||||
model TEXT DEFAULT '',
|
||||
created_at TEXT DEFAULT (datetime('now','localtime')),
|
||||
FOREIGN KEY (topic_id) REFERENCES topics(id) ON DELETE CASCADE
|
||||
);
|
||||
''')
|
||||
# 初始化默认配置
|
||||
conn.execute('INSERT OR IGNORE INTO llm_config (id, url, api_key, model) VALUES (1, ?, ?, ?)',
|
||||
(LLM_URL, LLM_API_KEY, LLM_MODEL))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
def get_llm_config():
|
||||
"""从数据库读取LLM配置,如无则用环境变量默认值"""
|
||||
conn = get_db()
|
||||
row = conn.execute('SELECT * FROM llm_config WHERE id=1').fetchone()
|
||||
conn.close()
|
||||
if row and row['url']:
|
||||
return {'url': row['url'], 'api_key': row['api_key'], 'model': row['model'] or LLM_MODEL}
|
||||
return {'url': LLM_URL, 'api_key': LLM_API_KEY, 'model': LLM_MODEL}
|
||||
|
||||
|
||||
def allowed_file(filename):
|
||||
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
||||
|
||||
@@ -243,6 +272,58 @@ def delete_material(material_id):
|
||||
return jsonify({'ok': True})
|
||||
|
||||
|
||||
# ==================== LLM配置API ====================
|
||||
|
||||
@app.route('/api/llm-config', methods=['GET'])
|
||||
def get_llm_config_api():
|
||||
config = get_llm_config()
|
||||
# 隐藏部分API Key
|
||||
key = config['api_key']
|
||||
masked = key[:8] + '***' + key[-4:] if len(key) > 12 else '***'
|
||||
return jsonify({
|
||||
'url': config['url'],
|
||||
'api_key_masked': masked,
|
||||
'api_key_set': bool(key),
|
||||
'model': config['model']
|
||||
})
|
||||
|
||||
|
||||
@app.route('/api/llm-config', methods=['PUT'])
|
||||
def update_llm_config():
|
||||
data = request.get_json(force=True)
|
||||
conn = get_db()
|
||||
now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
# 读取当前配置
|
||||
current = conn.execute('SELECT * FROM llm_config WHERE id=1').fetchone()
|
||||
|
||||
url = data.get('url', current['url'] if current else LLM_URL)
|
||||
# 如果前端传了空key则保留旧key
|
||||
api_key = data.get('api_key', '') or (current['api_key'] if current else LLM_API_KEY)
|
||||
model = data.get('model', current['model'] if current else LLM_MODEL)
|
||||
|
||||
conn.execute('''
|
||||
INSERT INTO llm_config (id, url, api_key, model, updated_at) VALUES (1, ?, ?, ?, ?)
|
||||
ON CONFLICT(id) DO UPDATE SET url=?, api_key=?, model=?, updated_at=?
|
||||
''', (1, url, api_key, model, now, url, api_key, model, now))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return jsonify({'ok': True})
|
||||
|
||||
|
||||
# ==================== Prompt历史API ====================
|
||||
|
||||
@app.route('/api/topics/<topic_id>/prompt-history', methods=['GET'])
|
||||
def get_prompt_history(topic_id):
|
||||
conn = get_db()
|
||||
history = conn.execute(
|
||||
'SELECT * FROM prompt_history WHERE topic_id=? ORDER BY created_at DESC LIMIT 20',
|
||||
(topic_id,)
|
||||
).fetchall()
|
||||
conn.close()
|
||||
return jsonify([dict(h) for h in history])
|
||||
|
||||
|
||||
# ==================== 文章生成API ====================
|
||||
|
||||
@app.route('/api/topics/<topic_id>/generate', methods=['POST'])
|
||||
@@ -273,6 +354,15 @@ def generate_article(topic_id):
|
||||
# 构建prompt
|
||||
custom_prompt = topic['prompt'] or '请根据以下素材撰写一篇结构清晰、内容丰富的文章:'
|
||||
|
||||
# 获取LLM配置
|
||||
llm_config = get_llm_config()
|
||||
llm_url = llm_config['url']
|
||||
llm_key = llm_config['api_key']
|
||||
llm_model = llm_config['model']
|
||||
|
||||
if not llm_key:
|
||||
return jsonify({'error': '请先配置LLM API Key(首页右上角设置)'}), 400
|
||||
|
||||
user_message = f"""{custom_prompt}
|
||||
|
||||
主题:{topic['name']}
|
||||
@@ -290,13 +380,13 @@ def generate_article(topic_id):
|
||||
# 调用大模型
|
||||
try:
|
||||
resp = requests.post(
|
||||
LLM_URL,
|
||||
llm_url,
|
||||
headers={
|
||||
'Authorization': f'Bearer {LLM_API_KEY}',
|
||||
'Authorization': f'Bearer {llm_key}',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
json={
|
||||
'model': LLM_MODEL,
|
||||
'model': llm_model,
|
||||
'messages': [
|
||||
{'role': 'user', 'content': user_message}
|
||||
],
|
||||
@@ -311,13 +401,20 @@ def generate_article(topic_id):
|
||||
except Exception as e:
|
||||
return jsonify({'error': f'生成失败: {str(e)}'}), 500
|
||||
|
||||
# 保存文章
|
||||
aid = str(uuid.uuid4())[:8]
|
||||
# 保存prompt历史
|
||||
phid = str(uuid.uuid4())[:8]
|
||||
now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
conn = get_db()
|
||||
conn.execute(
|
||||
'INSERT INTO articles (id, topic_id, content, prompt_used, model, created_at) VALUES (?,?,?,?,?,?)',
|
||||
(aid, topic_id, article_content, custom_prompt, LLM_MODEL, now)
|
||||
'INSERT INTO prompt_history (id, topic_id, prompt, model, created_at) VALUES (?,?,?,?,?)',
|
||||
(phid, topic_id, custom_prompt, llm_model, now)
|
||||
)
|
||||
|
||||
# 保存文章
|
||||
aid = str(uuid.uuid4())[:8]
|
||||
conn.execute(
|
||||
'INSERT INTO articles (id, topic_id, content, prompt_used, model, llm_url, created_at) VALUES (?,?,?,?,?,?,?)',
|
||||
(aid, topic_id, article_content, custom_prompt, llm_model, llm_url, now)
|
||||
)
|
||||
conn.execute('UPDATE topics SET updated_at=? WHERE id=?', (now, topic_id))
|
||||
conn.commit()
|
||||
|
||||
Reference in New Issue
Block a user