Skip to content

Commit efc13e0

Browse files
authored
dockerize webapp and api
1 parent edc39a8 commit efc13e0

File tree

15 files changed

+177
-12
lines changed

15 files changed

+177
-12
lines changed

.github/workflows/cd.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: Tiny-URL-CD-Pipeline
2+
3+
on:
4+
workflow_run:
5+
workflows: ["Tiny-URL-CI-Pipeline"] # must match `name:` in ci.yml
6+
types:
7+
- completed
8+
workflow_dispatch:
9+
10+
jobs:
11+
build-docker-images:
12+
#if: >
13+
# github.event.workflow_run.conclusion == 'success' &&
14+
#github.event.workflow_run.head_branch == 'main'
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- name: Checkout code
19+
uses: actions/checkout@v4
20+
21+
- name: Set up Docker Buildx
22+
uses: docker/setup-buildx-action@v3
23+
24+
- name: Cache Docker layers
25+
uses: actions/cache@v3
26+
with:
27+
path: /tmp/.buildx-cache
28+
key: ${{ runner.os }}-docker-${{ github.sha }}
29+
restore-keys: |
30+
${{ runner.os }}-docker-
31+
32+
- name: Build API Docker image
33+
run: |
34+
docker build \
35+
--file api/Dockerfile \
36+
--tag your-org/tinyurl-api:main \
37+
--cache-from=type=local,src=/tmp/.buildx-cache \
38+
--cache-to=type=local,dest=/tmp/.buildx-cache \
39+
./api
40+
41+
- name: Build Webapp Docker image
42+
run: |
43+
docker build \
44+
--file webapp/Dockerfile \
45+
--tag your-org/tinyurl-webapp:main \
46+
--cache-from=type=local,src=/tmp/.buildx-cache \
47+
--cache-to=type=local,dest=/tmp/.buildx-cache \
48+
./webapp

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Tiny URL CI Pipeline
1+
name: Tiny-URL-CI-Pipeline
22

33
on:
44
push:

api/.dockerignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules
2+
dist
3+
.next
4+
.env
5+
*.log

api/.env-dev

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
MACHINE_ID=1
2-
PORT=3000
3-
# Perhpas we should only use a domain name here instead of a full URL (with the port as it is error prone)
4-
SHORTENED_BASE_URL=http://localhost:3000
2+
PORT=3001
3+
# This is the base URL for the shortened URLs. It is used to generate the shortened URL and redirect to the original URL.
4+
# Like http://localhost:3001/aZ34PM redirecting to https://google.com
5+
SHORTENED_BASE_URL=http://localhost:3001
56
# You can set this property to false for rapid testing. It will use in-memory storage rather than pulling Redis and DynamoDB
67
USE_PERSISTENT_STORAGE=false
78
REDIS_URL=redis://localhost:6379

api/.env-docker

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
NODE_ENV=development
2+
MACHINE_ID=1
3+
PORT=3000
4+
# This is the base URL for the shortened URLs. It is used to generate the shortened URL and redirect to the original URL.
5+
# Like http://localhost:3001/aZ34PM redirecting to https://google.com
6+
SHORTENED_BASE_URL=http://localhost:3001
7+
# You can set this property to false for rapid testing. It will use in-memory storage rather than pulling Redis and DynamoDB
8+
USE_PERSISTENT_STORAGE=true
9+
REDIS_URL=redis://redis:6379
10+
# 10 minutes
11+
REDIS_TTL=600
12+
AWS_REGION=us-east-1
13+
DYNAMO_TABLE=shortUrls
14+
# for local development only
15+
DYNAMO_ENDPOINT=http://dynamodb:8000
16+
AWS_ACCESS_KEY_ID=dummy
17+
AWS_SECRET_ACCESS_KEY=dummy

api/Dockerfile

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Stage 1: Build
2+
FROM node:18-alpine AS builder
3+
4+
WORKDIR /app
5+
COPY . .
6+
RUN yarn install --frozen-lockfile
7+
RUN yarn build
8+
9+
# Stage 2: Production image
10+
FROM node:18-alpine
11+
12+
WORKDIR /app
13+
ENV NODE_ENV=production
14+
15+
# Copy only what is needed for production
16+
COPY --from=builder /app/dist ./dist
17+
COPY --from=builder /app/package.json ./
18+
COPY --from=builder /app/yarn.lock ./
19+
20+
RUN yarn install --production --frozen-lockfile
21+
22+
# Create non-root user
23+
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
24+
USER appuser
25+
26+
EXPOSE 3000
27+
CMD ["node", "dist/main"]

api/src/shorten-url/create-shorten-url/create-shorten-url.controller.spec.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ describe('ShortenUrl controller', () => {
2121
const response = await underTest.shortenUrl({ url: longUrl });
2222

2323
// Extract the unique ID part of the URL
24-
const urlPattern = /^http:\/\/localhost:3000\/([A-Za-z0-9]{10})$/;
24+
const baseUrl = process.env.SHORTENED_BASE_URL;
25+
if (!baseUrl) throw new Error('SHORTENED_BASE_URL is not defined');
26+
27+
const escapedBaseUrl = baseUrl.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // Escape for regex
28+
const urlPattern = new RegExp(`^${escapedBaseUrl}/([A-Za-z0-9]{10})$`);
2529
const match = response?.shortenedUrl.match(urlPattern);
2630

2731
expect(response).not.toBeNull();

docker-compose.yml

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,39 @@
11
version: '3.8'
22

33
services:
4+
5+
api:
6+
build:
7+
context: ./api
8+
dockerfile: Dockerfile
9+
container_name: tinyurl-api
10+
restart: always
11+
ports:
12+
- "3001:3000"
13+
env_file:
14+
- ./api/.env-docker
15+
16+
webapp:
17+
build:
18+
context: ./webapp
19+
dockerfile: Dockerfile
20+
container_name: tinyurl-webapp
21+
ports:
22+
- "3000:3000"
23+
depends_on:
24+
- api
25+
env_file:
26+
- ./webapp/.env-docker
27+
428
redis:
529
image: redis:7.2.7
630
container_name: redis
731
restart: always
832
ports:
933
- "6379:6379"
10-
command: redis-server --save "" --appendonly no --maxmemory 100mb --maxmemory-policy allkeys-lru # Disable disk persistence
34+
command: redis-server --save "" --appendonly no --maxmemory 100mb --maxmemory-policy allkeys-lru # Disable disk persistence
1135
volumes:
12-
- redis_data:/data # ✅ Ensures no persistence to disk
36+
- redis_data:/data
1337
environment:
1438
- ALLOW_EMPTY_PASSWORD=yes
1539

@@ -22,7 +46,7 @@ services:
2246
depends_on:
2347
- redis
2448
environment:
25-
- REDIS_URI=redis://redis:6379 #Connect RedisInsight to Redis automatically
49+
- REDIS_URI=redis://redis:6379 #Connect RedisInsight to Redis automatically
2650

2751
dynamodb:
2852
image: amazon/dynamodb-local:2.6.0

webapp/.dockerignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules
2+
dist
3+
.next
4+
.env
5+
*.log

webapp/.env-dev

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
SHORTENED_BASE_URL=http://localhost:3001

0 commit comments

Comments
 (0)