Add GetNotebook RPC for pulling complete notebook data
New RPC returns notebook metadata, all pages, and all strokes for a given server-side notebook ID. Enables desktop and other clients to download notebooks from the server (pull sync). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -114,6 +114,71 @@ func (s *SyncService) SyncNotebook(ctx context.Context, req *pb.SyncNotebookRequ
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *SyncService) GetNotebook(ctx context.Context, req *pb.GetNotebookRequest) (*pb.GetNotebookResponse, error) {
|
||||
userID, ok := UserIDFromContext(ctx)
|
||||
if !ok {
|
||||
return nil, status.Error(codes.Internal, "missing user context")
|
||||
}
|
||||
|
||||
var resp pb.GetNotebookResponse
|
||||
var syncedAt int64
|
||||
err := s.DB.QueryRowContext(ctx,
|
||||
"SELECT id, remote_id, title, page_size, synced_at FROM notebooks WHERE id = ? AND user_id = ?",
|
||||
req.NotebookId, userID,
|
||||
).Scan(&resp.ServerNotebookId, &resp.RemoteId, &resp.Title, &resp.PageSize, &syncedAt)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, status.Error(codes.NotFound, "notebook not found")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "query notebook: %v", err)
|
||||
}
|
||||
resp.SyncedAt = timestamppb.New(time.UnixMilli(syncedAt))
|
||||
|
||||
pageRows, err := s.DB.QueryContext(ctx,
|
||||
"SELECT id, remote_id, page_number FROM pages WHERE notebook_id = ? ORDER BY page_number",
|
||||
resp.ServerNotebookId,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "query pages: %v", err)
|
||||
}
|
||||
defer func() { _ = pageRows.Close() }()
|
||||
|
||||
for pageRows.Next() {
|
||||
var pageID, remoteID int64
|
||||
var pageNum int32
|
||||
if err := pageRows.Scan(&pageID, &remoteID, &pageNum); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "scan page: %v", err)
|
||||
}
|
||||
|
||||
pd := &pb.PageData{
|
||||
PageId: remoteID,
|
||||
PageNumber: pageNum,
|
||||
}
|
||||
|
||||
strokeRows, err := s.DB.QueryContext(ctx,
|
||||
"SELECT pen_size, color, style, point_data, stroke_order FROM strokes WHERE page_id = ? ORDER BY stroke_order",
|
||||
pageID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "query strokes: %v", err)
|
||||
}
|
||||
|
||||
for strokeRows.Next() {
|
||||
var sd pb.StrokeData
|
||||
if err := strokeRows.Scan(&sd.PenSize, &sd.Color, &sd.Style, &sd.PointData, &sd.StrokeOrder); err != nil {
|
||||
_ = strokeRows.Close()
|
||||
return nil, status.Errorf(codes.Internal, "scan stroke: %v", err)
|
||||
}
|
||||
pd.Strokes = append(pd.Strokes, &sd)
|
||||
}
|
||||
_ = strokeRows.Close()
|
||||
|
||||
resp.Pages = append(resp.Pages, pd)
|
||||
}
|
||||
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (s *SyncService) DeleteNotebook(ctx context.Context, req *pb.DeleteNotebookRequest) (*pb.DeleteNotebookResponse, error) {
|
||||
userID, ok := UserIDFromContext(ctx)
|
||||
if !ok {
|
||||
|
||||
Reference in New Issue
Block a user