Skip to content

Commit 11a9781

Browse files
committed
Rewrite iOS course backend
1 parent 4f18a30 commit 11a9781

35 files changed

+5431
-0
lines changed

.envtemplate

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
MONGO_URI=[insert here]

.github/pull_request_template.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
## Overview
2+
3+
<!-- Provide a one-sentence summary about this PR. -->
4+
5+
## Changes Made
6+
7+
<!-- Summarize changes made in this PR. -->
8+
9+
### Change 1
10+
11+
- TODO
12+
- TODO
13+
14+
### Change 2
15+
16+
- TODO
17+
- TODO
18+
19+
## Test Coverage
20+
21+
<!-- Provide step-by-step instructions on how to test. -->
22+
23+
## Next Steps (optional)
24+
25+
<!-- Describe the next steps, if applicable. -->
26+
27+
## Related PRs or Issues (optional)
28+
29+
<!-- Link any connect PRs or GitHub Issues, if applicable. -->
30+
31+
## Screenshots
32+
33+
<!-- Replace 'TODO' with a title, then use the provided `<img>` and `<video>` tags as a template. -->
34+
<!-- If multiple sections are needed, duplicate the `<details>` block. -->
35+
36+
<details>
37+
<summary>TODO</summary>
38+
<img src="enterurlhere" width="300px" height="auto">
39+
<video src="enterurlhere" type="video/mp4">
40+
</details>

.github/workflows/deploy.yaml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: Production Deployment
2+
3+
on:
4+
push:
5+
branches:
6+
- "main"
7+
8+
jobs:
9+
setup-build-publish-deploy:
10+
name: Setup, Build, Publish, and Deploy
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout
15+
uses: actions/checkout@v3
16+
17+
- name: Auth Docker
18+
uses: docker/login-action@v3
19+
with:
20+
username: ${{ secrets.DOCKER_USERNAME }}
21+
password: ${{ secrets.DOCKER_PASSWORD }}
22+
23+
- name: Get SHA
24+
id: vars
25+
run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
26+
27+
- name: Build
28+
run: |-
29+
docker build --tag "cornellappdev/ios-course-backend:${{ steps.vars.outputs.sha_short }}" .
30+
31+
- name: Publish
32+
run: |-
33+
docker push "cornellappdev/ios-course-backend:${{ steps.vars.outputs.sha_short }}"
34+
35+
- name: SSH & Deploy
36+
uses: appleboy/ssh-action@master
37+
with:
38+
host: ${{ secrets.SERVER_HOST }}
39+
username: ${{ secrets.SERVER_USER }}
40+
key: ${{ secrets.PRIVATE_KEY }}
41+
script: |
42+
export IOS_COURSE_IMG_TAG="${{ steps.vars.outputs.sha_short }}"
43+
cd docker-compose
44+
yes | docker system prune --all --volumes
45+
docker-compose down --remove-orphans
46+
docker-compose up -d --build

.github/workflows/jestci.yaml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Jest CI
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
11+
jobs:
12+
test:
13+
runs-on: ubuntu-latest
14+
15+
steps:
16+
- name: Checkout repository
17+
uses: actions/checkout@v3
18+
19+
- name: Use Node.js ${{ matrix.node-version }}
20+
uses: actions/setup-node@v3
21+
with:
22+
cache-dependency-path: ./yarn.lock
23+
node-version: ${{ matrix.node-version }}
24+
cache: "yarn"
25+
26+
- name: Install dependencies
27+
run: yarn install
28+
29+
- name: Run Jest tests
30+
run: yarn test

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.DS_Store
2+
node_modules/
3+
package-lock.json
4+
*.env
5+
build/

