Skip to content
This repository has been archived by the owner on Sep 15, 2024. It is now read-only.

Sprint 3 #9

Merged
merged 68 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
db9a999
feat: implement route for deleting officers
Dwigoric Nov 2, 2023
6e5ec5e
chore: add `watch` script using nodemon
Dwigoric Nov 3, 2023
03e0e91
refactor: generate UUID on schema-level
Dwigoric Nov 4, 2023
c8b8391
feat: loan and deposit schemas
kndonetm Nov 4, 2023
555a8b7
refactor: generating loan application forms now uses the Loan schema …
kndonetm Nov 4, 2023
2f6a5f6
style: consolidate all loan-related routes under a /loans router
kndonetm Nov 4, 2023
22f3f0f
feat: approve or reject loan application backend
kndonetm Nov 4, 2023
38a4921
feat: generate UUID on new loans and deposits
Dwigoric Nov 4, 2023
2deac75
feat: generate base36 ID on new transactions
Dwigoric Nov 4, 2023
4b9aeda
refactor: mark IDs as unique
Dwigoric Nov 4, 2023
d0bde31
!refactor: change uuid fields to id
Dwigoric Nov 4, 2023
897c42e
feat: implement indexes for faster queries
Dwigoric Nov 4, 2023
9f7bea3
refactor: ensure format of generated IDs
Dwigoric Nov 4, 2023
6fc7479
chore: merge same middlewares into one
Dwigoric Nov 4, 2023
9a0042c
fix: use regular function in pre middlewares
Dwigoric Nov 5, 2023
1172520
fix: check admin `id` field instead of `uuid`
Dwigoric Nov 5, 2023
393701a
chore: update mongoose
Dwigoric Nov 5, 2023
194850e
fix: use Query#set for IDs in pre-validation hooks
Dwigoric Nov 5, 2023
cd0d4de
chore: include base URL in request logs
Dwigoric Nov 5, 2023
86c23db
chore(morgan): handle if undefined base URL
Dwigoric Nov 5, 2023
ccd1d90
refactor: use text indexes for searching
Dwigoric Nov 5, 2023
38c351d
chore: remove loanee username validation to allow passbook numbers
kndonetm Nov 5, 2023
5955f94
refactor: spouse is no longer mandatory for users
kndonetm Nov 5, 2023
92b50c6
fix: correctly add loan type on loan creation
kndonetm Nov 5, 2023
415f80c
build(yarn): add vscode SDKs
Dwigoric Nov 6, 2023
7d3dcc7
refactor(loans): return 404 on nonexistent loanee
Dwigoric Nov 8, 2023
a8260d8
refactor(loan + deposit schema): make `approvalDate` immutable
Dwigoric Nov 8, 2023
7a1cdba
chore(vscode): enable `editor.formatOnSave`
Dwigoric Nov 8, 2023
d715dda
feat: deposit backend route
Dwigoric Nov 8, 2023
01d7351
feat: edit and delete loan backend routes
kndonetm Nov 8, 2023
7391dad
feat: add coborrowers to db in loan creation route
kndonetm Nov 8, 2023
531cc2b
chore: add field weights for searching
Dwigoric Nov 9, 2023
8698791
feat: implement `GET /loans` endpoint
Dwigoric Nov 9, 2023
2b7e12e
refactor: coborrower no longer required
kndonetm Nov 10, 2023
f03b03e
feat: loan mode of repayment (paymentFrequency internally)
kndonetm Nov 10, 2023
9cc1536
feat: route for getting loans by id
kndonetm Nov 10, 2023
0622aab
fix(loan review): use variable consistency
Dwigoric Nov 10, 2023
3aa85d4
feat: ledger routes
Dwigoric Nov 10, 2023
0a36bd6
refactor: add passport protection to users routes
kndonetm Nov 11, 2023
8e34465
feat: deposit backend routes
kndonetm Nov 11, 2023
2df032a
refactor: handle auth errors in users router
Dwigoric Nov 12, 2023
9e022ed
refactor: search text index in `GET /users/search`
Dwigoric Nov 12, 2023
a476f97
build: update yarn
Dwigoric Nov 12, 2023
0e27e84
feat(loans): adjust `GET /` for pending status
Dwigoric Nov 12, 2023
15709d1
build: add yarn release cjs
Dwigoric Nov 12, 2023
fc4a871
feat: loan and deposit settings schema
Dwigoric Nov 12, 2023
c74d4b0
feat: deposit form connection to backend (partial)
kndonetm Nov 12, 2023
36fee76
feat: add spouse occupation
Dwigoric Nov 13, 2023
93b83d9
!refactor: Use term `sex` instead of gender
Dwigoric Nov 13, 2023
8b00346
chore: upgrade dependencies
Dwigoric Nov 13, 2023
ff159b0
feat: settings routes
Dwigoric Nov 13, 2023
d4ec131
refactor: log errors only if they are not validation errors
Dwigoric Nov 13, 2023
fcb69ef
!refactor: process all coborrower information
Dwigoric Nov 13, 2023
6988dea
feat: implement routes to get member loans and deposits
Dwigoric Nov 13, 2023
94fa0a1
feat: handle status switching for loans
Dwigoric Nov 13, 2023
67b1701
feat: reworked loan transaction schema
kndonetm Nov 13, 2023
a80bb8e
Merge branch 'Sprint-3' of https://github.com/Dwigoric/unboundmnl-are…
kndonetm Nov 13, 2023
4147d11
cd: add Azure Web App deployment workflow
Dwigoric Nov 13, 2023
abbb93c
fix: loan transaction editing route now properly assigns fields
kndonetm Nov 13, 2023
aa74724
Merge branch 'Sprint-3' of https://github.com/Dwigoric/unboundmnl-are…
kndonetm Nov 13, 2023
1044e94
feat: deposit ledger routes
Dwigoric Nov 14, 2023
c201381
feat: loan status switching for user loans
Dwigoric Nov 14, 2023
716519b
feat: support multiple origins for CORS
Dwigoric Nov 14, 2023
ca41cbd
fix: use callback function for cors
Dwigoric Nov 14, 2023
483865e
fix: split correct env variable
Dwigoric Nov 14, 2023
a05a12c
fix: use cors whitelist as-is
Dwigoric Nov 14, 2023
49aead0
refator: adjust ledgers to match frontend
kndonetm Nov 15, 2023
9c98706
refactor: adjusted deposit ledger editing route to fit new db format
kndonetm Nov 15, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/workflows/deployment_azure-app-service.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions

name: Deploy Node.js express app to Azure Web App - compact-donuts

on:
push:
branches:
- main
- 'Sprint-[0-9]'
workflow_dispatch:

jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: 'Production'
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}

steps:
- uses: actions/checkout@v4

- name: Setup Node.js version
uses: actions/setup-node@v1
with:
node-version: '18.x'

- name: Install dependencies with yarn
run: |
echo 'Get latest yarn'
yarn set version stable
echo 'Install dependencies'
yarn install --immutable

- name: 'Deploy to Azure Web App'
uses: azure/webapps-deploy@v2
id: deploy-to-webapp
with:
app-name: 'unboundmnl-area-2-backend'
slot-name: 'Production'
publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
package: .
3 changes: 2 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
"esbenp.prettier-vscode",
"arcanis.vscode-zipfs"
]
}
9 changes: 9 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"search.exclude": {
"**/.yarn": true,
"**/.pnp.*": true
},
"eslint.nodePath": ".yarn/sdks",
"prettier.prettierPath": ".yarn/sdks/prettier/index.cjs",
"editor.formatOnSave": true
}
646 changes: 323 additions & 323 deletions .yarn/releases/yarn-4.0.0.cjs → .yarn/releases/yarn-4.0.1.cjs

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions .yarn/sdks/eslint/bin/eslint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env node

const {existsSync} = require(`fs`);
const {createRequire} = require(`module`);
const {resolve} = require(`path`);

const relPnpApiPath = "../../../../.pnp.cjs";

const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = createRequire(absPnpApiPath);

if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require eslint/bin/eslint.js
require(absPnpApiPath).setup();
}
}

// Defer to the real eslint/bin/eslint.js your application uses
module.exports = absRequire(`eslint/bin/eslint.js`);
20 changes: 20 additions & 0 deletions .yarn/sdks/eslint/lib/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env node

const {existsSync} = require(`fs`);
const {createRequire} = require(`module`);
const {resolve} = require(`path`);

const relPnpApiPath = "../../../../.pnp.cjs";

const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = createRequire(absPnpApiPath);

if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require eslint
require(absPnpApiPath).setup();
}
}

