Skip to content

Commit e09cac8

Browse files
committed
Prepare frontend container
1 parent 4c67b5c commit e09cac8

File tree

12 files changed

+128
-11
lines changed

12 files changed

+128
-11
lines changed

.github/workflows/on-release-tag.yml

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ on:
1515
type: boolean
1616

1717
jobs:
18-
build-image:
18+
build-course-hub-backend:
1919
runs-on: ubuntu-latest
2020
permissions:
2121
contents: read
@@ -53,13 +53,54 @@ jobs:
5353
5454
- name: Build
5555
env:
56-
DOCKER_PUSH: true
56+
DOCKER_PUSH: ${{ github.repository == 'HackYourFuture/CourseHub' }}
5757
run: ./gradlew -Pversion=${{ steps.get-version.outputs.version }} bootBuildImage
5858

59+
build-course-hub-frontend:
60+
runs-on: ubuntu-latest
61+
permissions:
62+
contents: read
63+
packages: write
64+
steps:
65+
- uses: actions/checkout@v5
66+
67+
- uses: docker/setup-buildx-action@v3
68+
with:
69+
platforms: linux/amd64
70+
71+
- uses: docker/login-action@v3
72+
with:
73+
registry: ghcr.io
74+
username: ${{ github.actor }}
75+
password: ${{ secrets.GITHUB_TOKEN }}
76+
77+
- id: get-version
78+
run: |
79+
# Determine version from tag or input
80+
VERSION="${{ inputs.version || '' }}"
81+
if [ -z "$VERSION" ]; then
82+
VERSION="${GITHUB_REF_NAME#v}"
83+
fi
84+
echo "version=${VERSION}" >> $GITHUB_OUTPUT
85+
86+
- name: Build and push Docker image
87+
env:
88+
DOCKER_PUSH: ${{ github.repository == 'HackYourFuture/CourseHub' }}
89+
run: |
90+
docker build \
91+
--build-arg VERSION=${VERSION} \
92+
-t ghcr.io/hackyourfuture/course-hub-frontend:${{ steps.get-version.outputs.version }} \
93+
-t ghcr.io/hackyourfuture/course-hub-frontend:latest \
94+
.
95+
if [ "$DOCKER_PUSH" = "true" ]; then
96+
docker push ghcr.io/hackyourfuture/course-hub-frontend:${{ steps.get-version.outputs.version }}
97+
docker push ghcr.io/hackyourfuture/course-hub-frontend:latest
98+
fi
99+
59100
deploy:
60101
# do not execute on forks
61102
if: ${{ github.repository == 'HackYourFuture/CourseHub' && inputs.deploy }}
62-
needs: build-image
103+
needs: [build-course-hub-backend, build-course-hub-frontend]
63104
permissions:
64105
contents: read
65106
uses: ./.github/workflows/deploy-to-digital-ocean.yml

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,16 @@ To run the backend application:
5252

5353
The application is accessible on `http://localhost:8080`.
5454

55-
### Building docker image
55+
### Building docker images
5656

57-
To build a Docker image of the application, run the following command:
57+
To build a Docker image of the course-hub backend, run the following command:
5858
```bash
5959
./gradlew bootBuildImage
6060
```
61+
To build a Docker image for the frontend, from the `ui` directory, run:
62+
```bash
63+
docker build -t ghcr.io/hackyourfuture/course-hub-frontend ui
64+
```
6165

6266
#### Running docker image
6367

docker-compose.yaml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,14 @@ services:
1515
container_name: redis
1616
ports:
1717
- "6379:6379"
18-
course-hub:
18+
course-hub-frontend:
19+
image: ghcr.io/hackyourfuture/course-hub-frontend:latest
20+
container_name: course-hub-frontend
21+
environment:
22+
BACKEND_URL: http://host.docker.internal:8080
23+
ports:
24+
- "80:80"
25+
course-hub-backend:
1926
image: ghcr.io/hackyourfuture/course-hub-backend:latest
2027
container_name: course-hub
2128
profiles:

