完善会话草稿恢复与附件保留,统一人工服务时区
This commit is contained in:
@@ -2,21 +2,35 @@
|
||||
|
||||
import os
|
||||
import secrets
|
||||
import re
|
||||
import time
|
||||
from datetime import datetime
|
||||
from urllib.parse import unquote
|
||||
|
||||
from .config import get_setting
|
||||
|
||||
|
||||
|
||||
def cleanup_uploads():
|
||||
"""删除 uploads/ 里 7 天前的图片"""
|
||||
"""Keep attachments referenced by chat history; expire unused uploads after 7 days."""
|
||||
try:
|
||||
udir = get_setting('UPLOAD_DIR')
|
||||
if not os.path.isdir(udir):
|
||||
return
|
||||
from .db import connect
|
||||
connection = connect()
|
||||
try:
|
||||
referenced = set()
|
||||
rows = connection.execute("SELECT content FROM messages WHERE content LIKE '%/uploads/%' "
|
||||
"UNION ALL SELECT content FROM human_messages WHERE content LIKE '%/uploads/%'")
|
||||
for (content,) in rows:
|
||||
referenced.update(unquote(name) for name in re.findall(r'/uploads/([A-Za-z0-9_.%-]+)', content or ''))
|
||||
finally:
|
||||
connection.close()
|
||||
cutoff = time.time() - 7 * 86400
|
||||
for f in os.listdir(udir):
|
||||
if f in referenced:
|
||||
continue
|
||||
fp = os.path.join(udir, f)
|
||||
if os.path.isfile(fp) and os.path.getmtime(fp) < cutoff:
|
||||
os.remove(fp)
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
"""Human conversation lifecycle; typing state is supplied by the app."""
|
||||
|
||||
import time
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from .db import connect
|
||||
|
||||
SERVICE_TIMEZONE = timezone(timedelta(hours=8))
|
||||
|
||||
|
||||
def human_request(data):
|
||||
conv_id = data.get('conv_id')
|
||||
if not conv_id:
|
||||
return ({'ok': False, 'error': 'no conv'}, 400)
|
||||
if not 8 <= datetime.now().hour < 22:
|
||||
return {'ok': False, 'closed': True, 'msg': '人工客服工作时间为每天8:00-22:00,当前为非工作时间,请留言'}
|
||||
now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
service_time = datetime.now(SERVICE_TIMEZONE)
|
||||
if not 8 <= service_time.hour < 22:
|
||||
return {'ok': False, 'closed': True, 'msg': '人工客服工作时间为每天北京时间8:00-22:00,当前为非工作时间,请留言'}
|
||||
now = service_time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
conn = connect()
|
||||
conn.execute('BEGIN IMMEDIATE')
|
||||
if not conn.execute('SELECT 1 FROM chat_history WHERE id=?', (conv_id,)).fetchone():
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
"""HTTP adapters for files; business logic lives in services."""
|
||||
|
||||
from flask import Blueprint, abort, request, send_from_directory
|
||||
from flask import Blueprint, render_template, request, send_from_directory
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
from .. import file_service, visitor_service
|
||||
from ..config import get_setting
|
||||
|
||||
bp = Blueprint('files', __name__)
|
||||
|
||||
|
||||
def unavailable(staff=False):
|
||||
return render_template('attachment_unavailable.html', staff=staff), 404, {'Cache-Control':'private, no-store'}
|
||||
|
||||
@bp.route('/api/upload', methods=['POST'])
|
||||
def upload_file():
|
||||
return file_service.upload_file(request.files)
|
||||
@@ -15,14 +20,20 @@ def upload_file():
|
||||
@bp.route('/uploads/<path:name>')
|
||||
def uploaded_file(name):
|
||||
if not visitor_service.owns_upload(name):
|
||||
abort(404)
|
||||
response = send_from_directory(get_setting('UPLOAD_DIR'), name)
|
||||
return unavailable()
|
||||
try:
|
||||
response = send_from_directory(get_setting('UPLOAD_DIR'), name)
|
||||
except NotFound:
|
||||
return unavailable()
|
||||
response.headers['Cache-Control'] = 'private, no-store'
|
||||
return response
|
||||
|
||||
|
||||
@bp.route('/api/staff/uploads/<path:name>')
|
||||
def staff_uploaded_file(name):
|
||||
response = send_from_directory(get_setting('UPLOAD_DIR'), name)
|
||||
try:
|
||||
response = send_from_directory(get_setting('UPLOAD_DIR'), name)
|
||||
except NotFound:
|
||||
return unavailable(staff=True)
|
||||
response.headers['Cache-Control'] = 'private, no-store'
|
||||
return response
|
||||
|
||||
Reference in New Issue
Block a user