.prettierrc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"arrowParens": "always",
3+
"bracketSpacing": true,
4+
"endOfLine": "lf",
5+
"htmlWhitespaceSensitivity": "css",
6+
"insertPragma": false,
7+
"singleAttributePerLine": false,
8+
"bracketSameLine": false,
9+
"jsxBracketSameLine": false,
10+
"jsxSingleQuote": false,
11+
"printWidth": 80,
12+
"proseWrap": "preserve",
13+
"quoteProps": "as-needed",
14+
"requirePragma": false,
15+
"semi": true,
16+
"singleQuote": false,
17+
"tabWidth": 2,
18+
"trailingComma": "es5",
19+
"useTabs": false,
20+
"embeddedLanguageFormatting": "auto",
21+
"vueIndentScriptAndStyle": false
22+
}

Dockerfile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FROM node:20
2+
3+
RUN mkdir /usr/app
4+
WORKDIR /usr/app
5+
6+
COPY yarn.lock .
7+
COPY package.json .
8+
9+
RUN yarn install
10+
COPY . .
11+
EXPOSE 8000
12+
CMD ["yarn", "start"]

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,30 @@
11
# ios-course-backend
2+
23
Backend for L7: Networking II and A3
4+
5+
## About This Backend
6+
7+
The database is hosted on MongoDB Atlas. There are two collections:
8+
9+
1. posts (A3: ChatDev)
10+
2. members (L7: Networking II)
11+
12+
A CI/CD script has been created - simply merge a PR to `main` to deploy, after all checks have passed. If needed, the SSH key is pinned in our Slack.
13+
14+
## Getting Started
15+
16+
1. Duplicate the `.envtemplate` file and rename it to `.env`. Set `MONGO_URI` to the database URI.
17+
2. Run `yarn` to install dependencies. If you don't have yarn installed, run `npm install --global yarn`.
18+
3. Start the development server by running `yarn dev`. To run the production build instead, use `yarn build` followed by `yarn start`.
19+
4. API documentation can be found in the `/api/docs` route.
20+
21+
## Testing
22+
23+
### Jest
24+
25+
1. Make sure dependencies are installed with `yarn`.
26+
2. To run the test suite, use `yarn test`.
27+
28+
## Styling
29+
30+
- If using VSCode, install the Prettier extension and configure your settings to use it.

jest.config.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module.exports = {
2+
preset: "ts-jest",
3+
testEnvironment: "node",
4+
transformIgnorePatterns: ["<rootDir>/node_modules/"],
5+
moduleFileExtensions: ["js", "ts"],
6+
maxWorkers: 1,
7+
};

nodemon.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"events": {
3+
"restart": "tsoa spec-and-routes"
4+
}
5+
}

package.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"name": "ios-course-backend",
3+
"version": "1.0.0",
4+
"main": "src/index.ts",
5+
"scripts": {
6+
"start": "tsoa spec-and-routes && ts-node -r dotenv/config src/index.ts",
7+
"dev": "tsoa spec-and-routes && nodemon -r dotenv/config src/index.ts --watch src --config nodemon.json",
8+
"build": "tsoa spec-and-routes && tsc --outDir build --experimentalDecorators",
9+
"test": "jest"
10+
},
11+
"dependencies": {
12+
"@typegoose/typegoose": "^12.4.0",
13+
"@types/express": "^4.17.21",
14+
"@types/node": "^20.12.12",
15+
"@types/swagger-ui-express": "^4.1.6",
16+
"body-parser": "^1.20.2",
17+
"dotenv": "^16.4.5",
18+
"express": "^4.19.2",
19+
"mongodb-memory-server": "^10.0.0",
20+
"mongoose": "^8.3.5",
21+
"node-fetch-commonjs": "^3.3.2",
22+
"nodemon": "^3.1.0",
23+
"swagger-ui-express": "^5.0.0",
24+
"ts-node": "^10.9.2",
25+
"tsoa": "^6.2.1",
26+
"typescript": "^5.4.5"
27+
},
28+
"devDependencies": {
29+
"@faker-js/faker": "^8.4.1",
30+
"@jest/globals": "^29.7.0",
31+
"jest": "^29.7.0",
32+
"ts-jest": "^29.2.5"
33+
}
34+
}

