-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
130 lines (98 loc) · 4.77 KB
/
main.py
File metadata and controls
130 lines (98 loc) · 4.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
from fastapi import FastAPI
import markdown
from fastapi import Response
from pydantic import BaseModel
from spellchecker import SpellChecker
app = FastAPI()
@app.get("/")
def hello():
return {"message": "Hello, World!"}
@app.get("/about")
def about():
return {"name": "Markdown Notes API", "version": "0.1"}
from fastapi import UploadFile, File, HTTPException
from pathlib import Path
# 笔记存放的目录
NOTES_DIR = Path("notes")
NOTES_DIR.mkdir(exist_ok=True) # 程序启动时确保目录存在
spell = SpellChecker() # 初始化拼写检查器
@app.post("/notes")
async def upload_note(file: UploadFile = File(...)):
"""上传一个 markdown 文件并保存。"""
# TODO 1: 校验文件名以 .md 结尾
# 如果不是 .md, 抛出 HTTPException(status_code=400, detail="...")
# 提示: file.filename 是上传文件的原始名字
if not file.filename.endswith(".md"):
raise HTTPException(status_code=400, detail="Only .md files are allowed")
# TODO 2: 读取上传文件的内容
# 提示: content = await file.read() ← 注意 await, 后面会解释
# 读出来的 content 是 bytes (字节), 不是 str
content = await file.read()
# TODO 3: 把内容写到 NOTES_DIR / file.filename
# 提示: 用 Path 对象的 write_bytes 方法
save_path = NOTES_DIR / file.filename
save_path.write_bytes(content)
# TODO 4: 返回一个表示成功的 JSON
# 例如: {"filename": "xxx.md", "size": 1234, "message": "Uploaded"}
return {"filename": file.filename, "size": len(content), "message": "Uploaded"}
@app.get("/notes")
async def list_notes():
"""列出所有已保存的笔记。"""
# TODO 1: 遍历 NOTES_DIR 下所有 .md 文件
# 提示: NOTES_DIR.glob("*.md") 返回一个生成器, 每个元素是 Path 对象
# 比如: for p in NOTES_DIR.glob("*.md"): print(p.name, p.stat().st_size)
# TODO 2: 构造每个笔记的信息字典 {"filename": ..., "size": ...}
# 用列表推导式一行搞定
notes = [{"filename": p.name, "size": p.stat().st_size} for p in NOTES_DIR.glob("*.md")]
# TODO 3: 返回 {"count": ..., "notes": [...]}
return {"count": len(notes), "notes": notes}
@app.get("/notes/{filename}/html")
async def render_note_html(filename: str):
"""读取指定笔记, 转成 HTML 返回。"""
# TODO 1: 校验 filename, 防止路径遍历攻击
# 检查 filename 里不能含 "/", "\\", ".."
# 如果含, raise HTTPException(400, ...)
if "/" in filename or "\\" in filename or ".." in filename:
raise HTTPException(status_code=400, detail="Invalid filename")
# TODO 2: 拼接完整路径, 并校验它真的在 NOTES_DIR 下
# file_path = (NOTES_DIR / filename).resolve()
# 如果 not file_path.is_relative_to(NOTES_DIR.resolve()): 报 400
file_path = (NOTES_DIR / filename).resolve()
if not file_path.is_relative_to(NOTES_DIR.resolve()):
raise HTTPException(status_code=400, detail="Invalid filename")
# TODO 3: 检查文件是否存在
# 不存在 raise HTTPException(404, "Note not found")
if not file_path.exists():
raise HTTPException(status_code=404, detail="Note not found")
# TODO 4: 读取文件内容 (用 read_text, 指定 encoding="utf-8")
md_content = file_path.read_text(encoding="utf-8")
# TODO 5: 转成 HTML
html = markdown.markdown(md_content)
# TODO 6: 用 Response 返回, media_type="text/html"
return Response(content=html, media_type="text/html")
class GrammarCheckRequest(BaseModel):
text: str
@app.post("/grammar-check")
async def check_grammar(request: GrammarCheckRequest):
"""检查文本拼写, 返回错词和建议。"""
# TODO 1: 把 request.text 拆成词列表
# 提示: 简单粗暴用 .split() 按空白拆分就行
words = request.text.split()
# TODO 2: 用 spell.unknown(words) 找出所有拼错的词
# 注意返回的是 set, 不是 list
misspelled = spell.unknown(words)
# TODO 3: 对每个错词, 构造 {"word": ..., "suggestion": ..., "candidates": ...}
# 用列表推导式
# 注意 spell.candidates(word) 返回 set, 需要 list(...) 转一下才能放进 JSON
# 还要注意 spell.candidates 在某些罕见情况可能返回 None, 加个 if 防御一下
errors = []
for word in misspelled:
candidates = spell.candidates(word)
if candidates is None:
candidates = []
else:
candidates = list(candidates)
suggestion = spell.correction(word) # 最佳建议
errors.append({"word": word, "suggestion": suggestion, "candidates": candidates})
# TODO 4: 返回 {"text": ..., "misspelled_count": ..., "errors": [...]}
return {"text": request.text, "misspelled_count": len(misspelled), "errors": errors}