Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .github/workflows/room-reservation-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Room Reservation CI

on:
push:
branches: [ "**" ]
pull_request:

permissions:
contents: read
packages: write

jobs:
test:
runs-on: ubuntu-latest
defaults:
run:
working-directory: room-reservation
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
cache: maven
- name: Build and test
run: mvn -B -ntp clean verify

docker:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Compute image repo (lowercase)
run: echo "IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}/room-reservation" >> $GITHUB_ENV
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and (conditionally) push image
uses: docker/build-push-action@v6
with:
context: ./room-reservation
push: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' }}
tags: ${{ env.IMAGE }}:latest,${{ env.IMAGE }}:${{ github.sha }}
18 changes: 18 additions & 0 deletions room-reservation/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Maven
.target
**/target
.mvn
**/.mvn

# Git
.git
.gitignore

# IDE
.idea
*.iml
.vscode

# OS
.DS_Store
Thumbs.db
28 changes: 28 additions & 0 deletions room-reservation/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# syntax=docker/dockerfile:1.7

# ----------------------
# Build stage
# ----------------------
FROM maven:3.9.8-eclipse-temurin-17 AS build
WORKDIR /app

COPY pom.xml .
COPY src ./src

# Use Maven cache to speed up builds
RUN --mount=type=cache,target=/root/.m2 \
mvn -B -ntp clean package

# ----------------------
# Runtime stage
# ----------------------
FROM eclipse-temurin:17-jre-alpine
ENV JAVA_OPTS=""
ENV SPRING_PROFILES_ACTIVE=prod
WORKDIR /app

# Copy fat jar built by Spring Boot plugin
COPY --from=build /app/target/room-reservation-0.1.0-SNAPSHOT.jar /app/app.jar

EXPOSE 8080
ENTRYPOINT ["sh","-c","java $JAVA_OPTS -jar /app/app.jar"]
38 changes: 38 additions & 0 deletions room-reservation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Room Reservation App

## Run locally (Docker Compose)

Prereqs: Docker Desktop/Engine

```bash
# from repo root or room-reservation directory
cd room-reservation

# Build and start Postgres + app
docker compose up --build

# App is now on http://localhost:8080
```

To stop and remove containers:
```bash
docker compose down -v
```

## Config
- Active profile: `prod` (Postgres)
- DB creds: user `roomres`, password `roomres`, db `roomres`
- Change via env vars: `SPRING_DATASOURCE_URL`, `SPRING_DATASOURCE_USERNAME`, `SPRING_DATASOURCE_PASSWORD`

## Build jar locally
```bash
mvn -B -ntp clean package
```

## CI
GitHub Actions workflow `room-reservation-ci.yml` builds and tests on every branch. On `main`/`master`, it also builds and pushes a Docker image to GHCR at `ghcr.io/<owner>/<repo>/room-reservation:latest`.

## Next steps
- Add REST controllers and DTOs
- Wire authentication and payments
- Add frontend
105 changes: 105 additions & 0 deletions room-reservation/db/schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
-- SQL Server DDL for Room Reservation App

CREATE TABLE buildings (
id BIGINT IDENTITY(1,1) PRIMARY KEY,
name NVARCHAR(200) NOT NULL,
address NVARCHAR(400) NULL,
created_at DATETIME2 NOT NULL DEFAULT SYSUTCDATETIME()
);

CREATE TABLE apartments (
id BIGINT IDENTITY(1,1) PRIMARY KEY,
building_id BIGINT NOT NULL,
floor INT NOT NULL,
apartment_number NVARCHAR(50) NOT NULL,
owner_name NVARCHAR(200) NULL,
contact_email NVARCHAR(200) NULL,
contact_phone NVARCHAR(50) NULL,
is_active BIT NOT NULL DEFAULT 1,
CONSTRAINT uq_apartment UNIQUE (building_id, apartment_number),
CONSTRAINT fk_apartment_building FOREIGN KEY (building_id) REFERENCES buildings(id)
);

CREATE TABLE users (
id BIGINT IDENTITY(1,1) PRIMARY KEY,
email NVARCHAR(255) NOT NULL UNIQUE,
password_hash NVARCHAR(255) NOT NULL,
role NVARCHAR(32) NOT NULL,
apartment_id BIGINT NULL,
full_name NVARCHAR(200) NULL,
phone NVARCHAR(50) NULL,
created_at DATETIME2 NOT NULL DEFAULT SYSUTCDATETIME(),
CONSTRAINT fk_user_apartment FOREIGN KEY (apartment_id) REFERENCES apartments(id)
);

CREATE TABLE rooms (
id BIGINT IDENTITY(1,1) PRIMARY KEY,
name NVARCHAR(200) NOT NULL,
capacity INT NOT NULL,
description NVARCHAR(1000) NULL,
is_active BIT NOT NULL DEFAULT 1,
rules_json NVARCHAR(MAX) NULL
);

CREATE TYPE booking_status AS TABLE (
status NVARCHAR(16)
);
-- Note: Using plain NVARCHAR for enums in SQL Server; enforce via check constraint