src/main/java/net/hackyourfuture/coursehub/SecurityConfig.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,23 @@
1313
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
1414
import org.springframework.security.crypto.password.PasswordEncoder;
1515
import org.springframework.security.web.SecurityFilterChain;
16+
import org.springframework.web.cors.CorsConfiguration;
1617

1718
@Configuration
1819
@EnableWebSecurity
1920
public class SecurityConfig {
2021
@Bean
2122
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
2223
return http.csrf(AbstractHttpConfigurer::disable)
24+
.cors(cors -> cors.configurationSource(request -> {
25+
var config = new CorsConfiguration();
26+
config.addAllowedOriginPattern("http://localhost:8080");
27+
config.addAllowedOriginPattern("http://host.docker.internal:8080");
28+
config.addAllowedOriginPattern("https://coursehub.hyf.dev");
29+
config.addAllowedHeader("*");
30+
config.addAllowedMethod("*");
31+
return config;
32+
}))
2333
.authorizeHttpRequests(auth -> auth.requestMatchers("/login", "/register")
2434
.permitAll()
2535
.requestMatchers(HttpMethod.GET, "/courses")

ui/Dockerfile

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Stage 1: Build the React app
2+
FROM node:20-alpine AS builder
3+
WORKDIR /app
4+
COPY package.json package-lock.json ./
5+
RUN npm ci
6+
COPY . .
7+
RUN npm run build
8+
9+
# Stage 2: Serve with nginx
10+
FROM nginx:alpine
11+
COPY --from=builder /app/dist /usr/share/nginx/html
12+
COPY public/images /usr/share/nginx/html/images
13+
COPY nginx-entrypoint.sh /usr/local/bin/nginx-entrypoint.sh
14+
15+
EXPOSE 80
16+
ENTRYPOINT ["nginx-entrypoint.sh"]

ui/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
</head>
99
<body>
1010
<div id="root"></div>
11+
<script src="/config.js"></script>
1112
<script type="module" src="/src/main.jsx"></script>
1213
</body>
1314
</html>

ui/nginx-entrypoint.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/bin/sh
2+
cat <<EOF > /usr/share/nginx/html/config.js
3+
// nginx-entrypoint.sh
4+
window.__CONFIG__ = {
5+
backendUrl: "${BACKEND_URL}"
6+
};
7+
EOF
8+
nginx -g 'daemon off;'

ui/public/config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// default
2+
window.__CONFIG__ = {
3+
backendUrl: "http://localhost:8080"
4+
};

ui/src/ConfigContext.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import {createContext, useContext} from "react";
2+
3+
export type Config = {
4+
backendUrl: string;
5+
};
6+
7+
export const config = loadConfig();
8+
export const ConfigContext = createContext<Config>(config);
9+
10+
function loadConfig(): Config {
11+
if ((window as any).__CONFIG__) {
12+
return (window as any).__CONFIG__;
13+
}
14+
return {
15+
backendUrl: 'http://localhost:8080'
16+
};
17+
}
18+
19+
export const useConfig = () => useContext(ConfigContext);

ui/src/main.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@ import {createRoot} from "react-dom/client";
33
import {QueryClient, QueryClientProvider} from "@tanstack/react-query";
44
import App from "./App";
55
import "./index.css";
6+
import { ConfigContext, config } from './ConfigContext';
67

78
const queryClient = new QueryClient();
89

910
createRoot(document.getElementById("root")!).render(
1011
<StrictMode>
11-
<QueryClientProvider client={queryClient}>
12-
<App/>
13-
</QueryClientProvider>
12+
<ConfigContext.Provider value={config}>
13+
<QueryClientProvider client={queryClient}>
14+
<App/>
15+
</QueryClientProvider>
16+
</ConfigContext.Provider>
1417
</StrictMode>
1518
);

0 commit comments

Comments
 (0)