Skip to content

Commit 9d5aff2

Browse files
Merge pull request #58 from AET-DevOps25/57-server-implement-course-service-api-crud-enrollments-bookmarks
feat(server): implement course service API (CRUD, enrollments, bookmarks) and add gateway-based rate limiting & authorization
2 parents 880ab73 + 26fcc9b commit 9d5aff2

File tree

180 files changed

+10585
-1747
lines changed

Some content is hidden

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

180 files changed

+10585
-1747
lines changed
File renamed without changes.

.github/actions/create-dotenv/action.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,12 @@ runs:
1515
# JWT (used by user-service tests)
1616
JWT_SECRET=test-secret
1717
JWT_EXPIRATION_MS=3600000
18+
19+
# MongoDB (for all services)
20+
MONGO_URL=mongodb://localhost:27017/test
21+
22+
# Redis (for gateway)
23+
REDIS_HOST=redis
24+
REDIS_PORT=6379
1825
EOF
1926
echo "✅ Generated .env:" && cat .env

.github/workflows/build-and-test-client.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ name: Build and Test Client
22

33
on:
44
push:
5+
branches: [main, test, dev]
56
paths:
67
- 'client/**'
78
pull_request:
8-
branches: [main]
9+
branches: [main, test, dev]
910
paths:
1011
- 'client/**'
1112
workflow_dispatch:

.github/workflows/build-and-test-genai.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ name: Build, Lint & Test the GenAI Service
22

33
on:
44
push:
5+
branches: [main, test, dev]
56
paths:
67
- 'genai/**'
78
pull_request:
9+
branches: [main, test, dev]
810
paths:
911
- 'genai/**'
1012
workflow_dispatch:

.github/workflows/build-and-test-server.yml

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@ name: Build and Test Server
22

33
on:
44
push:
5+
branches: [main, test, dev]
56
paths:
67
- 'server/skillforge-gateway/**'
78
- 'server/skillforge-user/**'
9+
- 'server/skillforge-course/**'
810
pull_request:
9-
branches: [main]
11+
branches: [main, test, dev]
1012
paths:
1113
- 'server/skillforge-gateway/**'
1214
- 'server/skillforge-user/**'
15+
- 'server/skillforge-course/**'
1316
workflow_dispatch:
1417

