Skip to content

Commit 89d1143

Browse files
authored
Merge pull request #2 from salieri009/feature/assignment-implementation
Implement UTS ISD Assignment 2: Features 01-04, Dockerization, CI/CD
2 parents bf1eb0a + 44f8ba3 commit 89d1143

43 files changed

Lines changed: 1861 additions & 1963 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.dockerignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
target/
2+
.git/
3+
.github/
4+
docs/
5+
node_modules/
6+
src/main/webapp/assets/ts/
7+
*.log
8+
*.db
9+
*.sqlite
10+
*.sqlite3
11+
.env
12+
.env.*
13+
*.legacy

.github/workflows/ci.yml

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,16 @@ on:
1717
# {Run on pushes to feature branches, but not main}
1818

1919
env:
20-
JAVA_VERSION: '17'
21-
MAVEN_VERSION: '3.9.5'
20+
JAVA_VERSION: '11'
2221

2322
jobs:
24-
# {CI Job: Build and Test}
2523
ci:
2624
name: CI - Build and Test
2725
runs-on: ubuntu-latest
2826

2927
strategy:
3028
matrix:
31-
# {Test on multiple Java versions if needed}
32-
java-version: ['17']
33-
# {Add more versions like '11', '21' if you need to test compatibility}
29+
java-version: ['11']
3430

3531
steps:
3632
- name: Checkout code

.github/workflows/deploy.yml

Lines changed: 76 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,83 @@
1-
name: Deploy
1+
name: Build & Deploy
22

33
on:
44
push:
5-
branches: [ main ]
6-
tags:
7-
- 'v*'
5+
branches: [main]
6+
tags: ['v*']
87

98
jobs:
10-
deploy:
9+
# ── 1. Build WAR ─────────────────────────────────────────────────────────────
10+
build:
11+
name: Build WAR
1112
runs-on: ubuntu-latest
12-
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
13-
13+
1414
steps:
15-
- uses: actions/checkout@v3
16-
17-
- name: Set up JDK 11
18-
uses: actions/setup-java@v3
19-
with:
20-
java-version: '11'
21-
distribution: 'temurin'
22-
23-
- name: Build WAR file
24-
run: mvn clean package -DskipTests
25-
26-
- name: Deploy to staging
27-
run: |
28-
echo "Deployment would occur here"
29-
echo "Built WAR: target/*.war"
30-
# Add actual deployment steps (e.g., SCP, Docker push, etc.)
15+
- uses: actions/checkout@v4
16+
17+
- name: Set up JDK 11
18+
uses: actions/setup-java@v4
19+
with:
20+
java-version: '11'
21+
distribution: 'temurin'
22+
23+
- name: Cache Maven dependencies
24+
uses: actions/cache@v4
25+
with:
26+
path: ~/.m2
27+
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
28+
restore-keys: ${{ runner.os }}-m2
29+
30+
- name: Build WAR (skip tests — tests run in CI)
31+
run: mvn -B clean package -DskipTests
32+
33+
- name: Upload WAR artifact
34+
uses: actions/upload-artifact@v4
35+
with:
36+
name: iot-bay-war
37+
path: target/iot-bay.war
38+
retention-days: 1
39+
40+
# ── 2. Build & Push Docker image to GHCR ────────────────────────────────────
41+
docker:
42+
name: Docker Build & Push
43+
needs: build
44+
runs-on: ubuntu-latest
45+
permissions:
46+
contents: read
47+
packages: write
48+
49+
steps:
50+
- uses: actions/checkout@v4
51+
52+
- name: Download WAR artifact
53+
uses: actions/download-artifact@v4
54+
with:
55+
name: iot-bay-war
56+
path: target/
57+
58+
- name: Log in to GitHub Container Registry
59+
uses: docker/login-action@v3
60+
with:
61+
registry: ghcr.io
62+
username: ${{ github.actor }}
63+
password: ${{ secrets.GITHUB_TOKEN }}
64+
65+
- name: Extract Docker metadata
66+
id: meta
67+
uses: docker/metadata-action@v5
68+
with:
69+
images: ghcr.io/${{ github.repository }}
70+
tags: |
71+
type=raw,value=latest,enable={{is_default_branch}}
72+
type=sha,prefix=sha-,format=short
73+
type=semver,pattern={{version}}
74+
75+
- name: Build and push Docker image
76+
uses: docker/build-push-action@v5
77+
with:
78+
context: .
79+
push: true
80+
tags: ${{ steps.meta.outputs.tags }}
81+
labels: ${{ steps.meta.outputs.labels }}
82+
cache-from: type=gha
83+
cache-to: type=gha,mode=max

