@@ -43,6 +43,7 @@ pub struct ApiState {
4343 pub no_auth : bool ,
4444 pub recent_writes : RecentWrites ,
4545 pub rate_limiter : Arc < RateLimiter > ,
46+ pub read_only : bool ,
4647}
4748
4849// ---------------------------------------------------------------------------
@@ -697,6 +698,9 @@ async fn handle_create(
697698 Json ( body) : Json < CreateBody > ,
698699) -> Result < impl IntoResponse , ApiError > {
699700 authorize ( & headers, & state, true ) ?;
701+ if state. read_only {
702+ return Err ( ApiError :: forbidden ( "Write operations disabled in read-only mode" ) ) ;
703+ }
700704 let store = state. store . lock ( ) . await ;
701705 let mut embedder = state. embedder . lock ( ) . await ;
702706 let input = CreateNoteInput {
@@ -726,6 +730,9 @@ async fn handle_append(
726730 Json ( body) : Json < AppendBody > ,
727731) -> Result < impl IntoResponse , ApiError > {
728732 authorize ( & headers, & state, true ) ?;
733+ if state. read_only {
734+ return Err ( ApiError :: forbidden ( "Write operations disabled in read-only mode" ) ) ;
735+ }
729736 let store = state. store . lock ( ) . await ;
730737 let mut embedder = state. embedder . lock ( ) . await ;
731738 let input = AppendInput {
@@ -746,6 +753,9 @@ async fn handle_edit(
746753 Json ( body) : Json < EditBody > ,
747754) -> Result < impl IntoResponse , ApiError > {
748755 authorize ( & headers, & state, true ) ?;
756+ if state. read_only {
757+ return Err ( ApiError :: forbidden ( "Write operations disabled in read-only mode" ) ) ;
758+ }
749759 let store = state. store . lock ( ) . await ;
750760 let mode = match body. mode . as_deref ( ) . unwrap_or ( "append" ) {
751761 "replace" => EditMode :: Replace ,
@@ -772,6 +782,9 @@ async fn handle_rewrite(
772782 Json ( body) : Json < RewriteBody > ,
773783) -> Result < impl IntoResponse , ApiError > {
774784 authorize ( & headers, & state, true ) ?;
785+ if state. read_only {
786+ return Err ( ApiError :: forbidden ( "Write operations disabled in read-only mode" ) ) ;
787+ }
775788 let store = state. store . lock ( ) . await ;
776789 let input = RewriteInput {
777790 file : body. file ,
@@ -792,6 +805,9 @@ async fn handle_edit_frontmatter(
792805 Json ( body) : Json < EditFrontmatterBody > ,
793806) -> Result < impl IntoResponse , ApiError > {
794807 authorize ( & headers, & state, true ) ?;
808+ if state. read_only {
809+ return Err ( ApiError :: forbidden ( "Write operations disabled in read-only mode" ) ) ;
810+ }
795811 let ops = parse_frontmatter_ops ( & body. operations ) ?;
796812 let store = state. store . lock ( ) . await ;
797813 let input = EditFrontmatterInput {
@@ -812,6 +828,9 @@ async fn handle_move(
812828 Json ( body) : Json < MoveBody > ,
813829) -> Result < impl IntoResponse , ApiError > {
814830 authorize ( & headers, & state, true ) ?;
831+ if state. read_only {
832+ return Err ( ApiError :: forbidden ( "Write operations disabled in read-only mode" ) ) ;
833+ }
815834 let store = state. store . lock ( ) . await ;
816835 let result = writer:: move_note ( & body. file , & body. new_folder , & store, & state. vault_path )
817836 . map_err ( |e| ApiError :: internal ( & format ! ( "{e:#}" ) ) ) ?;
@@ -826,6 +845,9 @@ async fn handle_archive(
826845 Json ( body) : Json < ArchiveBody > ,
827846) -> Result < impl IntoResponse , ApiError > {
828847 authorize ( & headers, & state, true ) ?;
848+ if state. read_only {
849+ return Err ( ApiError :: forbidden ( "Write operations disabled in read-only mode" ) ) ;
850+ }
829851 let store = state. store . lock ( ) . await ;
830852 let result = writer:: archive_note (
831853 & body. file ,
@@ -845,6 +867,9 @@ async fn handle_unarchive(
845867 Json ( body) : Json < UnarchiveBody > ,
846868) -> Result < impl IntoResponse , ApiError > {
847869 authorize ( & headers, & state, true ) ?;
870+ if state. read_only {
871+ return Err ( ApiError :: forbidden ( "Write operations disabled in read-only mode" ) ) ;
872+ }
848873 let store = state. store . lock ( ) . await ;
849874 let mut embedder = state. embedder . lock ( ) . await ;
850875 let result = writer:: unarchive_note ( & body. file , & store, & mut * embedder, & state. vault_path )
@@ -860,6 +885,9 @@ async fn handle_update_metadata(
860885 Json ( body) : Json < UpdateMetadataBody > ,
861886) -> Result < impl IntoResponse , ApiError > {
862887 authorize ( & headers, & state, true ) ?;
888+ if state. read_only {
889+ return Err ( ApiError :: forbidden ( "Write operations disabled in read-only mode" ) ) ;
890+ }
863891 let store = state. store . lock ( ) . await ;
864892 let input = UpdateMetadataInput {
865893 file : body. file ,
@@ -883,6 +911,9 @@ async fn handle_migrate_preview(
883911 headers : HeaderMap ,
884912) -> Result < impl IntoResponse , ApiError > {
885913 authorize ( & headers, & state, true ) ?;
914+ if state. read_only {
915+ return Err ( ApiError :: forbidden ( "Write operations disabled in read-only mode" ) ) ;
916+ }
886917 let store = state. store . lock ( ) . await ;
887918 let profile_ref = state. profile . as_ref ( ) . as_ref ( ) ;
888919 let preview = crate :: migrate:: generate_preview ( & store, & state. vault_path , profile_ref)
@@ -901,6 +932,9 @@ async fn handle_migrate_apply(
901932 Json ( body) : Json < MigrateApplyBody > ,
902933) -> Result < impl IntoResponse , ApiError > {
903934 authorize ( & headers, & state, true ) ?;
935+ if state. read_only {
936+ return Err ( ApiError :: forbidden ( "Write operations disabled in read-only mode" ) ) ;
937+ }
904938 let store = state. store . lock ( ) . await ;
905939 let preview: crate :: migrate:: MigrationPreview = serde_json:: from_value ( body. preview )
906940 . map_err ( |e| ApiError :: bad_request ( & format ! ( "Invalid preview: {e}" ) ) ) ?;
@@ -914,6 +948,9 @@ async fn handle_migrate_undo(
914948 headers : HeaderMap ,
915949) -> Result < impl IntoResponse , ApiError > {
916950 authorize ( & headers, & state, true ) ?;
951+ if state. read_only {
952+ return Err ( ApiError :: forbidden ( "Write operations disabled in read-only mode" ) ) ;
953+ }
917954 let store = state. store . lock ( ) . await ;
918955 let result = crate :: migrate:: undo_last ( & store, & state. vault_path )
919956 . map_err ( |e| ApiError :: internal ( & format ! ( "{e:#}" ) ) ) ?;
@@ -926,6 +963,9 @@ async fn handle_delete(
926963 Json ( body) : Json < DeleteBody > ,
927964) -> Result < impl IntoResponse , ApiError > {
928965 authorize ( & headers, & state, true ) ?;
966+ if state. read_only {
967+ return Err ( ApiError :: forbidden ( "Write operations disabled in read-only mode" ) ) ;
968+ }
929969 let store = state. store . lock ( ) . await ;
930970 let mode = match body. mode . as_deref ( ) . unwrap_or ( "soft" ) {
931971 "hard" => DeleteMode :: Hard ,
@@ -1013,6 +1053,7 @@ mod tests {
10131053 no_auth : false ,
10141054 recent_writes : Arc :: new ( Mutex :: new ( HashMap :: < PathBuf , SystemTime > :: new ( ) ) ) ,
10151055 rate_limiter,
1056+ read_only : false ,
10161057 }
10171058 }
10181059
0 commit comments