1518
permissions:
@@ -19,6 +22,11 @@ jobs:
1922
test-gateway:
2023
name: Build & Test Gateway
2124
runs-on: ubuntu-latest
25+
services:
26+
redis:
27+
image: redis:7-alpine
28+
ports:
29+
- 6379:6379
2230
defaults:
2331
run:
2432
working-directory: server/skillforge-gateway
@@ -33,12 +41,27 @@ jobs:
3341
- uses: ./.github/actions/create-dotenv
3442
- name: Build and test
3543
env:
36-
SPRING_PROFILE_ACTIVES: test
37-
run: ./gradlew build --no-daemon
38-
44+
SPRING_PROFILES_ACTIVE: test
45+
REDIS_HOST: redis
46+
REDIS_PORT: 6379
47+
run: ./gradlew build --no-daemon --scan
48+
- name: Post workflow summary
49+
if: always()
50+
run: |
51+
echo "## Workflow Summary" >> $GITHUB_STEP_SUMMARY
52+
echo "- Status: ${{ job.status }}" >> $GITHUB_STEP_SUMMARY
53+
echo "- Commit: ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
54+
echo "- Branch: ${{ github.ref }}" >> $GITHUB_STEP_SUMMARY
55+
echo "- Run: [View logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> $GITHUB_STEP_SUMMARY
56+
3957
test-user-service:
4058
name: Build & Test User Service
4159
runs-on: ubuntu-latest
60+
services:
61+
mongo:
62+
image: mongo:7
63+
ports:
64+
- 27017:27017
4265
defaults:
4366
run:
4467
working-directory: server/skillforge-user
@@ -53,5 +76,50 @@ jobs:
5376
- uses: ./.github/actions/create-dotenv
5477
- name: Build and test
5578
env:
56-
SPRING_PROFILE_ACTIVES: test
57-
run: ./gradlew build --no-daemon
79+
SPRING_PROFILES_ACTIVE: test
80+
JWT_SECRET: test-jwt-secret
81+
JWT_EXPIRATION_MS: 3600000
82+
MONGO_URL: mongodb://localhost:27017/test
83+
run: ./gradlew build --no-daemon --scan
84+
- name: Post workflow summary
85+
if: always()
86+
run: |
87+
echo "## Workflow Summary" >> $GITHUB_STEP_SUMMARY
88+
echo "- Status: ${{ job.status }}" >> $GITHUB_STEP_SUMMARY
89+
echo "- Commit: ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
90+
echo "- Branch: ${{ github.ref }}" >> $GITHUB_STEP_SUMMARY
91+
echo "- Run: [View logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> $GITHUB_STEP_SUMMARY
92+
93+
test-course-service:
94+
name: Build & Test Course Service
95+
runs-on: ubuntu-latest
96+
services:
97+
mongo:
98+
image: mongo:7
99+
ports:
100+
- 27017:27017
101+
defaults:
102+
run:
103+
working-directory: server/skillforge-course
104+
steps:
105+
- uses: actions/checkout@v4
106+
- name: Set up JDK 21
107+
uses: actions/setup-java@v4
108+
with:
109+
java-version: '21'
110+
distribution: 'temurin'
111+
cache: 'gradle'
112+
- uses: ./.github/actions/create-dotenv
113+
- name: Build and test
114+
env:
115+
SPRING_PROFILES_ACTIVE: test
116+
MONGO_URL: mongodb://localhost:27017/test
117+
run: ./gradlew build --no-daemon --scan
118+
- name: Post workflow summary
119+
if: always()
120+
run: |
121+
echo "## Workflow Summary" >> $GITHUB_STEP_SUMMARY
122+
echo "- Status: ${{ job.status }}" >> $GITHUB_STEP_SUMMARY
123+
echo "- Commit: ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
124+
echo "- Branch: ${{ github.ref }}" >> $GITHUB_STEP_SUMMARY
125+
echo "- Run: [View logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> $GITHUB_STEP_SUMMARY

.github/workflows/docker-build-push.yml

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,20 @@ name: Build and Push Docker Images
22

33
on:
44
push:
5-
branches: [main, test]
5+
branches: [main, test, dev]
66
paths:
77
- 'client/**'
88
- 'server/skillforge-gateway/**'
99
- 'server/skillforge-user/**'
10+
- 'server/skillforge-course/**'
1011
- 'genai/**'
1112
pull_request:
12-
branches: [main, test]
13+
branches: [main, test, dev]
1314
paths:
1415
- 'client/**'
1516
- 'server/skillforge-gateway/**'
1617
- 'server/skillforge-user/**'
18+
- 'server/skillforge-course/**'
1719
- 'genai/**'
1820
workflow_dispatch:
1921
workflow_call:
@@ -28,9 +30,6 @@ env:
2830
jobs:
2931
build-and-push:
3032
name: ${{ matrix.name }}
31-
environment:
32-
name: AWS
33-
3433
runs-on: ubuntu-latest
3534
strategy:
3635
matrix:
@@ -53,12 +52,14 @@ jobs:
5352
path: server/skillforge-gateway
5453
image: server-gateway
5554
name: Build and Push Server Gateway
56-
build-args: ""
5755
- id: user-service
5856
path: server/skillforge-user
5957
image: user-service
6058
name: Build and Push User Service
61-
build-args: ""
59+
- id: course-service
60+
path: server/skillforge-course
61+
image: course-service
62+
name: Build and Push Course Service
6263
- id: genai
6364
path: genai
6465
image: genai
@@ -116,7 +117,6 @@ jobs:
116117
${{ matrix.build-args }}
117118
VITE_APP_VERSION=${{ steps.extract_version.outputs.version }}
118119
119-
120120
- name: Clean up dangling images
121121
run: |
122122
docker image prune -f
@@ -127,3 +127,12 @@ jobs:
127127
- name: Output image URL
128128
run: |
129129
echo "Image URL: ghcr.io/aet-devops25/team-git-it-together/${{ matrix.image }}:${{ steps.set_tag.outputs.tag }}"
130+
- name: Post workflow summary
131+
if: always()
132+
run: |
133+
echo "## Docker Build & Push Summary" >> $GITHUB_STEP_SUMMARY
134+
echo "- Status: ${{ job.status }}" >> $GITHUB_STEP_SUMMARY
135+
echo "- Commit: ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
136+
echo "- Branch: ${{ github.ref }}" >> $GITHUB_STEP_SUMMARY
137+
echo "- Image: ghcr.io/aet-devops25/team-git-it-together/${{ matrix.image }}:${{ steps.set_tag.outputs.tag }}" >> $GITHUB_STEP_SUMMARY
138+
echo "- Run: [View logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> $GITHUB_STEP_SUMMARY

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ npm-debug.log
2222
testem.log
2323
typings/
2424
.angular/
25+
# Ignore seed courses folder
26+
seed/seed_courses/
27+
2528

