Skip to content

Commit acbb0be

Browse files
committed
fix: address Copilot review feedback
1 parent 79c27e5 commit acbb0be

8 files changed

Lines changed: 77 additions & 12 deletions

File tree

deployment-platforms/rest-express-docker-aws-ec2/.github/workflows/deploy.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ jobs:
6969
docker rm "$CONTAINER_NAME"
7070
fi
7171
72+
# Run database migrations
73+
docker run --rm \
74+
-e DATABASE_URL="${{ secrets.DATABASE_URL }}" \
75+
"$ECR_REGISTRY/$ECR_REPOSITORY:${{ github.sha }}" \
76+
npx prisma migrate deploy
77+
7278
# Run new container
7379
docker run -d \
7480
--name "$CONTAINER_NAME" \

deployment-platforms/rest-express-docker-aws-ec2/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ WORKDIR /app
66
COPY package*.json ./
77
COPY prisma ./prisma
88
COPY prisma.config.ts ./
9-
RUN npm ci
9+
RUN npm install
1010
RUN npx prisma generate
1111

1212
COPY tsconfig.json ./
@@ -23,7 +23,7 @@ RUN addgroup -S appgroup && adduser -S appuser -G appgroup
2323
COPY package*.json ./
2424
COPY prisma ./prisma
2525
COPY prisma.config.ts ./
26-
RUN npm ci --omit=dev
26+
RUN npm install --omit=dev
2727

2828
COPY --from=builder /app/dist ./dist
2929
COPY --from=builder /app/prisma/generated ./prisma/generated

deployment-platforms/rest-express-docker-aws-ec2/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,4 +160,4 @@ In your repository, go to **Settings → Secrets and variables → Actions** and
160160

161161
### 3. How deployment works
162162

163-
Pushing to `main` triggers [`.github/workflows/deploy.yml`](./.github/workflows/deploy.yml). The workflow authenticates with AWS, builds a Docker image using Buildx (with GitHub Actions layer caching for faster rebuilds) and pushes it to ECR tagged with both the commit SHA and `latest`. It then SSHs into your EC2 instance, pulls the new image, gracefully stops and removes the old container if one exists, starts the new one with `DATABASE_URL` injected at runtime, waits 5 seconds and verifies the container is running — printing logs and exiting non-zero if it isn't. Finally it prunes old images to keep the EC2 disk clean.
163+
Copy [`.github/workflows/deploy.yml`](./.github/workflows/deploy.yml) to `.github/workflows/` at the root of **your own repository**. Pushing to `main` then triggers the workflow. It authenticates with AWS, builds a Docker image using Buildx (with GitHub Actions layer caching for faster rebuilds) and pushes it to ECR tagged with both the commit SHA and `latest`. It then SSHs into your EC2 instance, runs `prisma migrate deploy` against your production database using a one-off container, pulls the new image, gracefully stops and removes the old container if one exists, starts the new one with `DATABASE_URL` injected at runtime, waits 5 seconds and verifies the container is running — printing logs and exiting non-zero if it isn't. Finally it prunes old images to keep the EC2 disk clean.

