diff --git a/compose.yaml b/compose.yaml index 4a81257..ef50db8 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,4 +1,36 @@ services: + user-db: + build: + context: ./docker/user-db + container_name: bytebite-user-db + environment: + POSTGRES_DB: ${USER_DB_NAME:-bytebite_user} + POSTGRES_USER: ${USER_DB_USER:-bytebite_user} + POSTGRES_PASSWORD: ${USER_DB_PASSWORD:-bytebite_user_password} + volumes: + - user-db-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] + interval: 10s + timeout: 5s + retries: 5 + + grocery-db: + build: + context: ./docker/grocery-db + container_name: bytebite-grocery-db + environment: + POSTGRES_DB: ${GROCERY_DB_NAME:-bytebite_grocery} + POSTGRES_USER: ${GROCERY_DB_USER:-bytebite_grocery} + POSTGRES_PASSWORD: ${GROCERY_DB_PASSWORD:-bytebite_grocery_password} + volumes: + - grocery-db-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] + interval: 10s + timeout: 5s + retries: 5 + gen-ai: # `up --build` builds this locally; on the VM `pull` fetches it from GHCR. image: ${REGISTRY:-ghcr.io/aet-devops26/team-bytebite}/gen-ai:${IMAGE_TAG:-latest} @@ -10,17 +42,57 @@ services: environment: OPENAI_API_KEY: ${OPENAI_API_KEY} - server: - image: ${REGISTRY:-ghcr.io/aet-devops26/team-bytebite}/server:${IMAGE_TAG:-latest} + + user-service: + build: + context: ./user-service + container_name: bytebite-user-service + expose: + - "8080" + environment: + SPRING_APPLICATION_NAME: user-service + SPRING_DATASOURCE_URL: jdbc:postgresql://user-db:5432/${USER_DB_NAME:-bytebite_user} + SPRING_DATASOURCE_USERNAME: ${USER_DB_USER:-bytebite_user} + SPRING_DATASOURCE_PASSWORD: ${USER_DB_PASSWORD:-bytebite_user_password} + depends_on: + user-db: + condition: service_healthy + + grocery-service: build: - context: ./server - container_name: bytebite-server + context: ./grocery-service + container_name: bytebite-grocery-service + expose: + - "8080" + environment: + SPRING_APPLICATION_NAME: grocery-service + GENAI_BASE_URL: http://gen-ai:8000 + SPRING_DATASOURCE_URL: jdbc:postgresql://grocery-db:5432/${GROCERY_DB_NAME:-bytebite_grocery} + SPRING_DATASOURCE_USERNAME: ${GROCERY_DB_USER:-bytebite_grocery} + SPRING_DATASOURCE_PASSWORD: ${GROCERY_DB_PASSWORD:-bytebite_grocery_password} + depends_on: + grocery-db: + condition: service_healthy + gen-ai: + condition: service_started + + api-gateway: + build: + context: ./api-gateway + container_name: bytebite-api-gateway ports: - "8080:8080" environment: - GENAI_BASE_URL: http://gen-ai:8000 + SPRING_APPLICATION_NAME: api-gateway + USER_SERVICE_BASE_URL: http://user-service:8080 + GROCERY_SERVICE_BASE_URL: http://grocery-service:8080 depends_on: - - gen-ai + user-service: + condition: service_started + grocery-service: + condition: service_started + gen-ai: + condition: service_started client: image: ${REGISTRY:-ghcr.io/aet-devops26/team-bytebite}/client:${IMAGE_TAG:-latest} @@ -30,4 +102,8 @@ services: ports: - "8081:80" depends_on: - - server \ No newline at end of file + - api-gateway + +volumes: + user-db-data: + grocery-db-data: diff --git a/docker/grocery-db/Dockerfile b/docker/grocery-db/Dockerfile new file mode 100644 index 0000000..f0f7827 --- /dev/null +++ b/docker/grocery-db/Dockerfile @@ -0,0 +1,3 @@ +FROM postgres:16-alpine + +COPY init.sql /docker-entrypoint-initdb.d/01-init.sql diff --git a/docker/grocery-db/init.sql b/docker/grocery-db/init.sql new file mode 100644 index 0000000..9316198 --- /dev/null +++ b/docker/grocery-db/init.sql @@ -0,0 +1,77 @@ +CREATE EXTENSION IF NOT EXISTS pgcrypto; + +CREATE TYPE grocery_category AS ENUM ( + 'PRODUCE', + 'DAIRY', + 'MEAT', + 'SEAFOOD', + 'BAKERY', + 'PANTRY', + 'FROZEN', + 'BEVERAGES', + 'SPICES', + 'OTHER' +); + +CREATE TABLE IF NOT EXISTS recipes ( + recipe_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + name VARCHAR(255) NOT NULL, + user_id UUID NOT NULL +); + +CREATE TABLE IF NOT EXISTS grocery_lists ( + grocery_list_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + name VARCHAR(255) NOT NULL, + outdated BOOLEAN NOT NULL DEFAULT FALSE, + user_id UUID NOT NULL +); + +CREATE TABLE IF NOT EXISTS grocery_list_recipes ( + grocery_list_id UUID NOT NULL, + recipe_id UUID NOT NULL, + PRIMARY KEY (grocery_list_id, recipe_id), + CONSTRAINT fk_grocery_list_recipes_grocery_list + FOREIGN KEY (grocery_list_id) + REFERENCES grocery_lists (grocery_list_id) + ON DELETE CASCADE, + CONSTRAINT fk_grocery_list_recipes_recipe + FOREIGN KEY (recipe_id) + REFERENCES recipes (recipe_id) + ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS grocery_items ( + item_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + name VARCHAR(255) NOT NULL, + quantity DOUBLE PRECISION NOT NULL, + unit VARCHAR(50) NOT NULL, + category grocery_category NOT NULL DEFAULT 'OTHER', + is_purchased BOOLEAN NOT NULL DEFAULT FALSE, + recipe_id UUID, + grocery_list_id UUID, + CONSTRAINT fk_grocery_items_recipe + FOREIGN KEY (recipe_id) + REFERENCES recipes (recipe_id) + ON DELETE CASCADE, + CONSTRAINT fk_grocery_items_grocery_list + FOREIGN KEY (grocery_list_id) + REFERENCES grocery_lists (grocery_list_id) + ON DELETE CASCADE, + CONSTRAINT chk_grocery_items_owner + CHECK (recipe_id IS NOT NULL OR grocery_list_id IS NOT NULL) +); + +CREATE INDEX IF NOT EXISTS idx_recipes_user_id + ON recipes (user_id); + +CREATE INDEX IF NOT EXISTS idx_grocery_lists_user_id + ON grocery_lists (user_id); + +CREATE INDEX IF NOT EXISTS idx_grocery_list_recipes_recipe_id + ON grocery_list_recipes (recipe_id); + +CREATE INDEX IF NOT EXISTS idx_grocery_items_recipe_id + ON grocery_items (recipe_id); + +CREATE INDEX IF NOT EXISTS idx_grocery_items_grocery_list_id + ON grocery_items (grocery_list_id); diff --git a/docker/user-db/Dockerfile b/docker/user-db/Dockerfile new file mode 100644 index 0000000..f0f7827 --- /dev/null +++ b/docker/user-db/Dockerfile @@ -0,0 +1,3 @@ +FROM postgres:16-alpine + +COPY init.sql /docker-entrypoint-initdb.d/01-init.sql diff --git a/docker/user-db/init.sql b/docker/user-db/init.sql new file mode 100644 index 0000000..201fe84 --- /dev/null +++ b/docker/user-db/init.sql @@ -0,0 +1,10 @@ +CREATE EXTENSION IF NOT EXISTS pgcrypto; + +CREATE TABLE IF NOT EXISTS users ( + user_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + name VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL UNIQUE, + password_hash VARCHAR(255) NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP +); + diff --git a/documentation/database/DBSchemaDiagram.drawio b/documentation/database/DBSchemaDiagram.drawio new file mode 100644 index 0000000..a478b80 --- /dev/null +++ b/documentation/database/DBSchemaDiagram.drawio @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documentation/database/SchemaDiagram.jpg b/documentation/database/SchemaDiagram.jpg new file mode 100644 index 0000000..168e529 Binary files /dev/null and b/documentation/database/SchemaDiagram.jpg differ