// Defer to the real eslint your application uses
module.exports = absRequire(`eslint`);
20 changes: 20 additions & 0 deletions .yarn/sdks/eslint/lib/unsupported-api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env node

const {existsSync} = require(`fs`);
const {createRequire} = require(`module`);
const {resolve} = require(`path`);

const relPnpApiPath = "../../../../.pnp.cjs";

const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = createRequire(absPnpApiPath);

if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require eslint/use-at-your-own-risk
require(absPnpApiPath).setup();
}
}

// Defer to the real eslint/use-at-your-own-risk your application uses
module.exports = absRequire(`eslint/use-at-your-own-risk`);
14 changes: 14 additions & 0 deletions .yarn/sdks/eslint/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "eslint",
"version": "8.53.0-sdk",
"main": "./lib/api.js",
"type": "commonjs",
"bin": {
"eslint": "./bin/eslint.js"
},
"exports": {
"./package.json": "./package.json",
".": "./lib/api.js",
"./use-at-your-own-risk": "./lib/unsupported-api.js"
}
}
5 changes: 5 additions & 0 deletions .yarn/sdks/integrations.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# This file is automatically generated by @yarnpkg/sdks.
# Manual changes might be lost!

integrations:
- vscode
20 changes: 20 additions & 0 deletions .yarn/sdks/prettier/bin/prettier.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env node

const {existsSync} = require(`fs`);
const {createRequire} = require(`module`);
const {resolve} = require(`path`);

const relPnpApiPath = "../../../../.pnp.cjs";

const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = createRequire(absPnpApiPath);

if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require prettier/bin/prettier.cjs
require(absPnpApiPath).setup();
}
}

// Defer to the real prettier/bin/prettier.cjs your application uses
module.exports = absRequire(`prettier/bin/prettier.cjs`);
20 changes: 20 additions & 0 deletions .yarn/sdks/prettier/index.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env node

const {existsSync} = require(`fs`);
const {createRequire} = require(`module`);
const {resolve} = require(`path`);

const relPnpApiPath = "../../../.pnp.cjs";

const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = createRequire(absPnpApiPath);

if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require prettier
require(absPnpApiPath).setup();
}
}

// Defer to the real prettier your application uses
module.exports = absRequire(`prettier`);
7 changes: 7 additions & 0 deletions .yarn/sdks/prettier/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "prettier",
"version": "3.0.3-sdk",
"main": "./index.cjs",
"type": "commonjs",
"bin": "./bin/prettier.cjs"
}
2 changes: 1 addition & 1 deletion .yarnrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ enableGlobalCache: false

nodeLinker: pnp

yarnPath: .yarn/releases/yarn-4.0.0.cjs
yarnPath: .yarn/releases/yarn-4.0.1.cjs
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ The MongoDB URI to use for the application, defaults to `mongodb://localhost:270
### `JWT_SECRET`
The secret to use for JWT. This is required for the application to run.
If not set, the application will throw an error.
### `FRONTEND_URL`
The URL of the frontend application, defaults to `http://localhost:5173`.
### `FRONTEND_URLS`
The comma-separated list of URLs of the frontend application.
This is used for CORS.
If no value is set, the application will allow all origins.

## Private Files
Put any private files in the `private` directory.
Expand All @@ -47,3 +48,8 @@ yarn
```sh
yarn start
```

### Automatically Restart the Server on Changes
```sh
yarn watch
```
2 changes: 1 addition & 1 deletion example.env
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
MONGODB_URI=
JWT_SECRET=
FRONTEND_URL=
FRONTEND_URLS=
PORT=
22 changes: 12 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,31 @@
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node index.js"
"start": "node index.js",
"watch": "nodemon --watch src index.js"
},
"dependencies": {
"argon2": "0.30.3",
"cookie-parser": "~1.4.4",
"cookie-parser": "~1.4.6",
"cors": "^2.8.5",
"debug": "~2.6.9",
"debug": "~4.3.4",
"dotenv": "^16.3.1",
"express": "~4.16.1",
"express": "~4.18.2",
"http-errors": "^2.0.0",
"jsonwebtoken": "^9.0.2",
"mongoose": "^7.5.3",
"morgan": "~1.9.1",
"mongoose": "^8.0.0",
"morgan": "~1.10.0",
"passport": "^0.6.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
"uuid": "^9.0.1"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.5.1",
"eslint": "^8.50.0",
"eslint-plugin-import": "^2.28.1",
"prettier": "^3.0.3"
"eslint": "^8.53.0",
"eslint-plugin-import": "^2.29.0",
"nodemon": "^3.0.1",
"prettier": "^3.1.0"
},
"packageManager": "[email protected].0"
"packageManager": "[email protected].1"
}
26 changes: 13 additions & 13 deletions src/app.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
// Default FRONTEND_URL

