from fastapi import FastAPI, HTTPException from fastapi.responses import FileResponse from fastapi.middleware.cors import CORSMiddleware import os, mimetypes, hashlib, re from collections import defaultdict VAULT = "vault" app = FastAPI() app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]) @app.get("/") def root(): return { "status": "vault-online", "browse": "/vault", "read": "/vault/{path}", "raw": "/raw/{path}", "tags": "/tags", "graph": "/graph" } @app.get("/vault") @app.get("/vault/{path:path}") def vault(path: str = ""): full = os.path.join(VAULT, path) if not os.path.exists(full): raise HTTPException(404) if os.path.isdir(full): return { "path": path, "entries": [{ "name": f, "type": "dir" if os.path.isdir(os.path.join(full, f)) else "file", "size": os.path.getsize(os.path.join(full, f)) if os.path.isfile(os.path.join(full, f)) else None, "modified": os.path.getmtime(os.path.join(full, f)), "hash": hashlib.md5(f.encode()).hexdigest()[:8] } for f in sorted(os.listdir(full))] } if os.path.isfile(full): try: with open(full, "r", encoding="utf-8") as f: return {"path": path, "content": f.read()} except: raise HTTPException(500) raise HTTPException(400) @app.get("/raw/{path:path}") def raw(path: str): full = os.path.join(VAULT, path) if not os.path.isfile(full): raise HTTPException(404) mt, _ = mimetypes.guess_type(full) return FileResponse(full, media_type=mt or "application/octet-stream") @app.get("/tags") def extract_tags(): tag_map = defaultdict(list) for root, _, files in os.walk(VAULT): for f in files: if f.endswith(".md"): path = os.path.join(root, f) try: with open(path, "r", encoding="utf-8") as file: content = file.read() tags = re.findall(r'(?