Dockerfile

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# ── Build Stage ──────────────────────────────────────────────────────────────
2+
FROM maven:3.9-eclipse-temurin-11 AS builder
3+
4+
WORKDIR /build
5+
6+
# Cache dependency layer separately from source
7+
COPY pom.xml .
8+
RUN mvn dependency:go-offline -B -q
9+
10+
COPY src/ ./src/
11+
RUN mvn clean package -DskipTests -B -q
12+
13+
# ── Runtime Stage ─────────────────────────────────────────────────────────────
14+
FROM tomcat:9.0-jdk11-openjdk-slim
15+
16+
LABEL maintainer="IoTBay Team <iotbay@example.com>"
17+
18+
# Remove default Tomcat webapps (security hardening)
19+
RUN rm -rf /usr/local/tomcat/webapps/ROOT \
20+
/usr/local/tomcat/webapps/examples \
21+
/usr/local/tomcat/webapps/host-manager \
22+
/usr/local/tomcat/webapps/manager
23+
24+
# Deploy WAR as ROOT (serves at /)
25+
COPY --from=builder /build/target/iot-bay.war \
26+
/usr/local/tomcat/webapps/ROOT.war
27+
28+
# SQLite data directory — mount a named volume here for persistence
29+
RUN mkdir -p /opt/iotbay/data
30+
VOLUME ["/opt/iotbay/data"]
31+
32+
# Environment variables (override at runtime)
33+
ENV IOTBAY_DB_PATH=/opt/iotbay/data/iotbay.db
34+
ENV APP_ENV=production
35+
ENV IOTBAY_JWT_SECRET=""
36+
37+
EXPOSE 8080
38+
39+
CMD ["catalina.sh", "run"]

docker-compose.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
version: '3.8'
2+
3+
services:
4+
app:
5+
image: ghcr.io/salieri009/iotbay:latest
6+
build:
7+
context: .
8+
dockerfile: Dockerfile
9+
ports:
10+
- "8080:8080"
11+
volumes:
12+
- iotbay-data:/opt/iotbay/data
13+
environment:
14+
- IOTBAY_DB_PATH=/opt/iotbay/data/iotbay.db
15+
- APP_ENV=production
16+
- IOTBAY_JWT_SECRET=${IOTBAY_JWT_SECRET:-changeme-in-production}
17+
restart: unless-stopped
18+
healthcheck:
19+
test: ["CMD", "curl", "-f", "http://localhost:8080/"]
20+
interval: 30s
21+
timeout: 10s
22+
retries: 3
23+
start_period: 60s
24+
25+
volumes:
26+
iotbay-data:
27+
driver: local

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
<properties>
1212
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
1313
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
14-
<maven.compiler.source>1.8</maven.compiler.source>
15-
<maven.compiler.target>1.8</maven.compiler.target>
14+
<maven.compiler.source>11</maven.compiler.source>
15+
<maven.compiler.target>11</maven.compiler.target>
1616
<jettyVersion>9.4.12.v20180830</jettyVersion>
1717
</properties>
1818

src/main/java/CheckDB.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
public class CheckDB {
44
public static void main(String[] args) {
5-
String url = "jdbc:sqlite:D:/UTS/IoTBayPersonnel/IoTBay/iotbay.db";
5+
String url = config.AppConfig.getDbUrl();
66
System.out.println("Checking DB at: " + url);
77

88
try (Connection conn = DriverManager.getConnection(url)) {

src/main/java/config/AppConfig.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,20 @@ public static int getIntProperty(String key, int defaultValue) {
3838
return defaultValue;
3939
}
4040
}
41+
42+
/**
43+
* Returns the SQLite JDBC URL.
44+
* Priority: IOTBAY_DB_PATH env var > db.path property > fallback.
45+
*/
46+
public static String getDbUrl() {
47+
String envPath = System.getenv("IOTBAY_DB_PATH");
48+
if (envPath != null && !envPath.trim().isEmpty()) {
49+
return "jdbc:sqlite:" + envPath.trim();
50+
}
51+
String propPath = properties.getProperty("db.path");
52+
if (propPath != null && !propPath.trim().isEmpty()) {
53+
return "jdbc:sqlite:" + propPath.trim();
54+
}
55+
return "jdbc:sqlite:./iotbay.db";
56+
}
4157
}

src/main/java/controller/BrowsePageController.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,24 @@ else if (categoryIdParam != null && !categoryIdParam.trim().isEmpty()) {
9191
products = productDAO.getAllProducts();
9292
}
9393
}
94-
// Handle keyword search
94+
// Handle combined keyword + category search
9595
else if (keyword != null && !keyword.trim().isEmpty()) {
96-
products = productDAO.getProductsByName(keyword.trim());
96+
if (categoryId != null) {
97+
products = productDAO.searchByNameAndCategory(keyword.trim(), categoryId);
98+
request.setAttribute("categoryId", categoryId);
99+
} else if (categoryParam != null && !categoryParam.trim().isEmpty()) {
100+
try {
101+
dao.CategoryDAO catDAO = new dao.CategoryDAO();
102+
model.Category cat = catDAO.getCategoryByName(categoryParam.trim());
103+
Integer cid = cat != null ? cat.getId() : null;
104+
products = productDAO.searchByNameAndCategory(keyword.trim(), cid);
105+
request.setAttribute("category", categoryParam.trim());
106+
} catch (SQLException e) {
107+
products = productDAO.getProductsByName(keyword.trim());
108+
}
109+
} else {
110+
products = productDAO.searchByNameAndCategory(keyword.trim(), null);
111+
}
97112
request.setAttribute("keyword", keyword);
98113
}
99114
// Default: get all products

0 commit comments

Comments
 (0)