@@ -5,12 +5,14 @@ use std::sync::Arc;
5
5
use axum:: routing:: get;
6
6
use axum:: { extract:: State , http:: StatusCode , routing:: post, Json , Router } ;
7
7
use goose:: conversation:: { message:: Message , Conversation } ;
8
+ use goose:: recipe:: recipe_library;
8
9
use goose:: recipe:: Recipe ;
9
10
use goose:: recipe_deeplink;
10
11
11
12
use serde:: { Deserialize , Serialize } ;
12
13
use utoipa:: ToSchema ;
13
14
15
+ use crate :: routes:: errors:: ErrorResponse ;
14
16
use crate :: routes:: recipe_utils:: get_all_recipes_manifests;
15
17
use crate :: state:: AppState ;
16
18
@@ -72,11 +74,25 @@ pub struct ScanRecipeResponse {
72
74
has_security_warnings : bool ,
73
75
}
74
76
77
+ #[ derive( Debug , Deserialize , ToSchema ) ]
78
+ pub struct SaveRecipeRequest {
79
+ recipe : Recipe ,
80
+ id : Option < String > ,
81
+ is_global : Option < bool > ,
82
+ }
83
+ #[ derive( Debug , Deserialize , ToSchema ) ]
84
+ pub struct ParseRecipeRequest {
85
+ pub content : String ,
86
+ }
87
+
88
+ #[ derive( Debug , Serialize , ToSchema ) ]
89
+ pub struct ParseRecipeResponse {
90
+ pub recipe : Recipe ,
91
+ }
92
+
75
93
#[ derive( Debug , Serialize , ToSchema ) ]
76
94
pub struct RecipeManifestResponse {
77
95
name : String ,
78
- #[ serde( rename = "isGlobal" ) ]
79
- is_global : bool ,
80
96
recipe : Recipe ,
81
97
#[ serde( rename = "lastModified" ) ]
82
98
last_modified : String ,
@@ -235,7 +251,6 @@ async fn list_recipes(
235
251
recipe_file_hash_map. insert ( id. clone ( ) , file_path) ;
236
252
RecipeManifestResponse {
237
253
name : recipe_manifest_with_path. name . clone ( ) ,
238
- is_global : recipe_manifest_with_path. is_global ,
239
254
recipe : recipe_manifest_with_path. recipe . clone ( ) ,
240
255
id : id. clone ( ) ,
241
256
last_modified : recipe_manifest_with_path. last_modified . clone ( ) ,
@@ -278,6 +293,57 @@ async fn delete_recipe(
278
293
StatusCode :: NO_CONTENT
279
294
}
280
295
296
+ #[ utoipa:: path(
297
+ post,
298
+ path = "/recipes/save" ,
299
+ request_body = SaveRecipeRequest ,
300
+ responses(
301
+ ( status = 204 , description = "Recipe saved to file successfully" ) ,
302
+ ( status = 401 , description = "Unauthorized - Invalid or missing API key" ) ,
303
+ ( status = 500 , description = "Internal server error" , body = ErrorResponse )
304
+ ) ,
305
+ tag = "Recipe Management"
306
+ ) ]
307
+ async fn save_recipe (
308
+ State ( state) : State < Arc < AppState > > ,
309
+ Json ( request) : Json < SaveRecipeRequest > ,
310
+ ) -> Result < StatusCode , ErrorResponse > {
311
+ let file_path = match request. id {
312
+ Some ( id) => state. recipe_file_hash_map . lock ( ) . await . get ( & id) . cloned ( ) ,
313
+ None => None ,
314
+ } ;
315
+
316
+ match recipe_library:: save_recipe_to_file ( request. recipe , request. is_global , file_path) {
317
+ Ok ( _) => Ok ( StatusCode :: NO_CONTENT ) ,
318
+ Err ( e) => Err ( ErrorResponse {
319
+ message : e. to_string ( ) ,
320
+ status : StatusCode :: INTERNAL_SERVER_ERROR ,
321
+ } ) ,
322
+ }
323
+ }
324
+
325
+ #[ utoipa:: path(
326
+ post,
327
+ path = "/recipes/parse" ,
328
+ request_body = ParseRecipeRequest ,
329
+ responses(
330
+ ( status = 200 , description = "Recipe parsed successfully" , body = ParseRecipeResponse ) ,
331
+ ( status = 400 , description = "Bad request - Invalid recipe format" , body = ErrorResponse ) ,
332
+ ( status = 500 , description = "Internal server error" , body = ErrorResponse )
333
+ ) ,
334
+ tag = "Recipe Management"
335
+ ) ]
336
+ async fn parse_recipe (
337
+ Json ( request) : Json < ParseRecipeRequest > ,
338
+ ) -> Result < Json < ParseRecipeResponse > , ErrorResponse > {
339
+ let recipe = Recipe :: from_content ( & request. content ) . map_err ( |e| ErrorResponse {
340
+ message : format ! ( "Invalid recipe format: {}" , e) ,
341
+ status : StatusCode :: BAD_REQUEST ,
342
+ } ) ?;
343
+
344
+ Ok ( Json ( ParseRecipeResponse { recipe } ) )
345
+ }
346
+
281
347
pub fn routes ( state : Arc < AppState > ) -> Router {
282
348
Router :: new ( )
283
349
. route ( "/recipes/create" , post ( create_recipe) )
@@ -286,6 +352,8 @@ pub fn routes(state: Arc<AppState>) -> Router {
286
352
. route ( "/recipes/scan" , post ( scan_recipe) )
287
353
. route ( "/recipes/list" , get ( list_recipes) )
288
354
. route ( "/recipes/delete" , post ( delete_recipe) )
355
+ . route ( "/recipes/save" , post ( save_recipe) )
356
+ . route ( "/recipes/parse" , post ( parse_recipe) )
289
357
. with_state ( state)
290
358
}
291
359
0 commit comments