Compare commits

...

17 Commits

Author SHA1 Message Date
0912d658b8 feat(v4.2.0): 文件夹支持重命名和删除
- hover文件夹时显示编辑和删除按钮
- 编辑按钮打开重命名模态框
- 删除按钮确认后删除,数据移到未分类
2026-04-22 16:27:38 +08:00
8e63db4424 fix(v4.1.1): 待办事务功能优化
- 修复完成状态切换问题(可来回切换完成/未完成)
- 添加全部/未完成/已完成过滤按钮
- 翻页时保持弹窗滚动位置
2026-04-22 15:54:10 +08:00
0335937312 feat(v4.1.0): 文本类别添加待办事务功能
- 可在文本数据详情中追加待办事务
- 支持事务内容、剩余天数(默认1天)、完成状态
- 历史事务按时间倒序排列,显示创建/更新时间
- 分页显示,每页10条
2026-04-22 13:00:00 +08:00
6a0f8d7196 feat(v4.0.0): 快速插入支持选中位置插入
- 先选中部分文本,再双击可在选中位置前/后插入
- 支持替换选中内容
- 自动识别选中文本并在原内容中定位
2026-04-22 12:49:37 +08:00
407a63f3cf fix: 修复JS正则表达式在模板字符串中被破坏的问题 2026-04-22 12:22:07 +08:00
eec8b477be feat: 详情弹框内容支持Markdown格式显示 2026-04-22 12:06:42 +08:00
489e95d677 fix: 修复显示模式切换按钮无效问题(添加currentItems变量) 2026-04-22 12:02:39 +08:00
47cbcb25bc feat: 添加显示模式切换按钮(单行/双行) 2026-04-22 11:56:59 +08:00
527c411d87 fix: 卡片布局自适应,操作按钮不再被挤出可视区域 2026-04-22 11:35:13 +08:00
c8aecaeb03 fix: 修复文件夹过滤时分页总数计算错误 2026-04-22 11:18:51 +08:00
bf63610510 fix: 点击类别同时展开文件夹并过滤数据 2026-04-22 10:52:30 +08:00
7af3a7f21d feat: 侧边栏文件夹UI优化 - 折叠式设计
- 新建文件夹按钮移到类别右侧(文件夹+图标)
- 默认折叠状态,点击类别名称展开/折叠
- 投影箭头指示展开状态(▶折叠,▼展开)
- 按钮hover时显示绿色背景,更明显可见
2026-04-22 10:44:53 +08:00
4783e9d88e feat: 文件夹功能
- 数据库添加 folders 表和 items.folder_id 字段
- API 新增文件夹 CRUD 接口和条目移动接口
- 侧边栏每个类别下显示文件夹列表
- 新建文件夹按钮和模态框
- 条目卡片添加「移动到文件夹」按钮
- 点击文件夹过滤显示该文件夹下的数据
2026-04-21 22:23:20 +08:00
ccbd24be11 feat: 侧边栏和顶部按钮栏固定显示
- 侧边栏固定在左侧(position: fixed)
- 顶部按钮栏固定在顶部(position: sticky)
- 主内容区留出侧边栏空间(margin-left: 200px)
- 滚动页面时侧边栏和顶部栏保持不动
2026-04-21 22:12:03 +08:00
16ed5459fa feat: 连接状态移到侧边栏标题后面,点击标题回到首页 2026-04-20 18:24:29 +08:00
ca7dc10e92 feat: 顶部添加连接状态指示器,掉线时禁止编辑和新增操作 2026-04-20 18:10:37 +08:00
94efc524c6 fix: 修复 JavaScript 语法错误(多余的闭合括号导致页面无法加载) 2026-04-19 23:15:17 +08:00
11 changed files with 14623 additions and 121 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -2,12 +2,17 @@
import sqlite3
import json
from datetime import datetime
import shutil
import os
from datetime import datetime, timedelta
from typing import Optional, List, Dict, Any
from contextlib import contextmanager
from .config import DATABASE_URL, TODO_STATUS, PRIORITY_LEVELS
# 备份目录
BACKUP_DIR = os.path.join(os.path.dirname(DATABASE_URL), 'backups')
class Database:
"""SQLite数据库管理"""
@@ -80,6 +85,28 @@ class Database:
)
""")
# 邮箱表
cursor.execute("""
CREATE TABLE IF NOT EXISTS emails (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT NOT NULL UNIQUE,
name TEXT,
created_at TEXT NOT NULL
)
""")
# 邮件发送记录表
cursor.execute("""
CREATE TABLE IF NOT EXISTS email_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
item_id INTEGER NOT NULL,
email TEXT NOT NULL,
sent_at TEXT NOT NULL,
success INTEGER DEFAULT 1,
FOREIGN KEY (item_id) REFERENCES items(id) ON DELETE CASCADE
)
""")
# 创建索引
cursor.execute("CREATE INDEX IF NOT EXISTS idx_items_type ON items(type)")
cursor.execute("CREATE INDEX IF NOT EXISTS idx_items_status ON items(status)")
@@ -177,6 +204,41 @@ class Database:
return items
def count_items(self, type: str = None, status: str = None, tag: str = None,
keyword: str = None) -> int:
"""计算符合条件的条目总数"""
with self.get_conn() as conn:
cursor = conn.cursor()
query = "SELECT COUNT(DISTINCT i.id) as count FROM items i"
params = []
conditions = []
# 标签过滤需要JOIN
if tag:
query += " JOIN item_tags it ON i.id = it.item_id JOIN tags t ON it.tag_id = t.id"
conditions.append("t.name = ?")
params.append(tag)
if type:
conditions.append("i.type = ?")
params.append(type)
if status:
conditions.append("i.status = ?")
params.append(status)
if keyword:
conditions.append("(i.title LIKE ? OR i.content LIKE ? OR i.note LIKE ?)")
keyword_pattern = f"%{keyword}%"
params.extend([keyword_pattern, keyword_pattern, keyword_pattern])
if conditions:
query += " WHERE " + " AND ".join(conditions)
cursor.execute(query, params)
return cursor.fetchone()['count']
def update_item(self, item_id: int, **kwargs) -> bool:
"""更新条目"""
allowed_fields = ['type', 'title', 'content', 'url', 'source', 'status', 'priority', 'due_date', 'note']
@@ -244,6 +306,18 @@ class Database:
""")
return [dict(row) for row in cursor.fetchall()]
def update_tag(self, tag_id: int, name: str) -> bool:
"""更新标签名称"""
with self.get_conn() as conn:
cursor = conn.cursor()
# 检查名称是否已存在(排除自己)
cursor.execute("SELECT id FROM tags WHERE name = ? AND id != ?", (name, tag_id))
if cursor.fetchone():
return False # 名称已存在
cursor.execute("UPDATE tags SET name = ? WHERE id = ?", (name, tag_id))
conn.commit()
return cursor.rowcount > 0
def delete_tag(self, tag_id: int = None, name: str = None) -> bool:
"""删除标签"""
with self.get_conn() as conn:
@@ -290,6 +364,90 @@ class Database:
""", (item_id,))
return [row['name'] for row in cursor.fetchall()]
# ============ Email 操作 ============
def create_email(self, email: str, name: str = None) -> int:
"""创建邮箱"""
now = datetime.now().isoformat()
with self.get_conn() as conn:
cursor = conn.cursor()
try:
cursor.execute("INSERT INTO emails (email, name, created_at) VALUES (?, ?, ?)",
(email, name, now))
conn.commit()
return cursor.lastrowid
except sqlite3.IntegrityError:
# 邮箱已存在返回已有ID
cursor.execute("SELECT id FROM emails WHERE email = ?", (email,))
return cursor.fetchone()['id']
def list_emails(self) -> List[Dict[str, Any]]:
"""列出所有邮箱"""
with self.get_conn() as conn:
cursor = conn.cursor()
cursor.execute("SELECT * FROM emails ORDER BY created_at DESC")
return [dict(row) for row in cursor.fetchall()]
def get_email(self, email_id: int) -> Optional[Dict[str, Any]]:
"""获取单个邮箱"""
with self.get_conn() as conn:
cursor = conn.cursor()
cursor.execute("SELECT * FROM emails WHERE id = ?", (email_id,))
row = cursor.fetchone()
return dict(row) if row else None
def update_email(self, email_id: int, email: str = None, name: str = None) -> bool:
"""更新邮箱"""
now = datetime.now().isoformat()
with self.get_conn() as conn:
cursor = conn.cursor()
if email:
# 检查邮箱是否已存在(排除自己)
cursor.execute("SELECT id FROM emails WHERE email = ? AND id != ?", (email, email_id))
if cursor.fetchone():
return False # 邎箱已存在
if email and name:
cursor.execute("UPDATE emails SET email = ?, name = ? WHERE id = ?", (email, name, email_id))
elif email:
cursor.execute("UPDATE emails SET email = ? WHERE id = ?", (email, email_id))
elif name:
cursor.execute("UPDATE emails SET name = ? WHERE id = ?", (name, email_id))
conn.commit()
return cursor.rowcount > 0
def delete_email(self, email_id: int) -> bool:
"""删除邮箱"""
with self.get_conn() as conn:
cursor = conn.cursor()
cursor.execute("DELETE FROM emails WHERE id = ?", (email_id,))
conn.commit()
return cursor.rowcount > 0
# ============ 邮件日志 操作 ============
def log_email_send(self, item_id: int, email: str, success: bool = True) -> int:
"""记录邮件发送"""
now = datetime.now().isoformat()
with self.get_conn() as conn:
cursor = conn.cursor()
cursor.execute("""
INSERT INTO email_logs (item_id, email, sent_at, success)
VALUES (?, ?, ?, ?)
""", (item_id, email, now, 1 if success else 0))
conn.commit()
return cursor.lastrowid
def get_email_logs(self, item_id: int) -> List[Dict[str, Any]]:
"""获取收藏的邮件发送记录"""
with self.get_conn() as conn:
cursor = conn.cursor()
cursor.execute("""
SELECT * FROM email_logs
WHERE item_id = ?
ORDER BY sent_at DESC
""", (item_id,))
return [dict(row) for row in cursor.fetchall()]
def stats(self) -> Dict[str, Any]:
"""获取统计信息"""
self._ensure_init()
@@ -315,6 +473,252 @@ class Database:
stats['tags'] = cursor.fetchone()['count']
return stats
# ============ 提醒相关 ============
def get_reminders(self) -> Dict[str, Any]:
"""获取提醒信息:即将到期和已过期的待办"""
self._ensure_init()
with self.get_conn() as conn:
cursor = conn.cursor()
now = datetime.now()
reminders = {
'overdue': [], # 已过期
'due_today': [], # 今天到期
'due_soon': [] # 24小时内到期不含今天
}
# 查询未完成的待办(有截止日期的)
cursor.execute("""
SELECT * FROM items
WHERE type = 'todo'
AND status != 'completed'
AND due_date IS NOT NULL
AND due_date != ''
ORDER BY due_date ASC
""")
for row in cursor.fetchall():
item = dict(row)
item['tags'] = self._get_item_tags(conn, item['id'])
try:
due_date_str = item['due_date']
# 支持多种日期格式
if 'T' in due_date_str:
# ISO 格式2026-04-16T14:30
due_date = datetime.strptime(due_date_str[:16], '%Y-%m-%dT%H:%M')
elif len(due_date_str) == 10:
# 只有日期2026-04-16视为当天 23:59:59
due_date = datetime.strptime(due_date_str, '%Y-%m-%d').replace(hour=23, minute=59, second=59)
else:
# 其他格式,尝试解析
due_date = datetime.strptime(due_date_str.split('.')[0], '%Y-%m-%dT%H:%M:%S')
# 计算距离到期的时间
time_left = due_date - now
if time_left.total_seconds() < 0:
# 已过期
days_overdue = abs(int(time_left.total_seconds() / 86400))
item['days_overdue'] = days_overdue
reminders['overdue'].append(item)
elif time_left.total_seconds() < 86400: # 24小时内
# 判断是今天还是明天
if due_date.date() == now.date():
reminders['due_today'].append(item)
else:
reminders['due_soon'].append(item)
elif due_date.date() == now.date():
# 今天到期超过24小时的情况比如现在凌晨截止时间是晚上
reminders['due_today'].append(item)
except (ValueError, AttributeError) as e:
# 日期格式错误,跳过
continue
# 统计总数
reminders['total'] = len(reminders['overdue']) + len(reminders['due_today']) + len(reminders['due_soon'])
return reminders
# ============ 备份操作 ============
def create_backup(self, manual: bool = False) -> Dict[str, Any]:
"""创建数据库备份"""
import os
# 确保备份目录存在
os.makedirs(BACKUP_DIR, exist_ok=True)
now = datetime.now()
backup_name = now.strftime('%Y-%m-%d_%H%M%S')
if manual:
backup_name += '_manual'
backup_path = os.path.join(BACKUP_DIR, f'{backup_name}.db')
# 复制数据库文件
shutil.copy2(self.db_path, backup_path)
# 获取备份信息
backup_info = {
'name': backup_name,
'path': backup_path,
'size': os.path.getsize(backup_path),
'created_at': now.isoformat(),
'manual': manual,
'is_first_of_month': now.day == 1
}
# 保存备份元数据
self._save_backup_meta(backup_info)
# 清理旧备份
self._cleanup_old_backups()
return backup_info
def list_backups(self) -> List[Dict[str, Any]]:
"""列出所有备份"""
import os
if not os.path.exists(BACKUP_DIR):
return []
# 读取备份元数据
meta_path = os.path.join(BACKUP_DIR, 'backup_meta.json')
if os.path.exists(meta_path):
with open(meta_path, 'r') as f:
backups = json.load(f)
else:
# 从文件重建元数据
backups = []
for f in os.listdir(BACKUP_DIR):
if f.endswith('.db'):
path = os.path.join(BACKUP_DIR, f)
backups.append({
'name': f.replace('.db', ''),
'path': path,
'size': os.path.getsize(path),
'created_at': datetime.fromtimestamp(os.path.getmtime(path)).isoformat(),
'manual': '_manual' in f,
'is_first_of_month': self._is_first_of_month_filename(f)
})
# 按时间倒序排列
backups.sort(key=lambda x: x['created_at'], reverse=True)
return backups
def restore_backup(self, backup_name: str) -> bool:
"""恢复备份"""
import os
backup_path = os.path.join(BACKUP_DIR, f'{backup_name}.db')
if not os.path.exists(backup_path):
return False
# 先备份当前数据库(以防万一)
current_backup = self.db_path + '.before_restore'
shutil.copy2(self.db_path, current_backup)
# 恢复备份
shutil.copy2(backup_path, self.db_path)
return True
def delete_backup(self, backup_name: str) -> bool:
"""删除备份"""
import os
backup_path = os.path.join(BACKUP_DIR, f'{backup_name}.db')
if not os.path.exists(backup_path):
return False
os.remove(backup_path)
# 更新元数据
self._remove_backup_meta(backup_name)
return True
def _save_backup_meta(self, backup_info: Dict[str, Any]):
"""保存备份元数据"""
import os
meta_path = os.path.join(BACKUP_DIR, 'backup_meta.json')
# 读取现有元数据
backups = []
if os.path.exists(meta_path):
with open(meta_path, 'r') as f:
backups = json.load(f)
# 添加新备份
backups.append(backup_info)
# 保存
with open(meta_path, 'w') as f:
json.dump(backups, f, indent=2)
def _remove_backup_meta(self, backup_name: str):
"""从元数据中删除备份"""
import os
meta_path = os.path.join(BACKUP_DIR, 'backup_meta.json')
if not os.path.exists(meta_path):
return
with open(meta_path, 'r') as f:
backups = json.load(f)
backups = [b for b in backups if b['name'] != backup_name]
with open(meta_path, 'w') as f:
json.dump(backups, f, indent=2)
def _is_first_of_month_filename(self, filename: str) -> bool:
"""判断是否是每月第一天的备份"""
# 格式2026-04-01_040000.db 或 2026-05-01_...
try:
date_part = filename.split('_')[0]
day = int(date_part.split('-')[2])
return day == 1
except:
return False
def _cleanup_old_backups(self):
"""清理旧备份保留30天 + 每月第一天"""
import os
backups = self.list_backups()
now = datetime.now()
keep_paths = []
for backup in backups:
backup_date = datetime.fromisoformat(backup['created_at'])
days_old = (now - backup_date).days
# 保留条件:
# 1. 手动备份永久保留最多10个
# 2. 30天内的备份
# 3. 每月第一天的备份
if backup['manual']:
# 手动备份保留但最多10个
manual_backups = [b for b in backups if b['manual']]
if manual_backups.index(backup) < 10:
keep_paths.append(backup['path'])
else:
self.delete_backup(backup['name'])
elif days_old <= 30:
keep_paths.append(backup['path'])
elif backup['is_first_of_month']:
# 每月第一天永久保留
keep_paths.append(backup['path'])
else:
# 删除
self.delete_backup(backup['name'])
# 全局数据库实例

0
data/xian_favor.db Normal file
View File

402
logs/api.log Normal file
View File

@@ -0,0 +1,402 @@
* Serving Flask app 'xian_favor.api'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:19014
* Running on http://192.168.2.17:19014
Press CTRL+C to quit
127.0.0.1 - - [19/Apr/2026 10:06:56] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:07:04] "GET /api/items?limit=2&sort_by=updated_at&sort_order=desc HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:07:06] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:07:16] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:07:17] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:07:26] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:07:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:07:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:07:36] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:07:46] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:07:47] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:07:56] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:08:06] "GET /api/health HTTP/1.1" 404 -
192.168.2.10 - - [19/Apr/2026 10:08:15] "GET /api/reminders HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:08:16] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:08:17] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:08:26] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:08:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:08:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:08:36] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:08:46] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:08:47] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:08:56] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:09:06] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:09:16] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:09:17] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:09:26] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:09:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:09:33] "GET /api/health HTTP/1.1" 404 -
192.168.2.8 - - [19/Apr/2026 10:09:33] "GET /api/reminders HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:09:36] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:09:46] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:09:47] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:09:56] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:10:06] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:10:16] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:10:17] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:10:26] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:10:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:10:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:10:36] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:10:46] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:10:47] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:10:56] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:11:06] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:11:16] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:11:17] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:11:26] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:11:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:11:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:11:36] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:11:46] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:11:47] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:11:56] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:12:06] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:12:16] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:12:17] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:12:26] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:12:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:12:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:12:36] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:12:46] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:12:47] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:12:56] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:13:06] "GET /api/health HTTP/1.1" 404 -
192.168.2.10 - - [19/Apr/2026 10:13:16] "GET /api/reminders HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:13:16] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:13:17] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:13:26] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:13:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:13:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:13:36] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:13:46] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:13:47] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:13:56] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:14:06] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:14:16] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:14:17] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:14:26] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:14:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:14:33] "GET /api/health HTTP/1.1" 404 -
192.168.2.8 - - [19/Apr/2026 10:14:33] "GET /api/reminders HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:14:36] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:14:38] "GET /api/health HTTP/1.1" 404 -
192.168.2.10 - - [19/Apr/2026 10:14:44] "GET / HTTP/1.1" 200 -
192.168.2.10 - - [19/Apr/2026 10:14:44] "GET /api/stats HTTP/1.1" 200 -
192.168.2.10 - - [19/Apr/2026 10:14:44] "GET /api/reminders HTTP/1.1" 200 -
192.168.2.10 - - [19/Apr/2026 10:14:44] "GET /api/tags HTTP/1.1" 200 -
192.168.2.10 - - [19/Apr/2026 10:14:44] "GET /api/items?limit=20&offset=0 HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:14:46] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:14:47] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:14:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:14:56] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:14:58] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:15:01] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:15:06] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:15:08] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:15:16] "GET /api/health HTTP/1.1" 404 -
192.168.2.10 - - [19/Apr/2026 10:15:16] "GET /api/items?limit=20&offset=0&sort_by=updated_at&sort_order=desc HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:15:17] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:15:18] "GET /api/health HTTP/1.1" 404 -
192.168.2.10 - - [19/Apr/2026 10:15:21] "GET /api/items?limit=20&offset=0&sort_order=desc HTTP/1.1" 200 -
192.168.2.10 - - [19/Apr/2026 10:15:24] "GET /api/items?limit=20&offset=0&sort_by=created_at&sort_order=desc HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:15:26] "GET /api/health HTTP/1.1" 404 -
192.168.2.10 - - [19/Apr/2026 10:15:27] "GET /api/items?limit=20&offset=0&sort_by=updated_at&sort_order=desc HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:15:28] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:15:31] "GET /api/health HTTP/1.1" 404 -
192.168.2.10 - - [19/Apr/2026 10:15:34] "GET /api/items?limit=20&offset=0&sort_order=desc HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:15:36] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:15:46] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:15:47] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:15:56] "GET /api/health HTTP/1.1" 404 -
192.168.2.10 - - [19/Apr/2026 10:15:56] "GET /api/items/59 HTTP/1.1" 200 -
192.168.2.10 - - [19/Apr/2026 10:16:00] "GET /api/items/59 HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:16:06] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:16:16] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:16:17] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:16:26] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:16:29] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:16:32] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:16:36] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:16:46] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:16:47] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:16:56] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:17:06] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:17:16] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:17:17] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:17:26] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:17:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:17:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:17:36] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:17:46] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:17:47] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:17:56] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:18:06] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:18:16] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:18:17] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:18:26] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:18:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:18:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:18:36] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:18:46] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:18:47] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:18:56] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:19:06] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:19:16] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:19:17] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:19:26] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:19:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:19:33] "GET /api/health HTTP/1.1" 404 -
192.168.2.8 - - [19/Apr/2026 10:19:33] "GET /api/reminders HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:19:36] "GET /api/health HTTP/1.1" 404 -
192.168.2.10 - - [19/Apr/2026 10:19:44] "GET /api/reminders HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:19:47] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:19:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:19:58] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:20:08] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:20:18] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:20:19] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:20:29] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:20:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:20:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:20:39] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:20:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:20:49] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:20:59] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:21:09] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:21:18] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:21:19] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:21:29] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:21:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:21:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:21:39] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:21:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:21:49] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:22:00] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:22:10] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:22:18] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:22:20] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:22:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:22:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:22:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:22:40] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:22:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:22:50] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:23:00] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:23:10] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:23:18] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:23:20] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:23:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:23:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:23:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:23:40] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:23:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:23:50] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:24:00] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:24:10] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:24:18] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:24:20] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:24:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:24:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:24:33] "GET /api/health HTTP/1.1" 404 -
192.168.2.8 - - [19/Apr/2026 10:24:37] "GET /api/reminders HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:24:40] "GET /api/health HTTP/1.1" 404 -
192.168.2.10 - - [19/Apr/2026 10:24:44] "GET /api/reminders HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:24:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:24:50] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:25:00] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:25:10] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:25:18] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:25:20] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:25:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:25:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:25:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:25:40] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:25:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:25:50] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:26:00] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:26:10] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:26:18] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:26:20] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:26:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:26:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:26:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:26:40] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:26:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:26:50] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:27:00] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:27:10] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:27:18] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:27:20] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:27:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:27:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:27:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:27:40] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:27:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:27:50] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:28:00] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:28:10] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:28:18] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:28:20] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:28:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:28:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:28:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:28:40] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:28:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:28:50] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:29:00] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:29:10] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:29:18] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:29:20] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:29:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:29:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:29:33] "GET /api/health HTTP/1.1" 404 -
192.168.2.8 - - [19/Apr/2026 10:29:37] "GET /api/reminders HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:29:40] "GET /api/health HTTP/1.1" 404 -
192.168.2.10 - - [19/Apr/2026 10:29:44] "GET /api/reminders HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:29:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:29:50] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:30:00] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:30:10] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:30:18] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:30:20] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:30:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:30:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:30:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:30:40] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:30:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:30:50] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:31:00] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:31:10] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:31:18] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:31:20] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:31:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:31:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:31:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:31:40] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:31:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:31:50] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:32:00] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:32:10] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:32:18] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:32:20] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:32:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:32:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:32:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:32:40] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:32:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:32:50] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:33:00] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:33:10] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:33:18] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:33:20] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:33:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:33:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:33:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:33:40] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:33:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:33:50] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:34:00] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:34:10] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:34:18] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:34:20] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:34:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:34:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:34:33] "GET /api/health HTTP/1.1" 404 -
192.168.2.8 - - [19/Apr/2026 10:34:37] "GET /api/reminders HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:34:40] "GET /api/health HTTP/1.1" 404 -
192.168.2.10 - - [19/Apr/2026 10:34:44] "GET /api/reminders HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:34:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:34:50] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:35:00] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:35:10] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:35:18] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:35:20] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:35:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:35:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:35:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:35:40] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:35:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:35:50] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:36:00] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:36:10] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:36:18] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:36:20] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:36:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:36:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:36:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:36:40] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:36:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:36:50] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:37:00] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:37:10] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:37:18] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:37:20] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:37:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:37:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:37:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:37:40] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:37:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:37:50] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:38:00] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:38:10] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:38:18] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:38:20] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:38:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:38:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:38:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:38:40] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:38:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:38:50] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:39:00] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:39:10] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:39:18] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:39:20] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:39:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:39:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:39:33] "GET /api/health HTTP/1.1" 404 -
192.168.2.8 - - [19/Apr/2026 10:39:37] "GET /api/reminders HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:39:40] "GET /api/health HTTP/1.1" 404 -
192.168.2.10 - - [19/Apr/2026 10:39:44] "GET /api/reminders HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:39:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:39:50] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:40:00] "GET /api/health HTTP/1.1" 404 -
192.168.2.10 - - [19/Apr/2026 10:40:05] "PUT /api/items/59 HTTP/1.1" 200 -
192.168.2.10 - - [19/Apr/2026 10:40:05] "GET /api/stats HTTP/1.1" 200 -
192.168.2.10 - - [19/Apr/2026 10:40:05] "GET /api/items?limit=20&offset=0&sort_order=desc HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:40:10] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:40:18] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:40:20] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:40:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:40:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:40:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:40:40] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:40:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:40:50] "GET /api/health HTTP/1.1" 404 -
192.168.2.10 - - [19/Apr/2026 10:40:57] "GET /api/items/68 HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:41:00] "GET /api/health HTTP/1.1" 404 -
192.168.2.10 - - [19/Apr/2026 10:41:01] "GET /api/items/68 HTTP/1.1" 200 -
127.0.0.1 - - [19/Apr/2026 10:41:10] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:41:18] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:41:20] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:41:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:41:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:41:33] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:41:40] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:41:48] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:41:50] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:42:00] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:42:10] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:42:18] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:42:20] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:42:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:42:30] "GET /api/health HTTP/1.1" 404 -
127.0.0.1 - - [19/Apr/2026 10:42:33] "GET /api/health HTTP/1.1" 404 -

156
logs/app.log Normal file
View File

@@ -0,0 +1,156 @@
🚀 启动API服务: http://0.0.0.0:19014
* Serving Flask app 'xian_favor.api'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:19014
* Running on http://192.168.2.17:19014
Press CTRL+C to quit
127.0.0.1 - - [20/Apr/2026 18:16:59] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:17:06] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:17:09] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:17:18] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:17:19] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:17:23] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:17:29] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:17:32] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:17:36] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:17:37] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:17:39] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:17:49] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:17:59] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:18:06] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:18:09] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:18:10] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:18:15] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:18:19] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:18:19] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:18:27] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:18:31] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:18:33] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:18:37] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:18:39] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:18:41] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:18:45] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:18:51] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:18:52] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:19:00] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:19:01] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:19:06] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:19:07] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:19:11] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:19:12] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:19:20] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:19:21] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:19:30] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:19:31] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:19:36] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:19:37] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:19:41] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:19:42] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:19:51] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 18:19:55] "GET /api/reminders HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:19:55] "GET /api/reminders HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:20:01] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:20:07] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:20:11] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:20:21] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:20:31] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:20:34] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:20:37] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:20:40] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:20:41] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:20:42] "GET / HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:20:42] "GET /api/stats HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:20:42] "GET /api/stats HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:20:42] "GET /api/reminders HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:20:42] "GET /api/tags HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:20:42] "GET /api/items?limit=20&offset=0 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:20:47] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:20:51] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:20:52] "GET /api/stats HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:20:53] "GET / HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:20:53] "GET /api/stats HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:20:53] "GET /api/stats HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:20:54] "GET /api/reminders HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:20:54] "GET /api/items?limit=20&offset=0 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:20:54] "GET /api/tags HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:20:58] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:21:01] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:21:03] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:21:07] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:21:08] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:21:11] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:21:13] "GET /api/stats HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:21:18] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:21:21] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:21:23] "GET /api/stats HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:21:28] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:21:32] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:21:33] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:21:34] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:21:37] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:21:38] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:21:40] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:21:42] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:21:43] "GET /api/stats HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:21:48] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:21:52] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:21:53] "GET /api/stats HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:21:58] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:22:02] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:22:03] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:22:07] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:22:08] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:22:12] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:22:13] "GET /api/stats HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:22:18] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:22:22] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:22:23] "GET /api/stats HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:22:28] "GET /api/stats HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:22:31] "GET /api/items?limit=20&offset=20 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:22:32] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:22:33] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:22:34] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:22:37] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:22:38] "GET /api/stats HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:22:40] "GET /api/items?limit=20&offset=0 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:22:40] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:22:42] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:22:43] "GET /api/stats HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:22:48] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:22:52] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:22:53] "GET /api/stats HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:22:58] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:23:02] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:23:03] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:23:07] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:23:08] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:23:12] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:23:13] "GET /api/stats HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:23:18] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:23:22] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:23:23] "GET /api/stats HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:23:28] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:23:32] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:23:33] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:23:34] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:23:37] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:23:39] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:23:40] "GET /api/items?limit=1 HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:23:42] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:23:44] "GET /api/stats HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:23:48] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:23:52] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:23:53] "GET /api/stats HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:23:58] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:24:02] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:24:04] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:24:07] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:24:08] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:24:12] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:24:14] "GET /api/stats HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:24:19] "GET /api/stats HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 18:24:22] "GET /api/items?limit=1 HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:24:24] "GET /api/stats HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 18:24:28] "GET /api/stats HTTP/1.1" 200 -

9709
logs/server.log Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -131,6 +131,19 @@ class Database:
)
""")
# 文件夹表
cursor.execute("""
CREATE TABLE IF NOT EXISTS folders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
type TEXT NOT NULL,
parent_id INTEGER DEFAULT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
FOREIGN KEY (parent_id) REFERENCES folders(id) ON DELETE CASCADE
)
""")
# 创建索引
cursor.execute("CREATE INDEX IF NOT EXISTS idx_items_type ON items(type)")
cursor.execute("CREATE INDEX IF NOT EXISTS idx_items_status ON items(status)")
@@ -163,6 +176,33 @@ class Database:
except sqlite3.OperationalError:
cursor.execute("ALTER TABLE items ADD COLUMN deleted_at TEXT")
# 检查并添加 folder_id 字段(兼容旧数据库)
try:
cursor.execute("SELECT folder_id FROM items LIMIT 1")
except sqlite3.OperationalError:
cursor.execute("ALTER TABLE items ADD COLUMN folder_id INTEGER DEFAULT NULL")
# 创建 folder 相关索引
cursor.execute("CREATE INDEX IF NOT EXISTS idx_items_folder ON items(folder_id)")
cursor.execute("CREATE INDEX IF NOT EXISTS idx_folders_type ON folders(type)")
cursor.execute("CREATE INDEX IF NOT EXISTS idx_folders_parent ON folders(parent_id)")
# 待办事务表关联到文本类别的items
cursor.execute("""
CREATE TABLE IF NOT EXISTS todo_events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
item_id INTEGER NOT NULL,
content TEXT NOT NULL,
remaining_days INTEGER DEFAULT 1,
is_completed INTEGER DEFAULT 0,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
FOREIGN KEY (item_id) REFERENCES items(id) ON DELETE CASCADE
)
""")
cursor.execute("CREATE INDEX IF NOT EXISTS idx_todo_events_item ON todo_events(item_id)")
cursor.execute("CREATE INDEX IF NOT EXISTS idx_todo_events_created ON todo_events(created_at)")
conn.commit()
# ============ Item 操作 ============
@@ -170,7 +210,8 @@ class Database:
def create_item(self, type: str = "text", title: str = None, content: str = None,
url: str = None, source: str = None, status: str = "pending",
priority: str = "medium", due_date: str = None, note: str = None,
tags: List[str] = None, is_starred: bool = False) -> int:
tags: List[str] = None, is_starred: bool = False,
folder_id: int = None) -> int:
"""创建新条目"""
self._ensure_init()
now = datetime.now().isoformat()
@@ -184,9 +225,9 @@ class Database:
with self.get_conn() as conn:
cursor = conn.cursor()
cursor.execute("""
INSERT INTO items (type, title, content, url, source, status, priority, due_date, note, is_starred, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (type, title, content, url, source, status, priority, due_date, note, 1 if is_starred else 0, now, now))
INSERT INTO items (type, title, content, url, source, status, priority, due_date, note, is_starred, folder_id, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (type, title, content, url, source, status, priority, due_date, note, 1 if is_starred else 0, folder_id, now, now))
item_id = cursor.lastrowid
# 添加标签
@@ -210,12 +251,14 @@ class Database:
return item
def list_items(self, type: str = None, status: str = None, tag: str = None,
keyword: str = None, starred: bool = None, sort_by: str = None,
sort_order: str = None, limit: int = 50, offset: int = 0) -> List[Dict[str, Any]]:
keyword: str = None, starred: bool = None, folder_id: int = None,
sort_by: str = None, sort_order: str = None,
limit: int = 50, offset: int = 0) -> List[Dict[str, Any]]:
"""列出条目
sort_by: created_at, updated_at
sort_order: desc, asc
folder_id: 文件夹IDNone表示不限制-1表示未分类folder_id为null
"""
with self.get_conn() as conn:
cursor = conn.cursor()
@@ -245,6 +288,15 @@ class Database:
conditions.append("i.is_starred = ?")
params.append(1 if starred else 0)
# 文件夹过滤
if folder_id is not None:
if folder_id == -1:
# -1 表示未分类folder_id 为 null
conditions.append("i.folder_id IS NULL")
else:
conditions.append("i.folder_id = ?")
params.append(folder_id)
if keyword:
conditions.append("(i.title LIKE ? OR i.content LIKE ? OR i.note LIKE ?)")
keyword_pattern = f"%{keyword}%"
@@ -290,7 +342,7 @@ class Database:
return items
def count_items(self, type: str = None, status: str = None, tag: str = None,
keyword: str = None, starred: bool = None) -> int:
keyword: str = None, starred: bool = None, folder_id: int = None) -> int:
"""计算符合条件的条目总数"""
with self.get_conn() as conn:
cursor = conn.cursor()
@@ -322,6 +374,15 @@ class Database:
keyword_pattern = f"%{keyword}%"
params.extend([keyword_pattern, keyword_pattern, keyword_pattern])
# 文件夹过滤
if folder_id is not None:
if folder_id == -1:
# 未分类folder_id为NULL
conditions.append("i.folder_id IS NULL")
else:
conditions.append("i.folder_id = ?")
params.append(folder_id)
if conditions:
query += " WHERE " + " AND ".join(conditions)
@@ -464,6 +525,116 @@ class Database:
conn.commit()
return cursor.rowcount > 0
def move_item_to_folder(self, item_id: int, folder_id: int) -> bool:
"""将条目移动到文件夹"""
with self.get_conn() as conn:
cursor = conn.cursor()
now = datetime.now().isoformat()
cursor.execute("UPDATE items SET folder_id = ?, updated_at = ? WHERE id = ?", (folder_id, now, item_id))
conn.commit()
return cursor.rowcount > 0
# ============ Folder 文件夹操作 ============
def create_folder(self, name: str, type: str, parent_id: int = None) -> int:
"""创建文件夹
Args:
name: 文件夹名称
type: 类别类型text/link/column/todo
parent_id: 父文件夹ID目前不支持嵌套预留
"""
self._ensure_init()
now = datetime.now().isoformat()
with self.get_conn() as conn:
cursor = conn.cursor()
cursor.execute("""
INSERT INTO folders (name, type, parent_id, created_at, updated_at)
VALUES (?, ?, ?, ?, ?)
""", (name, type, parent_id, now, now))
folder_id = cursor.lastrowid
conn.commit()
return folder_id
def get_folder(self, folder_id: int) -> Optional[Dict[str, Any]]:
"""获取文件夹信息"""
with self.get_conn() as conn:
cursor = conn.cursor()
cursor.execute("SELECT * FROM folders WHERE id = ?", (folder_id,))
row = cursor.fetchone()
if not row:
return None
folder = dict(row)
# 获取文件夹内条目数量
cursor.execute("SELECT COUNT(*) FROM items WHERE folder_id = ? AND is_deleted = 0", (folder_id,))
folder['item_count'] = cursor.fetchone()[0]
return folder
def list_folders(self, type: str = None) -> List[Dict[str, Any]]:
"""列出文件夹
Args:
type: 按类型过滤None表示列出所有
"""
with self.get_conn() as conn:
cursor = conn.cursor()
if type:
cursor.execute("SELECT * FROM folders WHERE type = ? ORDER BY created_at DESC", (type,))
else:
cursor.execute("SELECT * FROM folders ORDER BY created_at DESC")
folders = []
for row in cursor.fetchall():
folder = dict(row)
# 获取文件夹内条目数量
cursor.execute("SELECT COUNT(*) FROM items WHERE folder_id = ? AND is_deleted = 0", (folder['id'],))
folder['item_count'] = cursor.fetchone()[0]
folders.append(folder)
return folders
def update_folder(self, folder_id: int, name: str = None) -> bool:
"""更新文件夹"""
with self.get_conn() as conn:
cursor = conn.cursor()
now = datetime.now().isoformat()
if name:
cursor.execute("UPDATE folders SET name = ?, updated_at = ? WHERE id = ?", (name, now, folder_id))
conn.commit()
return cursor.rowcount > 0
return False
def delete_folder(self, folder_id: int, move_items_to_root: bool = True) -> bool:
"""删除文件夹
Args:
folder_id: 文件夹ID
move_items_to_root: 是否将条目移出文件夹到未分类True则移动False则一起删除
"""
with self.get_conn() as conn:
cursor = conn.cursor()
if move_items_to_root:
# 将条目移出文件夹folder_id设为null
cursor.execute("UPDATE items SET folder_id = NULL WHERE folder_id = ?", (folder_id,))
else:
# 删除文件夹内的所有条目
cursor.execute("DELETE FROM items WHERE folder_id = ?", (folder_id,))
# 删除文件夹
cursor.execute("DELETE FROM folders WHERE id = ?", (folder_id,))
conn.commit()
return cursor.rowcount > 0
def count_items_by_folder(self, folder_id: int) -> int:
"""统计文件夹内的条目数量"""
with self.get_conn() as conn:
cursor = conn.cursor()
cursor.execute("SELECT COUNT(*) FROM items WHERE folder_id = ? AND is_deleted = 0", (folder_id,))
return cursor.fetchone()[0]
# ============ Draft 草稿操作 ============
def save_draft(self, type: str = "text", title: str = None, content: str = None,
@@ -1011,6 +1182,93 @@ class Database:
# 删除
self.delete_backup(backup['name'])
# ============ Todo Events 待办事务操作 ============
def create_todo_event(self, item_id: int, content: str, remaining_days: int = 1) -> int:
"""创建待办事务"""
self._ensure_init()
now = datetime.now().isoformat()
with self.get_conn() as conn:
cursor = conn.cursor()
cursor.execute("""
INSERT INTO todo_events (item_id, content, remaining_days, is_completed, created_at, updated_at)
VALUES (?, ?, ?, 0, ?, ?)
""", (item_id, content, remaining_days, now, now))
conn.commit()
return cursor.lastrowid
def list_todo_events(self, item_id: int, limit: int = 10, offset: int = 0) -> List[Dict[str, Any]]:
"""列出待办事务(按时间倒序)"""
self._ensure_init()
with self.get_conn() as conn:
cursor = conn.cursor()
cursor.execute("""
SELECT * FROM todo_events
WHERE item_id = ?
ORDER BY created_at DESC
LIMIT ? OFFSET ?
""", (item_id, limit, offset))
rows = cursor.fetchall()
return [dict(row) for row in rows]
def count_todo_events(self, item_id: int) -> int:
"""计算待办事务总数"""
self._ensure_init()
with self.get_conn() as conn:
cursor = conn.cursor()
cursor.execute("SELECT COUNT(*) as count FROM todo_events WHERE item_id = ?", (item_id,))
return cursor.fetchone()['count']
def get_todo_event(self, event_id: int) -> Optional[Dict[str, Any]]:
"""获取单个待办事务"""
self._ensure_init()
with self.get_conn() as conn:
cursor = conn.cursor()
cursor.execute("SELECT * FROM todo_events WHERE id = ?", (event_id,))
row = cursor.fetchone()
return dict(row) if row else None
def update_todo_event(self, event_id: int, content: str = None, remaining_days: int = None, is_completed: bool = None) -> bool:
"""更新待办事务"""
self._ensure_init()
now = datetime.now().isoformat()
with self.get_conn() as conn:
cursor = conn.cursor()
# 获取当前数据
cursor.execute("SELECT * FROM todo_events WHERE id = ?", (event_id,))
row = cursor.fetchone()
if not row:
return False
# 更新字段
new_content = content if content is not None else row['content']
new_days = remaining_days if remaining_days is not None else row['remaining_days']
new_completed = 1 if is_completed is True else (0 if is_completed is False else row['is_completed'])
cursor.execute("""
UPDATE todo_events
SET content = ?, remaining_days = ?, is_completed = ?, updated_at = ?
WHERE id = ?
""", (new_content, new_days, new_completed, now, event_id))
conn.commit()
return True
def delete_todo_event(self, event_id: int) -> bool:
"""删除待办事务"""
self._ensure_init()
with self.get_conn() as conn:
cursor = conn.cursor()
cursor.execute("DELETE FROM todo_events WHERE id = ?", (event_id,))
conn.commit()
return cursor.rowcount > 0
# 全局数据库实例
db = Database()