deployment-platforms/rest-express-docker-aws-ec2/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@
1212
"@prisma/client": "7.0.0",
1313
"dotenv": "^17.2.1",
1414
"express": "5.1.0",
15-
"pg": "^8.16.3"
15+
"pg": "^8.16.3",
16+
"prisma": "7.0.0"
1617
},
1718
"devDependencies": {
1819
"@types/express": "5.0.5",
1920
"@types/node": "22.18.12",
2021
"@types/pg": "^8.15.6",
21-
"prisma": "7.0.0",
2222
"tsx": "^4.20.6",
2323
"typescript": "5.8.2"
2424
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
-- CreateTable
2+
CREATE TABLE "User" (
3+
"id" SERIAL NOT NULL,
4+
"email" TEXT NOT NULL,
5+
"name" TEXT,
6+
7+
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
8+
);
9+
10+
-- CreateTable
11+
CREATE TABLE "Post" (
12+
"id" SERIAL NOT NULL,
13+
"title" TEXT NOT NULL,
14+
"content" TEXT,
15+
"published" BOOLEAN NOT NULL DEFAULT false,
16+
"authorId" INTEGER NOT NULL,
17+
18+
CONSTRAINT "Post_pkey" PRIMARY KEY ("id")
19+
);
20+
21+
-- CreateIndex
22+
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
23+
24+
-- AddForeignKey
25+
ALTER TABLE "Post" ADD CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Please do not edit this file manually
2+
# It should be added in your version-control system (e.g., Git)
3+
provider = "postgresql"

deployment-platforms/rest-express-docker-aws-ec2/src/routes/post.routes.ts

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Router, Request, Response } from 'express'
2+
import { Prisma } from '../../prisma/generated/client'
23
import { prisma } from '../lib/prisma'
34

45
export const postRouter = Router()
@@ -15,8 +16,13 @@ postRouter.get('/feed', async (_req: Request, res: Response) => {
1516
// GET /post/:id — single post by id
1617
postRouter.get('/post/:id', async (req: Request, res: Response) => {
1718
const { id } = req.params
19+
const postId = Number(id)
20+
if (!Number.isInteger(postId)) {
21+
res.status(400).json({ error: `Invalid post ID: ${id}` })
22+
return
23+
}
1824
const post = await prisma.post.findUnique({
19-
where: { id: Number(id) },
25+
where: { id: postId },
2026
})
2127
if (!post) {
2228
res.status(404).json({ error: `Post with ID ${id} not found` })
@@ -41,26 +47,50 @@ postRouter.post('/post', async (req: Request, res: Response) => {
4147
// PUT /publish/:id — publish a post
4248
postRouter.put('/publish/:id', async (req: Request, res: Response) => {
4349
const { id } = req.params
50+
const postId = Number(id)
51+
if (!Number.isInteger(postId)) {
52+
res.status(400).json({ error: `Invalid post ID: ${id}` })
53+
return
54+
}
4455
try {
4556
const post = await prisma.post.update({
46-
where: { id: Number(id) },
57+
where: { id: postId },
4758
data: { published: true },
4859
})
4960
res.json(post)
50-
} catch {
51-
res.status(404).json({ error: `Post with ID ${id} not found` })
61+
} catch (error: unknown) {
62+
if (
63+
error instanceof Prisma.PrismaClientKnownRequestError &&
64+
error.code === 'P2025'
65+
) {
66+
res.status(404).json({ error: `Post with ID ${id} not found` })
67+
return
68+
}
69+
res.status(500).json({ error: 'Internal server error' })
5270
}
5371
})
5472

5573
// DELETE /post/:id — delete post
5674
postRouter.delete('/post/:id', async (req: Request, res: Response) => {
5775
const { id } = req.params
76+
const postId = Number(id)
77+
if (!Number.isInteger(postId)) {
78+
res.status(400).json({ error: `Invalid post ID: ${id}` })
79+
return
80+
}
5881
try {
5982
const post = await prisma.post.delete({
60-
where: { id: Number(id) },
83+
where: { id: postId },
6184
})
6285
res.json(post)
63-
} catch {
64-
res.status(404).json({ error: `Post with ID ${id} not found` })
86+
} catch (error: unknown) {
87+
if (
88+
error instanceof Prisma.PrismaClientKnownRequestError &&
89+
error.code === 'P2025'
90+
) {
91+
res.status(404).json({ error: `Post with ID ${id} not found` })
92+
return
93+
}
94+
res.status(500).json({ error: 'Internal server error' })
6595
}
6696
})

deployment-platforms/rest-express-docker-aws-ec2/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"compilerOptions": {
3+
"target": "ES2022",
34
"outDir": "dist",
45
"strict": true,
56
"lib": ["esnext"],

0 commit comments

Comments
 (0)