src/database.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import mongoose from "mongoose";
2+
3+
/**
4+
* Connects to the MongoDB database.
5+
*
6+
* Retrieves the MongoDB URI from the `MONGO_URI` environment variable
7+
* and establishes a connection to the database.
8+
*/
9+
export const dbConnect = async () => {
10+
const uri = process.env.MONGO_URI;
11+
12+
// Connect to MongoDB
13+
await mongoose.connect(uri!);
14+
console.log("✅ Connected to MongoDB");
15+
};
16+
17+
/**
18+
* Disconnects from the MongoDB database.
19+
*/
20+
export const dbDisconnect = async () => {
21+
await mongoose.disconnect();
22+
console.log("✅ Disconnected from MongoDB");
23+
};

src/index.html

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Intro to iOS Development Backend</title>
5+
<style>
6+
body {
7+
font-family: sans-serif;
8+
display: flex;
9+
flex-direction: column;
10+
align-items: center;
11+
justify-content: center;
12+
min-height: 100vh;
13+
background-color: #f0f0f0;
14+
margin: 0;
15+
}
16+
17+
h1 {
18+
color: #333;
19+
}
20+
21+
p {
22+
text-align: center;
23+
margin-bottom: 20px;
24+
}
25+
26+
a {
27+
display: inline-block;
28+
padding: 10px 20px;
29+
background-color: #007bff;
30+
color: #fff;
31+
text-decoration: none;
32+
border-radius: 5px;
33+
}
34+
</style>
35+
</head>
36+
<body>
37+
<h1>Welcome to the backend for Intro to iOS Development!</h1>
38+
<p>If you have any questions, reach out to Vin on Slack.</p>
39+
<a href="/api/docs">Click here to view API docs</a>
40+
</body>
41+
</html>

src/index.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import express, { Request, Response } from "express";
2+
import bodyParser from "body-parser";
3+
import swaggerUI from "swagger-ui-express";
4+
import { RegisterRoutes } from "../build/routes";
5+
import { dbConnect } from "./database";
6+
import { errorMiddleware } from "./middleware/errorMiddleware";
7+
8+
/**
9+
* Initializes and starts the Express server.
10+
*
11+
* This script sets up the Express server with the following configurations:
12+
* - Body parsing: Uses `body-parser` to parse JSON request bodies.
13+
* - Default route: Serves an `index.html` file from the current directory for the root path.
14+
* - Swagger documentation: Sets up Swagger UI for API documentation at `/api/docs`.
15+
* - API routes: Registers API routes from the generated `routes` file.
16+
* - Error handling: Uses a custom error middleware to handle errors.
17+
* - Database connection: Connects to the MongoDB database using the `dbConnect` function.
18+
*
19+
* The server listens on the port specified by the `PORT` environment variable or port 8000 if not defined.
20+
*/
21+
22+
// Express Server
23+
const app = express();
24+
app.use(bodyParser.json());
25+
26+
// Default Route
27+
app.get("/", (req, res) => {
28+
return res.sendFile("index.html", { root: __dirname });
29+
});
30+
31+
// Swagger API Docs
32+
app.use("/api/docs", swaggerUI.serve, async (req: Request, res: Response) => {
33+
return res.send(
34+
swaggerUI.generateHTML(await import("../build/swagger.json"))
35+
);
36+
});
37+
38+
RegisterRoutes(app);
39+
40+
// Define error middleware last
41+
app.use(errorMiddleware);
42+
43+
app.listen(process.env.PORT || 8000, async () => {
44+
console.log(
45+
`✅ Server is up and running at http://localhost:${
46+
process.env.PORT || 8000
47+
}`
48+
);
49+
await dbConnect();
50+
});

src/members/examples.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* An example member object.
3+
*/
4+
export const exampleMember = {
5+
/**
6+
* The name of the member.
7+
*/
8+
name: "Vin Bui",
9+
/**
10+
* The position or role of the member.
11+
*/
12+
position: "Course Instructor",
13+
/**
14+
* The subteam the member belongs to.
15+
*/
16+
subteam: "iOS",
17+
};
18+
19+
/**
20+
* An array containing the example member.
21+
*/
22+
export const exampleMembers = [exampleMember];

0 commit comments

Comments
 (0)