diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 27fd5d4..9f1b02d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -20,7 +20,7 @@ val quarkusPlatformVersion: String by project dependencies { errorprone(libs.errorprone.core) errorprone(libs.nullaway) - + implementation(enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")) implementation(libs.quarkus.arc) @@ -33,6 +33,8 @@ dependencies { implementation(libs.quarkus.rest) implementation(libs.quarkus.rest.jackson) implementation(libs.quarkus.smallrye.jwt) + implementation("io.quarkiverse.amazonservices:quarkus-amazon-s3:3.12.1") + implementation("software.amazon.awssdk:url-connection-client:2.40.13") testImplementation(libs.assertj.core) testImplementation(libs.mockito.junit) diff --git a/app/data/images/spaghetti.jpg b/app/data/images/spaghetti.jpg new file mode 100644 index 0000000..ff00875 Binary files /dev/null and b/app/data/images/spaghetti.jpg differ diff --git a/app/src/main/java/dev/blaauwendraad/recipe_book/config/DemoDataSyncService.java b/app/src/main/java/dev/blaauwendraad/recipe_book/config/DemoDataSyncService.java index f423ace..c1e3a02 100644 --- a/app/src/main/java/dev/blaauwendraad/recipe_book/config/DemoDataSyncService.java +++ b/app/src/main/java/dev/blaauwendraad/recipe_book/config/DemoDataSyncService.java @@ -1,7 +1,10 @@ package dev.blaauwendraad.recipe_book.config; +import dev.blaauwendraad.recipe_book.service.ImageService; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; import jakarta.transaction.Transactional; +import java.io.File; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.flywaydb.core.Flyway; import org.jboss.logging.Logger; @@ -9,18 +12,22 @@ @ApplicationScoped public final class DemoDataSyncService { private static final Logger log = Logger.getLogger(DemoDataSyncService.class); + private ImageService imageService; private final String jdbcUrl; private final String username; private final String password; + @Inject public DemoDataSyncService( @ConfigProperty(name = "quarkus.datasource.jdbc.url") String jdbcUrl, @ConfigProperty(name = "quarkus.datasource.username") String username, - @ConfigProperty(name = "quarkus.datasource.password") String password) { + @ConfigProperty(name = "quarkus.datasource.password") String password, + ImageService imageService) { this.jdbcUrl = jdbcUrl; this.username = username; this.password = password; + this.imageService = imageService; } @Transactional @@ -46,4 +53,10 @@ void insertDemoData() { throw new RuntimeException("Demo data migration failed", e); } } + + void insertDemoImage() { + File imageFile = new File("data/images/spaghetti.jpg"); + imageService.putObject(imageFile); + log.info("Inserted demo image into Garage/S3"); + } } diff --git a/app/src/main/java/dev/blaauwendraad/recipe_book/config/ReferenceDataSyncService.java b/app/src/main/java/dev/blaauwendraad/recipe_book/config/ReferenceDataSyncService.java index 377cd4f..1eeae5d 100644 --- a/app/src/main/java/dev/blaauwendraad/recipe_book/config/ReferenceDataSyncService.java +++ b/app/src/main/java/dev/blaauwendraad/recipe_book/config/ReferenceDataSyncService.java @@ -32,6 +32,7 @@ public void syncReferenceData(@Observes StartupEvent startupEvent) { validateRoles(); if (profile.isPresent() && "dev".equals(profile.get())) { demoDataSyncService.insertDemoData(); + demoDataSyncService.insertDemoImage(); } } diff --git a/app/src/main/java/dev/blaauwendraad/recipe_book/data/model/RecipeEntity.java b/app/src/main/java/dev/blaauwendraad/recipe_book/data/model/RecipeEntity.java index 4671147..159d952 100644 --- a/app/src/main/java/dev/blaauwendraad/recipe_book/data/model/RecipeEntity.java +++ b/app/src/main/java/dev/blaauwendraad/recipe_book/data/model/RecipeEntity.java @@ -35,6 +35,10 @@ public class RecipeEntity extends PanacheEntityBase { @Nullable public String description; + @Column(name = "image_name") + @Nullable + public String imageName; + @Column(name = "num_servings") @SuppressWarnings("NullAway.Init") public Integer numServings; diff --git a/app/src/main/java/dev/blaauwendraad/recipe_book/repository/RecipeRepository.java b/app/src/main/java/dev/blaauwendraad/recipe_book/repository/RecipeRepository.java index 206ce93..8460ffd 100644 --- a/app/src/main/java/dev/blaauwendraad/recipe_book/repository/RecipeRepository.java +++ b/app/src/main/java/dev/blaauwendraad/recipe_book/repository/RecipeRepository.java @@ -34,6 +34,7 @@ public Long persistRecipeEntity( UserAccountEntity userAccountEntity, String title, @Nullable String description, + @Nullable String imageName, Integer numServings, PreparationTime preparationTime, List ingredients, @@ -49,6 +50,7 @@ public Long persistRecipeEntity( var recipeEntity = existingRecipeEntity != null ? existingRecipeEntity : new RecipeEntity(); recipeEntity.title = title; recipeEntity.description = description; + recipeEntity.imageName = imageName; recipeEntity.numServings = numServings; recipeEntity.preparationTime = preparationTime; diff --git a/app/src/main/java/dev/blaauwendraad/recipe_book/service/ImageService.java b/app/src/main/java/dev/blaauwendraad/recipe_book/service/ImageService.java new file mode 100644 index 0000000..cddfa24 --- /dev/null +++ b/app/src/main/java/dev/blaauwendraad/recipe_book/service/ImageService.java @@ -0,0 +1,38 @@ +package dev.blaauwendraad.recipe_book.service; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import java.io.File; +import software.amazon.awssdk.core.ResponseBytes; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.GetObjectRequest; +import software.amazon.awssdk.services.s3.model.GetObjectResponse; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; + +@ApplicationScoped +public class ImageService { + private static final String BUCKET_NAME = "images"; + private S3Client s3Client; + + @Inject + public ImageService(S3Client s3Client) { + this.s3Client = s3Client; + } + + public void putObject(File file) { + PutObjectRequest request = PutObjectRequest.builder() + .bucket(BUCKET_NAME) + .key(file.getName()) + .contentType("image/jpeg") + .build(); + s3Client.putObject(request, RequestBody.fromFile(file)); + } + + public byte[] getObject(String objectKey) { + GetObjectRequest getObjectRequest = + GetObjectRequest.builder().bucket(BUCKET_NAME).key(objectKey).build(); + ResponseBytes objectBytes = s3Client.getObjectAsBytes(getObjectRequest); + return objectBytes.asByteArray(); + } +} diff --git a/app/src/main/java/dev/blaauwendraad/recipe_book/service/RecipeService.java b/app/src/main/java/dev/blaauwendraad/recipe_book/service/RecipeService.java index 178f4ea..8196da6 100644 --- a/app/src/main/java/dev/blaauwendraad/recipe_book/service/RecipeService.java +++ b/app/src/main/java/dev/blaauwendraad/recipe_book/service/RecipeService.java @@ -62,6 +62,7 @@ private RecipeSummary toRecipeSummary(RecipeEntity recipeEntity) { recipeEntity.id, recipeEntity.title, recipeEntity.description, + recipeEntity.imageName, recipeEntity.numServings, recipeEntity.preparationTime, recipeEntity.author == null ? null : new Author(recipeEntity.author.id, recipeEntity.author.username)); @@ -77,6 +78,7 @@ public Recipe getRecipeById(Long recipeId) { recipeEntity.id, recipeEntity.title, recipeEntity.description, + recipeEntity.imageName, recipeEntity.numServings, recipeEntity.preparationTime, recipeEntity.author == null ? null : new Author(recipeEntity.author.id, recipeEntity.author.username), @@ -92,6 +94,7 @@ public Recipe getRecipeById(Long recipeId) { public Long createRecipe( String title, @Nullable String description, + @Nullable String imageName, Integer numServings, PreparationTime preparationTime, Long userId, @@ -106,6 +109,7 @@ public Long createRecipe( userAccountEntity, title, description, + imageName, numServings, preparationTime, ingredients, @@ -134,6 +138,7 @@ public void updateRecipe( Long recipeId, String title, @Nullable String description, + @Nullable String imageName, Integer numServings, PreparationTime preparationTime, Long userId, @@ -156,6 +161,7 @@ public void updateRecipe( existingRecipeEntity.author, title, description, + imageName, numServings, preparationTime, ingredients, diff --git a/app/src/main/java/dev/blaauwendraad/recipe_book/service/model/Recipe.java b/app/src/main/java/dev/blaauwendraad/recipe_book/service/model/Recipe.java index a041cc6..31f803a 100644 --- a/app/src/main/java/dev/blaauwendraad/recipe_book/service/model/Recipe.java +++ b/app/src/main/java/dev/blaauwendraad/recipe_book/service/model/Recipe.java @@ -7,6 +7,7 @@ public record Recipe( Long id, String title, @Nullable String description, + @Nullable String imageName, Integer numServings, PreparationTime preparationTime, @Nullable Author author, diff --git a/app/src/main/java/dev/blaauwendraad/recipe_book/service/model/RecipeSummary.java b/app/src/main/java/dev/blaauwendraad/recipe_book/service/model/RecipeSummary.java index 5fef468..de622e6 100644 --- a/app/src/main/java/dev/blaauwendraad/recipe_book/service/model/RecipeSummary.java +++ b/app/src/main/java/dev/blaauwendraad/recipe_book/service/model/RecipeSummary.java @@ -6,6 +6,7 @@ public record RecipeSummary( Long id, String title, @Nullable String description, + @Nullable String imageName, Integer numServings, PreparationTime preparationTime, @Nullable Author author) {} diff --git a/app/src/main/java/dev/blaauwendraad/recipe_book/web/RecipeResource.java b/app/src/main/java/dev/blaauwendraad/recipe_book/web/RecipeResource.java index 588abfb..4de29c5 100644 --- a/app/src/main/java/dev/blaauwendraad/recipe_book/web/RecipeResource.java +++ b/app/src/main/java/dev/blaauwendraad/recipe_book/web/RecipeResource.java @@ -1,5 +1,6 @@ package dev.blaauwendraad.recipe_book.web; +import dev.blaauwendraad.recipe_book.service.ImageService; import dev.blaauwendraad.recipe_book.service.RecipeService; import dev.blaauwendraad.recipe_book.service.model.Ingredient; import dev.blaauwendraad.recipe_book.service.model.PreparationStep; @@ -38,13 +39,15 @@ @Path("/api/recipes") public class RecipeResource { private final RecipeService recipeService; + private final ImageService imageService; @Inject JsonWebToken jwt; @Inject - public RecipeResource(RecipeService recipeService) { + public RecipeResource(RecipeService recipeService, ImageService imageService) { this.recipeService = recipeService; + this.imageService = imageService; } @GET @@ -69,6 +72,7 @@ public RecipeSummariesResponse getRecipeSummaries(@PathParam("filter") RecipeSum recipeSummary.id(), recipeSummary.title(), recipeSummary.description(), + recipeSummary.imageName(), recipeSummary.numServings(), recipeSummary.preparationTime(), recipeSummary.author() == null @@ -93,6 +97,7 @@ public RecipeResponse getRecipe(@PathParam("recipeId") Long id) { recipe.id(), recipe.title(), recipe.description(), + recipe.imageName(), recipe.numServings(), recipe.preparationTime(), recipe.author() == null @@ -107,6 +112,25 @@ public RecipeResponse getRecipe(@PathParam("recipeId") Long id) { .toList())); } + @GET + @Path("/{recipeId}/image") + @PermitAll + @Produces("image/jpeg") + public Response getRecipeImage(@PathParam("recipeId") Long id) { + Recipe recipe = recipeService.getRecipeById(id); + if (recipe == null) { + throw new NotFoundException("Recipe not found with recipeId: " + id); + } + if (recipe.imageName() == null) { + throw new NotFoundException("Recipe with recipeId: " + id + " has no image"); + } + byte[] imageFile = imageService.getObject(recipe.imageName()); + if (imageFile == null) { + throw new NotFoundException("Image not found for recipeId: " + id); + } + return Response.ok(imageFile).build(); + } + @POST @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @@ -115,6 +139,7 @@ public SaveRecipeResponseDto createRecipe(@NotNull @Valid SaveRecipeRequestDto n Long recipeId = recipeService.createRecipe( newRecipe.title(), newRecipe.description(), + newRecipe.imageName(), newRecipe.numServings(), newRecipe.preparationTime(), Long.valueOf(jwt.getName()), @@ -138,6 +163,7 @@ public SaveRecipeResponseDto updateRecipe( recipeId, updatedRecipe.title(), updatedRecipe.description(), + updatedRecipe.imageName(), updatedRecipe.numServings(), updatedRecipe.preparationTime(), Long.valueOf(jwt.getName()), diff --git a/app/src/main/java/dev/blaauwendraad/recipe_book/web/model/RecipeDto.java b/app/src/main/java/dev/blaauwendraad/recipe_book/web/model/RecipeDto.java index 6f6f360..d081134 100644 --- a/app/src/main/java/dev/blaauwendraad/recipe_book/web/model/RecipeDto.java +++ b/app/src/main/java/dev/blaauwendraad/recipe_book/web/model/RecipeDto.java @@ -8,6 +8,7 @@ public record RecipeDto( Long id, String title, @Nullable String description, + @Nullable String imageName, Integer numServings, PreparationTime preparationTime, @Nullable RecipeAuthorDto author, diff --git a/app/src/main/java/dev/blaauwendraad/recipe_book/web/model/RecipeSummaryDto.java b/app/src/main/java/dev/blaauwendraad/recipe_book/web/model/RecipeSummaryDto.java index c53a5a2..40542c0 100644 --- a/app/src/main/java/dev/blaauwendraad/recipe_book/web/model/RecipeSummaryDto.java +++ b/app/src/main/java/dev/blaauwendraad/recipe_book/web/model/RecipeSummaryDto.java @@ -7,6 +7,7 @@ public record RecipeSummaryDto( Long id, String title, @Nullable String description, + @Nullable String imageName, Integer numServings, PreparationTime preparationTime, @Nullable RecipeAuthorDto author) {} diff --git a/app/src/main/java/dev/blaauwendraad/recipe_book/web/model/SaveRecipeRequestDto.java b/app/src/main/java/dev/blaauwendraad/recipe_book/web/model/SaveRecipeRequestDto.java index 006447f..10ccd22 100644 --- a/app/src/main/java/dev/blaauwendraad/recipe_book/web/model/SaveRecipeRequestDto.java +++ b/app/src/main/java/dev/blaauwendraad/recipe_book/web/model/SaveRecipeRequestDto.java @@ -13,6 +13,7 @@ public record SaveRecipeRequestDto( @NotBlank @Size(min = 5, max = 100) String title, @Nullable @Size(max = 2000) String description, + @Nullable @Size(max = 255) String imageName, @NotNull @Positive @Max(100) Integer numServings, @NotNull PreparationTime preparationTime, @Size(min = 1, max = 50) List<@Valid IngredientDto> ingredients, diff --git a/app/src/main/resources/application-dev.properties b/app/src/main/resources/application-dev.properties index 0130de0..d1cae58 100644 --- a/app/src/main/resources/application-dev.properties +++ b/app/src/main/resources/application-dev.properties @@ -11,4 +11,13 @@ quarkus.flyway.locations=db/migration quarkus.http.cors.origins=* mp.jwt.verify.publickey.location=classpath:local-dev-keys/publicKey.pem -smallrye.jwt.sign.key.location=classpath:local-dev-keys/privateKey.pem \ No newline at end of file +smallrye.jwt.sign.key.location=classpath:local-dev-keys/privateKey.pem + +# S3 configuration for Garage for local development +quarkus.s3.endpoint-override=http://localhost:3900 +quarkus.s3.path-style-access=true +quarkus.s3.chunked-encoding=false +quarkus.s3.aws.region=garage +quarkus.s3.aws.credentials.type=static +quarkus.s3.aws.credentials.static-provider.access-key-id=${S3_ACCESS_KEY_ID} +quarkus.s3.aws.credentials.static-provider.secret-access-key=${S3_ACCESS_KEY_SECRET} \ No newline at end of file diff --git a/app/src/main/resources/db/dev-data/V2.2__insert_recipes.sql b/app/src/main/resources/db/dev-data/V2.2__insert_recipes.sql index 743b399..6f8f5bc 100644 --- a/app/src/main/resources/db/dev-data/V2.2__insert_recipes.sql +++ b/app/src/main/resources/db/dev-data/V2.2__insert_recipes.sql @@ -1,15 +1,15 @@ -- Insert demo recipes -INSERT INTO recipe (title, description, num_servings, preparation_time, author_id) -SELECT title, description, num_servings, preparation_time, ua.id -FROM (VALUES ('Spaghetti Bolognese', 'Classic Italian pasta dish.', 4, 'MIN_0_15', 'Robert'), - ('Chicken Curry', 'Spicy Indian-style curry with tender chicken.', 4, 'MIN_0_15', 'Breus'), - ('Caesar Salad', 'Classic romaine lettuce salad with creamy dressing.', 4, 'MIN_15_30', 'ChefMaster'), - ('Chocolate Chip Cookies', 'Soft and chewy homemade cookies.', 4, 'MIN_15_30', 'FoodLover'), - ('Mushroom Risotto', 'Creamy Italian rice dish with mushrooms.', 4, 'MIN_30_45', 'Breus'), - ('Fish Tacos', 'Mexican-style fish tacos with fresh salsa.', 4, 'MIN_30_45', 'ChefMaster'), - ('Greek Salad', 'Traditional Mediterranean salad with feta cheese.', 4, 'HOUR_PLUS', 'FoodLover'), - ('Beef Stir Fry', 'Quick and easy Asian-style beef dish.', 4, 'HOUR_PLUS', 'Robert'), - ('Banana Bread', 'Moist and delicious homemade bread.', 4, 'MIN_15_30', NULL), - ('Vegetable Soup', 'Healthy and warming soup with mixed vegetables.', 4, 'MIN_15_30', - NULL)) AS recipes(title, description, num_servings, preparation_time, username) +INSERT INTO recipe (title, description, image_name, num_servings, preparation_time, author_id) +SELECT title, description, image_name, num_servings, preparation_time, ua.id +FROM (VALUES ('Spaghetti Bolognese', 'Classic Italian pasta dish.', 'spaghetti.jpg', 4, 'MIN_0_15', 'Robert'), + ('Chicken Curry', 'Spicy Indian-style curry with tender chicken.', null, 4, 'MIN_0_15', 'Breus'), + ('Caesar Salad', 'Classic romaine lettuce salad with creamy dressing.', null, 4, 'MIN_15_30', 'ChefMaster'), + ('Chocolate Chip Cookies', 'Soft and chewy homemade cookies.', null, 4, 'MIN_15_30', 'FoodLover'), + ('Mushroom Risotto', 'Creamy Italian rice dish with mushrooms.', null, 4, 'MIN_30_45', 'Breus'), + ('Fish Tacos', 'Mexican-style fish tacos with fresh salsa.', null, 4, 'MIN_30_45', 'ChefMaster'), + ('Greek Salad', 'Traditional Mediterranean salad with feta cheese.', null, 4, 'HOUR_PLUS', 'FoodLover'), + ('Beef Stir Fry', 'Quick and easy Asian-style beef dish.', null, 4, 'HOUR_PLUS', 'Robert'), + ('Banana Bread', 'Moist and delicious homemade bread.', null, 4, 'MIN_15_30', NULL), + ('Vegetable Soup', 'Healthy and warming soup with mixed vegetables.', null, 4, 'MIN_15_30', + NULL)) AS recipes(title, description, image_name, num_servings, preparation_time, username) LEFT JOIN user_account ua ON ua.username = recipes.username; diff --git a/app/src/main/resources/db/migration/V1.2__create_recipe_table.sql b/app/src/main/resources/db/migration/V1.2__create_recipe_table.sql index f5110c7..989886b 100644 --- a/app/src/main/resources/db/migration/V1.2__create_recipe_table.sql +++ b/app/src/main/resources/db/migration/V1.2__create_recipe_table.sql @@ -3,6 +3,7 @@ CREATE TABLE recipe id BIGSERIAL PRIMARY KEY, title VARCHAR(255) NOT NULL, description TEXT, + image_name VARCHAR(255), num_servings INTEGER NOT NULL, preparation_time VARCHAR CHECK (preparation_time IN ('MIN_0_15', 'MIN_15_30', 'MIN_30_45', 'MIN_45_60', 'HOUR_PLUS')), diff --git a/dev_local.py b/dev_local.py index bc6a111..1f546e1 100755 --- a/dev_local.py +++ b/dev_local.py @@ -57,6 +57,32 @@ def start_docker_compose(): sys.exit(1) +def setup_garage(): + print("Starting Garage...") + try: + bucket_name = "images" + zone_name = "local" + garage_command = ["docker", "exec", "-it", "snapchef-garage-1", "/garage"] + output = subprocess.run(garage_command + ["status"], capture_output=True, text=True, check=True) + if zone_name in output.stdout: + print("Garage already set up.") + return + node_id = output.stdout.splitlines()[4].split(" ")[0] + subprocess.run(garage_command + ["layout", "assign", node_id, "-z", zone_name, "-c", "1G"], check=True) + subprocess.run(garage_command + ["layout", "apply", "--version", "1"], check=True) + subprocess.run(garage_command + ["bucket", "create", bucket_name], check=True) + output = subprocess.run(garage_command + ["key", "create", bucket_name + "-key"], capture_output=True, text=True, check=True) + key_id = output.stdout.splitlines()[3].split(":")[1].strip() + key_secret = output.stdout.splitlines()[5].split(":")[1].strip() + with open("app/.env", "w") as file: + file.write(f"S3_ACCESS_KEY_ID={key_id}\n") + file.write(f"S3_ACCESS_KEY_SECRET={key_secret}\n") + subprocess.run(garage_command + ["bucket", "allow", "--read", "--write", "--owner", bucket_name, "--key", bucket_name + "-key"], check=True) + except subprocess.CalledProcessError as exception: + print(f"Failed to setup Garage: {exception}") + sys.exit(1) + + def start_quarkus_dev(): print("Starting Quarkus Dev...") proc = subprocess.Popen( @@ -89,6 +115,7 @@ def main(): signal.signal(signal.SIGINT, cleanup) signal.signal(signal.SIGTERM, cleanup) start_docker_compose() + setup_garage() start_quarkus_dev() diff --git a/docker-compose.yml b/docker-compose.yml index ddf3f15..00c5f13 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,6 +11,16 @@ services: volumes: - pg_data:/var/lib/postgresql/data + garage: + image: dxflrs/garage:v2.1.0 + ports: + - "3900:3900" # S3 API + - "3901:3901" # RPC + volumes: + - ./garage.toml:/etc/garage.toml + - garage_meta:/var/lib/garage/meta + - garage_data:/var/lib/garage/data + quarkus: build: context: ./app @@ -21,5 +31,11 @@ services: PRIV_KEY_LOCATION: classpath:local-dev-keys/privateKey.pem ports: - "8081:8081" + volumes: + - images:/deployments/data/images + volumes: - pg_data: \ No newline at end of file + pg_data: + garage_meta: + garage_data: + images: \ No newline at end of file diff --git a/garage.toml b/garage.toml new file mode 100644 index 0000000..cc59115 --- /dev/null +++ b/garage.toml @@ -0,0 +1,16 @@ +metadata_dir = "/var/lib/garage/meta" +data_dir = "/var/lib/garage/data" +db_engine = "lmdb" +metadata_auto_snapshot_interval = "6h" + +replication_factor = 1 +compression_level = 1 + +rpc_bind_addr = "[::]:3901" +rpc_public_addr = "host.docker.internal:3901" # IP of host +rpc_secret = "46f752efc771a91a5314ec709f2bf772942393ad84e4487ef9b863cf16a3a054" + +[s3_api] +s3_region = "garage" +api_bind_addr = "[::]:3900" +root_domain = ".s3.garage" \ No newline at end of file diff --git a/ui/src/api/httpClient.ts b/ui/src/api/httpClient.ts index 90c0b3d..9e5e66c 100644 --- a/ui/src/api/httpClient.ts +++ b/ui/src/api/httpClient.ts @@ -3,7 +3,7 @@ import { useAuth } from "../auth/useAuth"; import { refreshAccessToken } from "./userAuthenticationApi"; const BACKEND_HOST_URL = import.meta.env.VITE_BACKEND_HOST_URL || "http://localhost:8081"; -const API_BASE_URL = `${BACKEND_HOST_URL}/api/`; +export const API_BASE_URL = `${BACKEND_HOST_URL}/api`; // Types for HTTP client export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH"; diff --git a/ui/src/components/RecipeSummary.vue b/ui/src/components/RecipeSummary.vue index 92fd3a5..c089bab 100644 --- a/ui/src/components/RecipeSummary.vue +++ b/ui/src/components/RecipeSummary.vue @@ -1,6 +1,7 @@