CREATE TABLE bookings (
id BIGINT IDENTITY(1,1) PRIMARY KEY,
booking_number BIGINT NULL,
room_id BIGINT NOT NULL,
apartment_id BIGINT NOT NULL,
requested_start DATETIME2 NOT NULL,
requested_end DATETIME2 NOT NULL,
status NVARCHAR(16) NOT NULL,
event_type NVARCHAR(100) NULL,
num_guests INT NULL,
notes NVARCHAR(2000) NULL,
created_by BIGINT NOT NULL,
created_at DATETIME2 NOT NULL DEFAULT SYSUTCDATETIME(),
updated_at DATETIME2 NULL,
approved_by BIGINT NULL,
approved_at DATETIME2 NULL,
payment_status NVARCHAR(16) NOT NULL DEFAULT 'NOT_PAID',
CONSTRAINT fk_booking_room FOREIGN KEY (room_id) REFERENCES rooms(id),
CONSTRAINT fk_booking_apartment FOREIGN KEY (apartment_id) REFERENCES apartments(id),
CONSTRAINT fk_booking_created_by FOREIGN KEY (created_by) REFERENCES users(id),
CONSTRAINT fk_booking_approved_by FOREIGN KEY (approved_by) REFERENCES users(id),
CONSTRAINT ck_booking_time CHECK (requested_end > requested_start),
CONSTRAINT ck_booking_status CHECK (status IN ('PENDING','APPROVED','REJECTED','CANCELLED','COMPLETED')),
CONSTRAINT ck_booking_payment CHECK (payment_status IN ('NOT_PAID','PAID','REFUNDED'))
);

-- Indexes for overlap queries
CREATE INDEX ix_bookings_room_time ON bookings (room_id, requested_start, requested_end);
CREATE INDEX ix_bookings_status ON bookings (status);
CREATE INDEX ix_bookings_apartment ON bookings (apartment_id);

CREATE TABLE payments (
id BIGINT IDENTITY(1,1) PRIMARY KEY,
booking_id BIGINT NOT NULL,
amount DECIMAL(12,2) NOT NULL,
currency NVARCHAR(8) NOT NULL,
status NVARCHAR(16) NOT NULL,
provider NVARCHAR(50) NOT NULL,
transaction_id NVARCHAR(128) NULL,
created_at DATETIME2 NOT NULL DEFAULT SYSUTCDATETIME(),
processed_at DATETIME2 NULL,
CONSTRAINT fk_payment_booking FOREIGN KEY (booking_id) REFERENCES bookings(id),
CONSTRAINT ck_payment_status CHECK (status IN ('INIT','SUCCESS','FAILED','REFUNDED'))
);

CREATE TABLE audit_logs (
id BIGINT IDENTITY(1,1) PRIMARY KEY,
entity_type NVARCHAR(64) NOT NULL,
entity_id BIGINT NOT NULL,
action NVARCHAR(64) NOT NULL,
actor_user_id BIGINT NULL,
details NVARCHAR(MAX) NULL,
created_at DATETIME2 NOT NULL DEFAULT SYSUTCDATETIME()
);

-- Optional sequences
CREATE SEQUENCE booking_number_seq AS BIGINT START WITH 1000 INCREMENT BY 1;
36 changes: 36 additions & 0 deletions room-reservation/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
version: "3.9"
services:
db:
image: postgres:16-alpine
container_name: roomres_db
environment:
POSTGRES_DB: roomres
POSTGRES_USER: roomres
POSTGRES_PASSWORD: roomres
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U roomres -d roomres"]
interval: 10s
timeout: 5s
retries: 5

app:
build: .
container_name: roomres_app
depends_on:
db:
condition: service_healthy
environment:
SPRING_PROFILES_ACTIVE: prod
SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/roomres
SPRING_DATASOURCE_USERNAME: roomres
SPRING_DATASOURCE_PASSWORD: roomres
JAVA_OPTS: -Xms256m -Xmx512m
ports:
- "8080:8080"

volumes:
pgdata: {}
106 changes: 106 additions & 0 deletions room-reservation/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<relativePath/>
</parent>

<groupId>com.building</groupId>
<artifactId>room-reservation</artifactId>
<version>0.1.0-SNAPSHOT</version>
<name>room-reservation</name>
<description>Room Reservation App</description>

<properties>
<java.version>17</java.version>
<testcontainers.version>1.20.2</testcontainers.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

<!-- Optional: Security (future) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<scope>provided</scope>
</dependency>

<!-- SQL Server driver for runtime -->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<scope>runtime</scope>
</dependency>

<!-- PostgreSQL driver for production profile/runtime -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>

<!-- H2 for local dev/tests -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>

<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mssqlserver</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-bom</artifactId>
<version>${testcontainers.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.building.roomreservation;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RoomReservationApplication {

public static void main(String[] args) {
SpringApplication.run(RoomReservationApplication.class, args);
}
}
Loading
Loading