2629
# e2e
2730
e2e/*.js

client/nginx.conf.template

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Nginx config – rendered at runtime via envsubst
2-
# `${API_INTERNAL_HOST}` is replaced with the environment variable value when the
2+
# `${VITE_API_INTERNAL_HOST}` is replaced with the environment variable value when the
33
# container starts (handled automatically by the nginx docker-entrypoint).
44

55
# in server_name, we use "_" to catch all.

client/src/contexts/AuthContext.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,26 @@ interface AuthContextType {
1616
username?: string;
1717
password: string;
1818
rememberMe: boolean;
19-
}) => Promise<void>;
19+
}) => Promise<UserLoginResponse>;
2020
logout: () => void;
2121
register: (opts: {
2222
firstName: string;
2323
lastName: string;
2424
username: string;
2525
email: string;
2626
password: string;
27-
}) => Promise<void>;
27+
}) => Promise<UserLoginResponse>;
2828
}
2929

3030
export const AuthContext = createContext<AuthContextType>({
3131
user: null,
3232
token: null,
3333
loading: true,
34-
login: async () => {},
35-
register: async () => {},
34+
login: async () => {
35+
throw new Error('Login function not implemented. Make sure to wrap your app in AuthProvider.');
36+
},
37+
register: async () => {
38+
throw new Error('Register function not implemented. Make sure to wrap your app in AuthProvider.');
39+
},
3640
logout: () => {},
3741
});

client/src/contexts/AuthProvider.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
3636
username?: string;
3737
password: string;
3838
rememberMe: boolean;
39-
}) => {
39+
}): Promise<UserLoginResponse> => {
4040
// Extract rememberMe separately; build a proper LoginPayload for userService.login
4141
const { email = '', username = '', password, rememberMe } = opts;
4242
const payload: LoginPayload = { email, username, password };
@@ -46,6 +46,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
4646
// response.jwtToken must exist, and response carries whatever user fields your backend returns
4747
persistAuth(response.jwtToken, response, rememberMe);
4848
navigate('/dashboard');
49+
return response;
4950
};
5051

5152
// Register function to register a new user
@@ -55,7 +56,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
5556
username: string;
5657
email: string;
5758
password: string;
58-
}) => {
59+
}): Promise<UserLoginResponse> => {
5960
const { firstName, lastName, username, email, password } = opts;
6061
// Call the userService.register method
6162
const response = await userService.register({
@@ -74,11 +75,14 @@ export function AuthProvider({ children }: { children: ReactNode }) {
7475
username: response.username,
7576
password, // Use the same password for login
7677
};
77-
await login({
78+
// The login function returns the user payload, so we can return it from here.
79+
return login({
7880
...loginPayload,
79-
rememberMe: true,
81+
rememberMe: true, // Auto-remember on register
8082
});
8183
}
84+
// This path should not be reached if userService.register throws on failure, but as a safeguard:
85+
throw new Error('Registration failed: server did not return a valid user.');
8286
};
8387

8488
const logout = () => {

0 commit comments

Comments
 (0)