const DEFAULT_FRONTEND_URL = 'http://localhost:5173'

// Packages
import createError from 'http-errors'
import express from 'express'
Expand All @@ -22,18 +18,20 @@ await database.init().catch((err) => {
import indexRouter from './routes/index.js'
import usersRouter from './routes/users.js'
import authRouter from './routes/auth.js'
import officerRoute from './routes/officers.js'
import loanApplications from './routes/loan-applications.js'
import officerRouter from './routes/officers.js'
import loansRouter from './routes/loans.js'
import depositsRouter from './routes/deposits.js'
import settingsRouter from './routes/settings.js'

const app = express()

// Configure CORS
if (!process.env.FRONTEND_URL)
console.warn(`FRONTEND_URL not set, using default: ${DEFAULT_FRONTEND_URL}`)

const whitelist = []
if (!process.env.FRONTEND_URLS) console.warn('FRONTEND_URLS not set, allowing all origins')
else whitelist.push(...process.env.FRONTEND_URLS.split(','))
app.use(
cors({
origin: process.env.FRONTEND_URL || DEFAULT_FRONTEND_URL,
origin: whitelist.length === 0 ? true : whitelist,
optionsSuccessStatus: 200
})
)
Expand All @@ -47,7 +45,7 @@ if (!process.env.JWT_SECRET) {
process.exit(1)
}

logger.token('url', (req) => req.path)
logger.token('url', (req) => (req.baseUrl || '') + req.path)
app.use(logger('dev'))
app.use(express.json())
app.use(express.urlencoded({ extended: false }))
Expand All @@ -69,8 +67,10 @@ app.use('/private', express.static('private'))
app.use('/', indexRouter)
app.use('/users', usersRouter)
app.use('/auth', authRouter)
app.use('/officers', officerRoute)
app.use('/loan-applications', loanApplications)
app.use('/officers', officerRouter)
app.use('/loans', loansRouter)
app.use('/deposits', depositsRouter)
app.use('/settings', settingsRouter)

// Catch 404 and forward to error handler
app.use(function (req, res, next) {
Expand Down
6 changes: 3 additions & 3 deletions src/auth/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ passport.use(
* to use this strategy.
*/
passport.use(
'register-officer',
'admin',
new JwtStrategy(
{
jwtFromRequest: ExtractJwt.fromExtractors([
Expand All @@ -96,7 +96,7 @@ passport.use(
},
async function verify(payload, done) {
// Retrieve admin from request body's UUID
const admin = await Admin.findOne({ uuid: payload.uuid }).lean()
const admin = await Admin.findOne({ id: payload.uuid }).lean()

// Check if admin exists
if (!admin) {
Expand Down Expand Up @@ -129,7 +129,7 @@ passport.use(
async function verify(payload, done) {
try {
const manager = await (payload.type === 'admin' ? Admin : LoanOfficer)
.findOne({ uuid: payload.uuid })
.findOne({ id: payload.uuid })
.lean()

if (!manager) {
Expand Down
12 changes: 6 additions & 6 deletions src/models/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ const Admin = model(
username: {
type: String,
unique: true,
required: [true, 'Username is required'],
immutable: true,
default: 'admin',
validate: {
validator: (username) => {
return username === 'admin'
Expand All @@ -21,10 +22,11 @@ const Admin = model(
type: String,
required: [true, 'Password is required']
},
uuid: {
id: {
type: String,
unique: true,
required: [true, 'UUID is required'],
immutable: true,
default: () => uuidV5('admin', uuidV5.URL),
validate: {
validator: (uuid) => uuidValidate(uuid) && uuidVersion(uuid) === 5,
message: 'UUID must be a valid UUID'
Expand All @@ -39,12 +41,10 @@ Admin.findOne({ username: 'admin' }, 'username')
.then(async (existing) => {
if (!existing) {
const password_hash = await argon2.hash('admin')
const uuid = uuidV5('admin', uuidV5.URL)

const admin = new Admin({
username: 'admin',
password_hash,
uuid
password_hash
})

admin.save()
Expand Down
Loading
Loading