131 lines
4.7 KiB
Python
131 lines
4.7 KiB
Python
from fastapi import FastAPI, HTTPException, Request
|
|
from fastapi.responses import HTMLResponse
|
|
from fastapi.staticfiles import StaticFiles
|
|
from fastapi.templating import Jinja2Templates
|
|
from pydantic import BaseModel
|
|
from typing import Optional
|
|
import os
|
|
|
|
from .database import init_db, get_db
|
|
from . import scheduler as sched
|
|
|
|
app = FastAPI(title="Memo")
|
|
|
|
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
|
app.mount("/static", StaticFiles(directory=os.path.join(BASE_DIR, "static")), name="static")
|
|
templates = Jinja2Templates(directory=os.path.join(BASE_DIR, "templates"))
|
|
|
|
@app.on_event("startup")
|
|
def startup():
|
|
init_db()
|
|
sched.start()
|
|
|
|
@app.on_event("shutdown")
|
|
def shutdown():
|
|
sched.stop()
|
|
|
|
@app.get("/", response_class=HTMLResponse)
|
|
def index(request: Request):
|
|
return templates.TemplateResponse("index.html", {"request": request})
|
|
|
|
|
|
# --- Memo models ---
|
|
|
|
class MemoCreate(BaseModel):
|
|
title: str
|
|
description: Optional[str] = ""
|
|
reminder_time: str = "09:00"
|
|
|
|
class MemoUpdate(BaseModel):
|
|
title: Optional[str] = None
|
|
description: Optional[str] = None
|
|
reminder_time: Optional[str] = None
|
|
|
|
|
|
# --- Memo routes ---
|
|
|
|
@app.get("/api/memos")
|
|
def list_memos(done: Optional[str] = None):
|
|
with get_db() as conn:
|
|
if done == "true":
|
|
rows = conn.execute("SELECT * FROM memos WHERE is_done = 1 ORDER BY done_at DESC").fetchall()
|
|
elif done == "false":
|
|
rows = conn.execute("SELECT * FROM memos WHERE is_done = 0 ORDER BY reminder_time, created_at").fetchall()
|
|
else:
|
|
rows = conn.execute("SELECT * FROM memos ORDER BY is_done, reminder_time, created_at").fetchall()
|
|
return [dict(r) for r in rows]
|
|
|
|
@app.post("/api/memos", status_code=201)
|
|
def create_memo(memo: MemoCreate):
|
|
with get_db() as conn:
|
|
cur = conn.execute(
|
|
"INSERT INTO memos (title, description, reminder_time) VALUES (?, ?, ?)",
|
|
(memo.title.strip(), memo.description.strip(), memo.reminder_time)
|
|
)
|
|
row = conn.execute("SELECT * FROM memos WHERE id = ?", (cur.lastrowid,)).fetchone()
|
|
return dict(row)
|
|
|
|
@app.put("/api/memos/{memo_id}")
|
|
def update_memo(memo_id: int, memo: MemoUpdate):
|
|
with get_db() as conn:
|
|
row = conn.execute("SELECT * FROM memos WHERE id = ?", (memo_id,)).fetchone()
|
|
if not row:
|
|
raise HTTPException(status_code=404, detail="Memo introuvable")
|
|
fields, values = [], []
|
|
if memo.title is not None:
|
|
fields.append("title = ?"); values.append(memo.title.strip())
|
|
if memo.description is not None:
|
|
fields.append("description = ?"); values.append(memo.description.strip())
|
|
if memo.reminder_time is not None:
|
|
fields.append("reminder_time = ?"); values.append(memo.reminder_time)
|
|
if not fields:
|
|
return dict(row)
|
|
values.append(memo_id)
|
|
conn.execute(f"UPDATE memos SET {', '.join(fields)} WHERE id = ?", values)
|
|
return dict(conn.execute("SELECT * FROM memos WHERE id = ?", (memo_id,)).fetchone())
|
|
|
|
@app.post("/api/memos/{memo_id}/done")
|
|
def mark_done(memo_id: int):
|
|
with get_db() as conn:
|
|
row = conn.execute("SELECT * FROM memos WHERE id = ?", (memo_id,)).fetchone()
|
|
if not row:
|
|
raise HTTPException(status_code=404, detail="Memo introuvable")
|
|
conn.execute(
|
|
"UPDATE memos SET is_done = 1, done_at = datetime('now', 'localtime') WHERE id = ?",
|
|
(memo_id,)
|
|
)
|
|
return dict(conn.execute("SELECT * FROM memos WHERE id = ?", (memo_id,)).fetchone())
|
|
|
|
@app.post("/api/memos/{memo_id}/undone")
|
|
def mark_undone(memo_id: int):
|
|
with get_db() as conn:
|
|
row = conn.execute("SELECT * FROM memos WHERE id = ?", (memo_id,)).fetchone()
|
|
if not row:
|
|
raise HTTPException(status_code=404, detail="Memo introuvable")
|
|
conn.execute("UPDATE memos SET is_done = 0, done_at = NULL WHERE id = ?", (memo_id,))
|
|
return dict(conn.execute("SELECT * FROM memos WHERE id = ?", (memo_id,)).fetchone())
|
|
|
|
@app.delete("/api/memos/{memo_id}", status_code=204)
|
|
def delete_memo(memo_id: int):
|
|
with get_db() as conn:
|
|
conn.execute("DELETE FROM memos WHERE id = ?", (memo_id,))
|
|
|
|
|
|
# --- Settings ---
|
|
|
|
class SettingsUpdate(BaseModel):
|
|
discord_webhook: Optional[str] = None
|
|
|
|
@app.get("/api/settings")
|
|
def get_settings():
|
|
with get_db() as conn:
|
|
rows = conn.execute("SELECT key, value FROM settings").fetchall()
|
|
return {r["key"]: r["value"] for r in rows}
|
|
|
|
@app.post("/api/settings")
|
|
def update_settings(data: SettingsUpdate):
|
|
with get_db() as conn:
|
|
if data.discord_webhook is not None:
|
|
conn.execute("UPDATE settings SET value = ? WHERE key = 'discord_webhook'", (data.discord_webhook,))
|
|
return {"ok": True}
|