11import re
22import unicodedata
33from fastapi import APIRouter , HTTPException , Depends , Query
4- from app .schemas import PageCreate , PageUpdate , PageResponse , PageListResponse
4+ from app .schemas import PageCreate , PageUpdate , PageResponse , PageListResponse , PageMoveRequest
55from app .auth import get_current_user
66from app .database import get_db
77from app .services .search import rebuild_search_index , remove_from_search_index
8+ from app .services .wikilink import parse_and_update_backlinks
89from app .routers .activity import log_activity
10+ from app .routers .versions import save_version
911
1012router = APIRouter (prefix = "/api/pages" , tags = ["pages" ])
1113
@@ -90,6 +92,18 @@ def build_tree(parent_id):
9092 return build_tree (None )
9193
9294
95+ @router .get ("/graph" )
96+ async def page_graph (user = Depends (get_current_user )):
97+ db = await get_db ()
98+ pages = await db .execute_fetchall ("SELECT id, slug, title FROM pages" )
99+ nodes = [{"id" : p ["id" ], "slug" : p ["slug" ], "title" : p ["title" ]} for p in pages ]
100+
101+ backlinks = await db .execute_fetchall ("SELECT source_page_id, target_page_id FROM backlinks" )
102+ links = [{"source" : b ["source_page_id" ], "target" : b ["target_page_id" ]} for b in backlinks ]
103+
104+ return {"nodes" : nodes , "links" : links }
105+
106+
93107@router .post ("" , response_model = PageResponse , status_code = 201 )
94108async def create_page (body : PageCreate , user = Depends (get_current_user )):
95109 db = await get_db ()
@@ -113,6 +127,8 @@ async def create_page(body: PageCreate, user=Depends(get_current_user)):
113127
114128 # Update search index
115129 await rebuild_search_index (db , page_id , body .title , content )
130+ # Parse wikilinks → update backlinks
131+ await parse_and_update_backlinks (db , page_id , content )
116132 # Log activity
117133 await log_activity (db , user ["id" ], "created" , "page" , page_id , {"title" : body .title , "slug" : slug })
118134 await db .commit ()
@@ -149,9 +165,12 @@ async def update_page(slug: str, body: PageUpdate, user=Depends(get_current_user
149165 current = dict (rows [0 ])
150166 title = body .title if body .title is not None else current ["title" ]
151167 content = body .content_md if body .content_md is not None else current ["content_md" ]
152- parent_id = body .parent_id if body . parent_id is not None else current ["parent_id" ]
168+ parent_id = body .parent_id if " parent_id" in body . model_fields_set else current ["parent_id" ]
153169 sort_order = body .sort_order if body .sort_order is not None else current ["sort_order" ]
154170
171+ # Save current state as a version before updating
172+ await save_version (db , current ["id" ], current ["title" ], current ["content_md" ], user ["id" ])
173+
155174 await db .execute (
156175 """UPDATE pages SET title = ?, content_md = ?, parent_id = ?, sort_order = ?,
157176 updated_at = CURRENT_TIMESTAMP WHERE slug = ?""" ,
@@ -160,6 +179,8 @@ async def update_page(slug: str, body: PageUpdate, user=Depends(get_current_user
160179
161180 # Update search index
162181 await rebuild_search_index (db , current ["id" ], title , content )
182+ # Parse wikilinks → update backlinks
183+ await parse_and_update_backlinks (db , current ["id" ], content )
163184 # Log activity
164185 await log_activity (db , user ["id" ], "updated" , "page" , current ["id" ], {"title" : title , "slug" : slug })
165186 await db .commit ()
@@ -168,6 +189,63 @@ async def update_page(slug: str, body: PageUpdate, user=Depends(get_current_user
168189 return dict (rows [0 ])
169190
170191
192+ @router .get ("/{slug}/children" )
193+ async def get_children (slug : str , user = Depends (get_current_user )):
194+ db = await get_db ()
195+ rows = await db .execute_fetchall ("SELECT id FROM pages WHERE slug = ?" , (slug ,))
196+ if not rows :
197+ raise HTTPException (status_code = 404 , detail = "Page not found" )
198+ page_id = rows [0 ]["id" ]
199+ children = await db .execute_fetchall (
200+ "SELECT * FROM pages WHERE parent_id = ? ORDER BY sort_order, title" ,
201+ (page_id ,),
202+ )
203+ return [dict (c ) for c in children ]
204+
205+
206+ @router .get ("/{slug}/backlinks" )
207+ async def get_backlinks (slug : str , user = Depends (get_current_user )):
208+ db = await get_db ()
209+ rows = await db .execute_fetchall ("SELECT id FROM pages WHERE slug = ?" , (slug ,))
210+ if not rows :
211+ raise HTTPException (status_code = 404 , detail = "Page not found" )
212+ page_id = rows [0 ]["id" ]
213+ backlinks = await db .execute_fetchall (
214+ """SELECT p.id, p.slug, p.title
215+ FROM backlinks b
216+ JOIN pages p ON p.id = b.source_page_id
217+ WHERE b.target_page_id = ?
218+ ORDER BY p.title""" ,
219+ (page_id ,),
220+ )
221+ return [dict (b ) for b in backlinks ]
222+
223+
224+ @router .patch ("/{slug}/move" )
225+ async def move_page (slug : str , body : PageMoveRequest , user = Depends (get_current_user )):
226+ db = await get_db ()
227+ rows = await db .execute_fetchall ("SELECT id FROM pages WHERE slug = ?" , (slug ,))
228+ if not rows :
229+ raise HTTPException (status_code = 404 , detail = "Page not found" )
230+
231+ updates = []
232+ params = []
233+ if "parent_id" in body .model_fields_set :
234+ updates .append ("parent_id = ?" )
235+ params .append (body .parent_id )
236+ if "sort_order" in body .model_fields_set :
237+ updates .append ("sort_order = ?" )
238+ params .append (body .sort_order )
239+
240+ if not updates :
241+ raise HTTPException (status_code = 400 , detail = "No fields to update" )
242+
243+ params .append (slug )
244+ await db .execute (f"UPDATE pages SET { ', ' .join (updates )} WHERE slug = ?" , params )
245+ await db .commit ()
246+ return {"ok" : True }
247+
248+
171249@router .delete ("/{slug}" )
172250async def delete_page (slug : str , user = Depends (get_current_user )):
173251 db = await get_db ()
0 commit comments