added kubernetes and removed keycloak from prod compose
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -46,6 +46,3 @@ frontend/dist/
|
|||||||
# OS
|
# OS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
# Other
|
|
||||||
*.sql
|
|
||||||
|
|||||||
392
db/periodic-table.sql
Normal file
392
db/periodic-table.sql
Normal file
@@ -0,0 +1,392 @@
|
|||||||
|
-- TAGS: Global (Shared across all projects)
|
||||||
|
CREATE TABLE tags (
|
||||||
|
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
tag_code VARCHAR(10) NOT NULL UNIQUE,
|
||||||
|
tag_description TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- GROUPS: Global (Shared across all projects)
|
||||||
|
CREATE TABLE groups (
|
||||||
|
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
group_name TEXT NOT NULL UNIQUE,
|
||||||
|
hex_color VARCHAR(7) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- PRIORITIES: Global
|
||||||
|
CREATE TABLE priorities (
|
||||||
|
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
priority_name TEXT NOT NULL UNIQUE,
|
||||||
|
priority_num INT NOT NULL DEFAULT 0
|
||||||
|
);
|
||||||
|
|
||||||
|
-- VALIDATION STATUSES: Global (Approved, Rejected, etc.)
|
||||||
|
CREATE TABLE validation_statuses (
|
||||||
|
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
status_name TEXT NOT NULL UNIQUE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- [NEW] REQUIREMENT STATUSES: Global (Draft, Regular, Deprecated)
|
||||||
|
-- This allows you to manage lifecycle states dynamically.
|
||||||
|
CREATE TABLE requirement_statuses (
|
||||||
|
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
status_code VARCHAR(20) NOT NULL UNIQUE, -- e.g. 'DRAFT'
|
||||||
|
status_name TEXT NOT NULL, -- e.g. 'Draft'
|
||||||
|
description TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Seed initial statuses so the FKs work immediately
|
||||||
|
INSERT INTO requirement_statuses (status_code, status_name, description) VALUES
|
||||||
|
('DRAFT', 'Draft', 'Initial version, not ready for review'),
|
||||||
|
('REGULAR', 'Regular', 'Active requirement');
|
||||||
|
|
||||||
|
-- ROLES: System-wide roles (e.g., Admin, User)
|
||||||
|
CREATE TABLE roles (
|
||||||
|
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
role_name TEXT NOT NULL UNIQUE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- USERS: System-wide users
|
||||||
|
CREATE TABLE users (
|
||||||
|
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
sub TEXT NOT NULL UNIQUE, -- Keycloak Subject ID
|
||||||
|
username TEXT NOT NULL,
|
||||||
|
full_name TEXT,
|
||||||
|
role_id INT NOT NULL,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
CONSTRAINT fk_users_role FOREIGN KEY (role_id) REFERENCES roles (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- PROJECTS: The container for requirements
|
||||||
|
CREATE TABLE projects (
|
||||||
|
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
project_name TEXT NOT NULL,
|
||||||
|
project_desc TEXT,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- PROJECT_MEMBERS: Controls Access (Many-to-Many)
|
||||||
|
CREATE TABLE project_members (
|
||||||
|
project_id INT NOT NULL,
|
||||||
|
user_id INT NOT NULL,
|
||||||
|
joined_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
|
||||||
|
PRIMARY KEY (project_id, user_id),
|
||||||
|
|
||||||
|
CONSTRAINT fk_pm_project FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE,
|
||||||
|
CONSTRAINT fk_pm_user FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- REQUIREMENTS: Updated with Status ID
|
||||||
|
CREATE TABLE requirements (
|
||||||
|
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
project_id INT NOT NULL,
|
||||||
|
user_id INT NOT NULL,
|
||||||
|
tag_id INT NOT NULL,
|
||||||
|
status_id INT NOT NULL DEFAULT 1, -- [NEW] Defaults to ID 1 (Draft)
|
||||||
|
last_editor_id INT,
|
||||||
|
req_name TEXT NOT NULL,
|
||||||
|
req_desc TEXT,
|
||||||
|
priority_id INT,
|
||||||
|
version INT NOT NULL DEFAULT 1,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
|
||||||
|
CONSTRAINT fk_req_project FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE,
|
||||||
|
CONSTRAINT fk_req_user FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE SET NULL,
|
||||||
|
CONSTRAINT fk_req_editor FOREIGN KEY (last_editor_id) REFERENCES users (id),
|
||||||
|
CONSTRAINT fk_req_priority FOREIGN KEY (priority_id) REFERENCES priorities (id),
|
||||||
|
CONSTRAINT fk_req_tag FOREIGN KEY (tag_id) REFERENCES tags (id),
|
||||||
|
CONSTRAINT fk_req_status FOREIGN KEY (status_id) REFERENCES requirement_statuses (id) -- [NEW]
|
||||||
|
);
|
||||||
|
|
||||||
|
-- REQUIREMENTS_GROUPS: Join table for M:N relationship
|
||||||
|
CREATE TABLE requirements_groups (
|
||||||
|
requirement_id INT NOT NULL,
|
||||||
|
group_id INT NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (requirement_id, group_id),
|
||||||
|
|
||||||
|
CONSTRAINT fk_rg_req FOREIGN KEY (requirement_id) REFERENCES requirements (id) ON DELETE CASCADE,
|
||||||
|
CONSTRAINT fk_rg_group FOREIGN KEY (group_id) REFERENCES groups (id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- VALIDATIONS: No change
|
||||||
|
CREATE TABLE validations (
|
||||||
|
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
requirement_id INT NOT NULL,
|
||||||
|
user_id INT NOT NULL,
|
||||||
|
status_id INT NOT NULL,
|
||||||
|
req_version_snapshot INT NOT NULL,
|
||||||
|
comment TEXT,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
|
||||||
|
CONSTRAINT fk_validation_req FOREIGN KEY (requirement_id) REFERENCES requirements (id) ON DELETE CASCADE,
|
||||||
|
CONSTRAINT fk_validation_user FOREIGN KEY (user_id) REFERENCES users (id),
|
||||||
|
CONSTRAINT fk_validation_status FOREIGN KEY (status_id) REFERENCES validation_statuses (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- REQUIREMENTS_HISTORY: Updated to track 'status_id' changes
|
||||||
|
CREATE TABLE requirements_history (
|
||||||
|
history_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
original_req_id INT NOT NULL,
|
||||||
|
project_id INT,
|
||||||
|
status_id INT, -- [NEW] Added to history
|
||||||
|
req_name TEXT,
|
||||||
|
req_desc TEXT,
|
||||||
|
priority_id INT,
|
||||||
|
tag_id INT,
|
||||||
|
version INT,
|
||||||
|
valid_from TIMESTAMPTZ,
|
||||||
|
valid_to TIMESTAMPTZ,
|
||||||
|
edited_by INT
|
||||||
|
);
|
||||||
|
|
||||||
|
-- The function to archive the old row before update
|
||||||
|
CREATE OR REPLACE FUNCTION archive_requirement_change()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO requirements_history (
|
||||||
|
original_req_id,
|
||||||
|
project_id,
|
||||||
|
status_id, -- [NEW]
|
||||||
|
req_name,
|
||||||
|
req_desc,
|
||||||
|
priority_id,
|
||||||
|
tag_id,
|
||||||
|
version,
|
||||||
|
valid_from,
|
||||||
|
valid_to,
|
||||||
|
edited_by
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
OLD.id,
|
||||||
|
OLD.project_id,
|
||||||
|
OLD.status_id, -- [NEW]
|
||||||
|
OLD.req_name,
|
||||||
|
OLD.req_desc,
|
||||||
|
OLD.priority_id,
|
||||||
|
OLD.tag_id,
|
||||||
|
OLD.version,
|
||||||
|
OLD.updated_at,
|
||||||
|
NOW(),
|
||||||
|
OLD.last_editor_id
|
||||||
|
);
|
||||||
|
|
||||||
|
NEW.version := OLD.version + 1;
|
||||||
|
NEW.updated_at := NOW();
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
-- Bind Trigger to requirements
|
||||||
|
CREATE TRIGGER trigger_audit_requirements
|
||||||
|
BEFORE UPDATE ON requirements
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION archive_requirement_change();
|
||||||
|
|
||||||
|
-- Indexes for performance
|
||||||
|
CREATE INDEX idx_req_project ON requirements(project_id);
|
||||||
|
CREATE INDEX idx_req_tag ON requirements(tag_id);
|
||||||
|
CREATE INDEX idx_req_priority ON requirements(priority_id);
|
||||||
|
CREATE INDEX idx_req_user ON requirements(user_id);
|
||||||
|
CREATE INDEX idx_req_status ON requirements(status_id); -- [NEW]
|
||||||
|
CREATE INDEX idx_pm_user ON project_members(user_id);
|
||||||
|
|
||||||
|
-- RELATIONSHIP_TYPES: Defines valid connection types per project
|
||||||
|
CREATE TABLE relationship_types (
|
||||||
|
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
project_id INT NOT NULL,
|
||||||
|
type_name TEXT NOT NULL,
|
||||||
|
type_description TEXT,
|
||||||
|
inverse_type_name TEXT,
|
||||||
|
|
||||||
|
CONSTRAINT fk_rel_type_project FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE,
|
||||||
|
CONSTRAINT uq_rel_type_name_project UNIQUE (project_id, type_name)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- REQUIREMENT_LINKS: The actual connections between requirements
|
||||||
|
CREATE TABLE requirement_links (
|
||||||
|
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
source_req_id INT NOT NULL,
|
||||||
|
target_req_id INT NOT NULL,
|
||||||
|
relationship_type_id INT NOT NULL,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
created_by INT,
|
||||||
|
|
||||||
|
CONSTRAINT fk_link_source FOREIGN KEY (source_req_id) REFERENCES requirements (id) ON DELETE CASCADE,
|
||||||
|
CONSTRAINT fk_link_target FOREIGN KEY (target_req_id) REFERENCES requirements (id) ON DELETE CASCADE,
|
||||||
|
CONSTRAINT fk_link_type FOREIGN KEY (relationship_type_id) REFERENCES relationship_types (id) ON DELETE CASCADE,
|
||||||
|
CONSTRAINT fk_link_creator FOREIGN KEY (created_by) REFERENCES users (id) ON DELETE SET NULL,
|
||||||
|
|
||||||
|
CONSTRAINT check_no_self_link CHECK (source_req_id <> target_req_id),
|
||||||
|
CONSTRAINT uq_req_link_pair UNIQUE (source_req_id, target_req_id, relationship_type_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_link_source ON requirement_links(source_req_id);
|
||||||
|
CREATE INDEX idx_link_target ON requirement_links(target_req_id);
|
||||||
|
|
||||||
|
-- REQUIREMENT_COMMENTS: Top-level comments
|
||||||
|
CREATE TABLE requirement_comments (
|
||||||
|
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
requirement_id INT NOT NULL,
|
||||||
|
user_id INT,
|
||||||
|
comment_text TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
is_deleted BOOLEAN DEFAULT FALSE,
|
||||||
|
|
||||||
|
CONSTRAINT fk_rc_req FOREIGN KEY (requirement_id) REFERENCES requirements (id) ON DELETE CASCADE,
|
||||||
|
CONSTRAINT fk_rc_user FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE SET NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- REQUIREMENT_COMMENT_REPLIES: Responses to top-level comments
|
||||||
|
CREATE TABLE requirement_comment_replies (
|
||||||
|
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
parent_comment_id INT NOT NULL,
|
||||||
|
user_id INT,
|
||||||
|
reply_text TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
is_deleted BOOLEAN DEFAULT FALSE,
|
||||||
|
|
||||||
|
CONSTRAINT fk_rcr_parent FOREIGN KEY (parent_comment_id) REFERENCES requirement_comments (id) ON DELETE CASCADE,
|
||||||
|
CONSTRAINT fk_rcr_user FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE SET NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_rc_req ON requirement_comments(requirement_id);
|
||||||
|
CREATE INDEX idx_rcr_parent ON requirement_comment_replies(parent_comment_id);
|
||||||
|
|
||||||
|
-- 1. HISTORY TABLE: Captures deleted/changed links with Snapshots
|
||||||
|
CREATE TABLE requirement_links_history (
|
||||||
|
history_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
original_link_id INT, -- The ID of the link from the main table
|
||||||
|
source_req_id INT,
|
||||||
|
target_req_id INT,
|
||||||
|
relationship_type_id INT, -- Kept for reference
|
||||||
|
relationship_type_snapshot TEXT, -- [IMPORTANT] Text copy of type name (e.g. "Depends On")
|
||||||
|
inverse_type_snapshot TEXT, -- [IMPORTANT] Text copy of inverse name (e.g. "Is Depended On By")
|
||||||
|
created_by INT,
|
||||||
|
valid_from TIMESTAMPTZ, -- When the link was created
|
||||||
|
valid_to TIMESTAMPTZ DEFAULT NOW() -- When the link was deleted/changed
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Indexes for querying link history
|
||||||
|
CREATE INDEX idx_link_hist_source ON requirement_links_history(source_req_id);
|
||||||
|
CREATE INDEX idx_link_hist_target ON requirement_links_history(target_req_id);
|
||||||
|
|
||||||
|
-- 2. FUNCTION: Handles the archiving with Snapshot lookup
|
||||||
|
CREATE OR REPLACE FUNCTION archive_link_change()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
DECLARE
|
||||||
|
v_type_name TEXT;
|
||||||
|
v_inverse_name TEXT;
|
||||||
|
BEGIN
|
||||||
|
-- Fetch current names from the configuration table to snapshot them
|
||||||
|
SELECT type_name, inverse_type_name INTO v_type_name, v_inverse_name
|
||||||
|
FROM relationship_types
|
||||||
|
WHERE id = OLD.relationship_type_id;
|
||||||
|
|
||||||
|
-- Insert into history with the text snapshots
|
||||||
|
INSERT INTO requirement_links_history (
|
||||||
|
original_link_id,
|
||||||
|
source_req_id,
|
||||||
|
target_req_id,
|
||||||
|
relationship_type_id,
|
||||||
|
relationship_type_snapshot,
|
||||||
|
inverse_type_snapshot,
|
||||||
|
created_by,
|
||||||
|
valid_from,
|
||||||
|
valid_to
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
OLD.id,
|
||||||
|
OLD.source_req_id,
|
||||||
|
OLD.target_req_id,
|
||||||
|
OLD.relationship_type_id,
|
||||||
|
v_type_name, -- Saved permanently
|
||||||
|
v_inverse_name, -- Saved permanently
|
||||||
|
OLD.created_by,
|
||||||
|
OLD.created_at,
|
||||||
|
NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
IF (TG_OP = 'DELETE') THEN
|
||||||
|
RETURN OLD;
|
||||||
|
ELSE
|
||||||
|
RETURN NEW;
|
||||||
|
END IF;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
-- 3. TRIGGER: Binds the function to the table
|
||||||
|
CREATE TRIGGER trigger_audit_links
|
||||||
|
BEFORE UPDATE OR DELETE ON requirement_links
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION archive_link_change();
|
||||||
|
|
||||||
|
-- 1. PRE-REQUISITE: Add created_at to the join table
|
||||||
|
-- We need this to know when the relationship began (valid_from).
|
||||||
|
ALTER TABLE requirements_groups
|
||||||
|
ADD COLUMN created_at TIMESTAMPTZ DEFAULT NOW();
|
||||||
|
|
||||||
|
-- 2. HISTORY TABLE: Captures removed/changed group associations
|
||||||
|
CREATE TABLE requirements_groups_history (
|
||||||
|
history_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
requirement_id INT NOT NULL,
|
||||||
|
group_id INT NOT NULL,
|
||||||
|
|
||||||
|
group_name_snapshot TEXT,
|
||||||
|
group_hex_color_snapshot VARCHAR(7),
|
||||||
|
|
||||||
|
valid_from TIMESTAMPTZ, -- When the association was created
|
||||||
|
valid_to TIMESTAMPTZ DEFAULT NOW() -- When the association was removed/changed
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Index for querying history by requirement
|
||||||
|
CREATE INDEX idx_req_groups_hist_req ON requirements_groups_history(requirement_id);
|
||||||
|
|
||||||
|
-- 3. FUNCTION: Handles the archiving with Snapshot lookup
|
||||||
|
CREATE OR REPLACE FUNCTION archive_requirements_groups_change()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
DECLARE
|
||||||
|
v_group_name TEXT;
|
||||||
|
v_hex_color VARCHAR(7);
|
||||||
|
BEGIN
|
||||||
|
-- Fetch current group details to snapshot them
|
||||||
|
-- We do this so the history remains readable even if the Group ID is deleted later
|
||||||
|
SELECT group_name, hex_color INTO v_group_name, v_hex_color
|
||||||
|
FROM groups
|
||||||
|
WHERE id = OLD.group_id;
|
||||||
|
|
||||||
|
-- Insert into history
|
||||||
|
INSERT INTO requirements_groups_history (
|
||||||
|
requirement_id,
|
||||||
|
group_id,
|
||||||
|
group_name_snapshot,
|
||||||
|
group_hex_color_snapshot,
|
||||||
|
valid_from,
|
||||||
|
valid_to
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
OLD.requirement_id,
|
||||||
|
OLD.group_id,
|
||||||
|
v_group_name, -- Saved permanently
|
||||||
|
v_hex_color, -- Saved permanently
|
||||||
|
OLD.created_at, -- The time it was originally linked
|
||||||
|
NOW() -- The time it was removed
|
||||||
|
);
|
||||||
|
|
||||||
|
IF (TG_OP = 'DELETE') THEN
|
||||||
|
RETURN OLD;
|
||||||
|
ELSE
|
||||||
|
RETURN NEW;
|
||||||
|
END IF;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
-- 4. TRIGGER: Binds the function to the table
|
||||||
|
-- Triggers on DELETE (removing a group) or UPDATE (swapping a group ID directly)
|
||||||
|
CREATE TRIGGER trigger_audit_requirements_groups
|
||||||
|
BEFORE UPDATE OR DELETE ON requirements_groups
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION archive_requirements_groups_change();
|
||||||
@@ -1,7 +1,3 @@
|
|||||||
# Development docker-compose
|
|
||||||
# Use this for local development with hot reload
|
|
||||||
# Run: docker-compose -f docker-compose.dev.yaml up
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# ===========================================
|
# ===========================================
|
||||||
# Keycloak Identity Provider
|
# Keycloak Identity Provider
|
||||||
@@ -12,9 +8,15 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
KEYCLOAK_ADMIN: admin
|
KEYCLOAK_ADMIN: admin
|
||||||
KEYCLOAK_ADMIN_PASSWORD: admin
|
KEYCLOAK_ADMIN_PASSWORD: admin
|
||||||
|
# Set hostname so tokens are issued with consistent issuer URL
|
||||||
|
KC_HOSTNAME: http://localhost:8081
|
||||||
|
KC_HOSTNAME_STRICT: false
|
||||||
|
KC_HTTP_ENABLED: true
|
||||||
ports:
|
ports:
|
||||||
- "8081:8080"
|
- "8081:8080"
|
||||||
command: start-dev
|
command: start-dev
|
||||||
|
volumes:
|
||||||
|
- keycloak_data:/opt/keycloak/data
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "exec 3<>/dev/tcp/localhost/8080 && echo -e 'GET /health/ready HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' >&3 && cat <&3 | grep -q '200 OK'"]
|
test: ["CMD-SHELL", "exec 3<>/dev/tcp/localhost/8080 && echo -e 'GET /health/ready HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' >&3 && cat <&3 | grep -q '200 OK'"]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
@@ -23,7 +25,7 @@ services:
|
|||||||
start_period: 30s
|
start_period: 30s
|
||||||
|
|
||||||
# ===========================================
|
# ===========================================
|
||||||
# FastAPI Backend (with hot reload)
|
# FastAPI Backend
|
||||||
# ===========================================
|
# ===========================================
|
||||||
fastapi-app:
|
fastapi-app:
|
||||||
build:
|
build:
|
||||||
@@ -41,10 +43,28 @@ services:
|
|||||||
- SUPER_ADMIN_USERNAME=${SUPER_ADMIN_USERNAME:-}
|
- SUPER_ADMIN_USERNAME=${SUPER_ADMIN_USERNAME:-}
|
||||||
- SUPER_ADMIN_EMAIL=${SUPER_ADMIN_EMAIL:-}
|
- SUPER_ADMIN_EMAIL=${SUPER_ADMIN_EMAIL:-}
|
||||||
- SUPER_ADMIN_PASSWORD=${SUPER_ADMIN_PASSWORD:-}
|
- SUPER_ADMIN_PASSWORD=${SUPER_ADMIN_PASSWORD:-}
|
||||||
|
extra_hosts:
|
||||||
|
- "localhost:host-gateway"
|
||||||
depends_on:
|
depends_on:
|
||||||
keycloak:
|
keycloak:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# React Frontend
|
||||||
|
# ===========================================
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: ./frontend
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: frontend
|
||||||
|
ports:
|
||||||
|
- "3000:80"
|
||||||
|
depends_on:
|
||||||
|
- fastapi-app
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
default:
|
default:
|
||||||
name: keycloak-auth-network
|
name: keycloak-auth-network
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
keycloak_data:
|
||||||
@@ -1,29 +1,4 @@
|
|||||||
services:
|
services:
|
||||||
# ===========================================
|
|
||||||
# Keycloak Identity Provider
|
|
||||||
# ===========================================
|
|
||||||
keycloak:
|
|
||||||
image: quay.io/keycloak/keycloak:latest
|
|
||||||
container_name: keycloak
|
|
||||||
environment:
|
|
||||||
KEYCLOAK_ADMIN: admin
|
|
||||||
KEYCLOAK_ADMIN_PASSWORD: admin
|
|
||||||
# Set hostname so tokens are issued with consistent issuer URL
|
|
||||||
KC_HOSTNAME: http://localhost:8081
|
|
||||||
KC_HOSTNAME_STRICT: false
|
|
||||||
KC_HTTP_ENABLED: true
|
|
||||||
ports:
|
|
||||||
- "8081:8080"
|
|
||||||
command: start-dev
|
|
||||||
volumes:
|
|
||||||
- keycloak_data:/opt/keycloak/data
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "exec 3<>/dev/tcp/localhost/8080 && echo -e 'GET /health/ready HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' >&3 && cat <&3 | grep -q '200 OK'"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
start_period: 30s
|
|
||||||
|
|
||||||
# ===========================================
|
# ===========================================
|
||||||
# FastAPI Backend
|
# FastAPI Backend
|
||||||
# ===========================================
|
# ===========================================
|
||||||
@@ -45,9 +20,6 @@ services:
|
|||||||
- SUPER_ADMIN_PASSWORD=${SUPER_ADMIN_PASSWORD:-}
|
- SUPER_ADMIN_PASSWORD=${SUPER_ADMIN_PASSWORD:-}
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
- "localhost:host-gateway"
|
- "localhost:host-gateway"
|
||||||
depends_on:
|
|
||||||
keycloak:
|
|
||||||
condition: service_healthy
|
|
||||||
|
|
||||||
# ===========================================
|
# ===========================================
|
||||||
# React Frontend
|
# React Frontend
|
||||||
@@ -65,6 +37,3 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
default:
|
default:
|
||||||
name: keycloak-auth-network
|
name: keycloak-auth-network
|
||||||
|
|
||||||
volumes:
|
|
||||||
keycloak_data:
|
|
||||||
79
k8s/prod.yaml
Normal file
79
k8s/prod.yaml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: periodic-table
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: fastapi-app
|
||||||
|
namespace: periodic-table
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: fastapi-app
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: fastapi-app
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: fastapi-app
|
||||||
|
image: periodic-table-backend:latest # TODO: push to registry and set full image name
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
ports:
|
||||||
|
- containerPort: 8080
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: periodic-table-env # Provide secrets for APP settings
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: fastapi-app
|
||||||
|
namespace: periodic-table
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: fastapi-app
|
||||||
|
ports:
|
||||||
|
- port: 8080
|
||||||
|
targetPort: 8080
|
||||||
|
name: http
|
||||||
|
type: ClusterIP
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: frontend
|
||||||
|
namespace: periodic-table
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: frontend
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: frontend
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: frontend
|
||||||
|
image: periodic-table-frontend:latest # TODO: push to registry and set full image name
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: frontend
|
||||||
|
namespace: periodic-table
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: frontend
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
targetPort: 80
|
||||||
|
name: http
|
||||||
|
type: LoadBalancer
|
||||||
Reference in New Issue
Block a user