Skip to content

Commit

Permalink
Add driver's license
Browse files Browse the repository at this point in the history
  • Loading branch information
aelassas committed Dec 3, 2024
1 parent ff0f511 commit 2c4328f
Show file tree
Hide file tree
Showing 40 changed files with 677 additions and 14 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ jobs:
echo BC_CDN_TEMP_LOCATIONS=$BC_CDN_TEMP_LOCATIONS >> .env
echo BC_CDN_CONTRACTS=$BC_CDN_CONTRACTS >> .env
echo BC_CDN_TEMP_CONTRACTS=$BC_CDN_TEMP_CONTRACTS >> .env
echo BC_CDN_LICENSES=$BC_CDN_LICENSES >> .env
echo BC_CDN_TEMP_LICENSES=$BC_CDN_TEMP_LICENSES >> .env
echo BC_BACKEND_HOST=$BC_BACKEND_HOST >> .env
echo BC_FRONTEND_HOST=$BC_FRONTEND_HOST >> .env
echo BC_MINIMUM_AGE=$BC_MINIMUM_AGE >> .env
Expand Down Expand Up @@ -84,6 +86,8 @@ jobs:
BC_CDN_TEMP_LOCATIONS: ${{ vars.BC_CDN_TEMP_LOCATIONS }}
BC_CDN_CONTRACTS: ${{ vars.BC_CDN_CONTRACTS }}
BC_CDN_TEMP_CONTRACTS: ${{ vars.BC_CDN_TEMP_CONTRACTS }}
BC_CDN_LICENSES: ${{ vars.BC_CDN_LICENSES }}
BC_CDN_TEMP_LICENSES: ${{ vars.BC_CDN_TEMP_LICENSES }}
BC_BACKEND_HOST: ${{ vars.BC_BACKEND_HOST }}
BC_FRONTEND_HOST: ${{ vars.BC_FRONTEND_HOST }}
BC_MINIMUM_AGE: ${{ vars.BC_MINIMUM_AGE }}
Expand Down
2 changes: 2 additions & 0 deletions api/.env.docker.example
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ BC_CDN_LOCATIONS=/var/www/cdn/bookcars/locations
BC_CDN_TEMP_LOCATIONS=/var/www/cdn/bookcars/temp/locations
BC_CDN_CONTRACTS=/var/www/cdn/bookcars/contracts
BC_CDN_TEMP_CONTRACTS=/var/www/cdn/bookcars/temp/contracts
BC_CDN_LICENSES=/var/www/cdn/bookcars/licenses
BC_CDN_TEMP_LICENSES=/var/www/cdn/bookcars/temp/licenses
BC_DEFAULT_LANGUAGE=en
BC_BACKEND_HOST=http://localhost:3001/
BC_FRONTEND_HOST=http://localhost/
Expand Down
2 changes: 2 additions & 0 deletions api/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ BC_CDN_LOCATIONS=/var/www/cdn/bookcars/locations
BC_CDN_TEMP_LOCATIONS=/var/www/cdn/bookcars/temp/locations
BC_CDN_CONTRACTS=/var/www/cdn/bookcars/contracts
BC_CDN_TEMP_CONTRACTS=/var/www/cdn/bookcars/temp/contracts
BC_CDN_LICENSES=/var/www/cdn/bookcars/licenses
BC_CDN_TEMP_LICENSES=/var/www/cdn/bookcars/temp/licenses
BC_DEFAULT_LANGUAGE=en
BC_BACKEND_HOST=http://localhost:3001/
BC_FRONTEND_HOST=http://localhost:3002/
Expand Down
2 changes: 2 additions & 0 deletions api/__tests__/supplier.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ describe('PUT /api/update-supplier', () => {
phone,
payLater,
minimumRentalDays: 3,
licenseRequired: true,
}
let res = await request(app)
.put('/api/update-supplier')
Expand All @@ -194,6 +195,7 @@ describe('PUT /api/update-supplier', () => {
expect(res.body.phone).toBe(phone)
expect(res.body.payLater).toBeFalsy()
expect(res.body.minimumRentalDays).toBe(3)
expect(res.body.licenseRequired).toBeTruthy()

// test success (supplier not found)
payload._id = testHelper.GetRandromObjectIdAsString()
Expand Down
2 changes: 2 additions & 0 deletions api/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,7 @@ await helper.mkdir(env.CDN_LOCATIONS)
await helper.mkdir(env.CDN_TEMP_LOCATIONS)
await helper.mkdir(env.CDN_CONTRACTS)
await helper.mkdir(env.CDN_TEMP_CONTRACTS)
await helper.mkdir(env.CDN_LICENSES)
await helper.mkdir(env.CDN_TEMP_LICENSES)

export default app
18 changes: 18 additions & 0 deletions api/src/config/env.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,20 @@ export const CDN_CONTRACTS = __env__('BC_CDN_CONTRACTS', true)
*/
export const CDN_TEMP_CONTRACTS = __env__('BC_CDN_TEMP_CONTRACTS', true)

/**
* Licenses' cdn folder path.
*
* @type {string}
*/
export const CDN_LICENSES = __env__('BC_CDN_LICENSES', true)

/**
* Licenses' temp cdn folder path.
*
* @type {string}
*/
export const CDN_TEMP_LICENSES = __env__('BC_CDN_TEMP_LICENSES', true)

/**
* Backend host.
*
Expand Down Expand Up @@ -373,6 +387,8 @@ export interface User extends Document {
payLater?: boolean
customerId?: string
contracts?: bookcarsTypes.Contract[]
licenseRequired?: boolean
license?: string | null
minimumRentalDays?: number
expireAt?: Date
}
Expand Down Expand Up @@ -403,6 +419,8 @@ export interface UserInfo {
type?: string
blacklisted?: boolean
payLater?: boolean
licenseRequired?: boolean,
license?: string
}

/**
Expand Down
4 changes: 4 additions & 0 deletions api/src/config/userRoutes.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ const routes = {
verifyRecaptcha: '/api/verify-recaptcha/:token/:ip',
sendEmail: '/api/send-email',
hasPassword: '/api/has-password/:id',
createLicense: '/api/create-license',
updateLicense: '/api/update-license/:id',
deleteLicense: '/api/delete-license/:id',
deleteTempLicense: '/api/delete-temp-license/:file',
}

export default routes
15 changes: 15 additions & 0 deletions api/src/controllers/bookingController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Expo, ExpoPushMessage, ExpoPushTicket } from 'expo-server-sdk'
import { Request, Response } from 'express'
import nodemailer from 'nodemailer'
import path from 'node:path'
import fs from 'node:fs/promises'
import * as bookcarsTypes from ':bookcars-types'
import i18n from '../lang/i18n'
import Booking from '../models/Booking'
Expand Down Expand Up @@ -202,10 +203,24 @@ export const checkout = async (req: Request, res: Response) => {
driver.verified = false
driver.blacklisted = false
driver.type = bookcarsTypes.UserType.User
const { license } = driver
driver.license = null

user = new User(driver)
await user.save()

// create license
if (license) {
const tempLicense = path.join(env.CDN_TEMP_LICENSES, license)
if (await helper.exists(tempLicense)) {
const filename = `${user.id}${path.extname(tempLicense)}`
const filepath = path.join(env.CDN_LICENSES, filename)
await fs.rename(tempLicense, filepath)
user.license = filename
await user.save()
}
}

const token = new Token({ user: user._id, token: helper.generateToken() })
await token.save()

Expand Down
2 changes: 2 additions & 0 deletions api/src/controllers/carController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,12 +381,14 @@ export const getCar = async (req: Request, res: Response) => {
fullName,
avatar,
payLater,
licenseRequired,
} = car.supplier
car.supplier = {
_id,
fullName,
avatar,
payLater,
licenseRequired,
}

for (const location of car.locations) {
Expand Down
5 changes: 5 additions & 0 deletions api/src/controllers/supplierController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,15 @@ export const update = async (req: Request, res: Response) => {
location,
bio,
payLater,
licenseRequired,
minimumRentalDays,
} = body
supplier.fullName = fullName
supplier.phone = phone
supplier.location = location
supplier.bio = bio
supplier.payLater = payLater
supplier.licenseRequired = licenseRequired
supplier.minimumRentalDays = minimumRentalDays

await supplier.save()
Expand Down Expand Up @@ -188,6 +190,7 @@ export const getSupplier = async (req: Request, res: Response) => {
bio,
payLater,
contracts,
licenseRequired,
minimumRentalDays,
} = user

Expand All @@ -201,13 +204,15 @@ export const getSupplier = async (req: Request, res: Response) => {
bio,
payLater,
contracts,
licenseRequired,
minimumRentalDays,
})
} catch (err) {
logger.error(`[supplier.getSupplier] ${i18n.t('DB_ERROR')} ${id}`, err)
return res.status(400).send(i18n.t('DB_ERROR') + err)
}
}

/**
* Get Suppliers.
*
Expand Down
158 changes: 158 additions & 0 deletions api/src/controllers/userController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,7 @@ export const update = async (req: Request, res: Response) => {
birthDate,
enableEmailNotifications,
payLater,
licenseRequired,
minimumRentalDays,
} = body

Expand All @@ -966,6 +967,9 @@ export const update = async (req: Request, res: Response) => {
if (typeof payLater !== 'undefined') {
user.payLater = payLater
}
if (typeof licenseRequired !== 'undefined') {
user.licenseRequired = licenseRequired
}

await user.save()
return res.sendStatus(200)
Expand Down Expand Up @@ -1077,6 +1081,7 @@ export const getUser = async (req: Request, res: Response) => {
birthDate: 1,
payLater: 1,
customerId: 1,
licenseRequired: 1,
license: 1,
minimumRentalDays: 1,
}).lean()
Expand Down Expand Up @@ -1449,6 +1454,13 @@ export const deleteUsers = async (req: Request, res: Response) => {
}
}

if (user.license) {
const file = path.join(env.CDN_LICENSES, user.license)
if (await helper.exists(file)) {
await fs.unlink(file)
}
}

if (user.type === bookcarsTypes.UserType.Supplier) {
const additionalDrivers = (await Booking.find({ supplier: id, _additionalDriver: { $ne: null } }, { _id: 0, _additionalDriver: 1 })).map((b) => b._additionalDriver)
await AdditionalDriver.deleteMany({ _id: { $in: additionalDrivers } })
Expand Down Expand Up @@ -1569,3 +1581,149 @@ export const hasPassword = async (req: Request, res: Response) => {
return res.status(400).send(i18n.t('DB_ERROR') + err)
}
}

/**
* Upload a license to temp folder.
*
* @export
* @async
* @param {Request} req
* @param {Response} res
* @returns {unknown}
*/
export const createLicense = async (req: Request, res: Response) => {
try {
if (!req.file) {
throw new Error('req.file not found')
}
if (!req.file.originalname.includes('.')) {
throw new Error('File extension not found')
}

const filename = `${nanoid()}${path.extname(req.file.originalname)}`
const filepath = path.join(env.CDN_TEMP_LICENSES, filename)

await fs.writeFile(filepath, req.file.buffer)
return res.json(filename)
} catch (err) {
logger.error(`[user.createLicense] ${i18n.t('DB_ERROR')}`, err)
return res.status(400).send(i18n.t('ERROR') + err)
}
}

/**
* Update a license.
*
* @export
* @async
* @param {Request} req
* @param {Response} res
* @returns {unknown}
*/
export const updateLicense = async (req: Request, res: Response) => {
const { id } = req.params
const { file } = req

try {
if (!file) {
throw new Error('req.file not found')
}
if (!file.originalname.includes('.')) {
throw new Error('File extension not found')
}
if (!helper.isValidObjectId(id)) {
throw new Error('User Id not valid')
}

const user = await User.findOne({ _id: id, type: bookcarsTypes.UserType.User })

if (user) {
if (user.license) {
const licenseFile = path.join(env.CDN_LICENSES, user.license)
if (await helper.exists(licenseFile)) {
await fs.unlink(licenseFile)
}
}

const filename = `${user.id}${path.extname(file.originalname)}`
const filepath = path.join(env.CDN_LICENSES, filename)

await fs.writeFile(filepath, file.buffer)

user.license = filename
await user.save()
return res.json(filename)
}

return res.sendStatus(204)
} catch (err) {
logger.error(`[user.updateLicense] ${i18n.t('DB_ERROR')} ${id}`, err)
return res.status(400).send(i18n.t('DB_ERROR') + err)
}
}

/**
* Delete a license.
*
* @export
* @async
* @param {Request} req
* @param {Response} res
* @returns {unknown}
*/
export const deleteLicense = async (req: Request, res: Response) => {
const { id } = req.params

try {
if (!helper.isValidObjectId(id)) {
throw new Error('User Id not valid')
}

const user = await User.findOne({ _id: id, type: bookcarsTypes.UserType.User })

if (user) {
if (user.license) {
const licenseFile = path.join(env.CDN_LICENSES, user.license)
if (await helper.exists(licenseFile)) {
await fs.unlink(licenseFile)
}
user.license = null
}

await user.save()
return res.sendStatus(200)
}
return res.sendStatus(204)
} catch (err) {
logger.error(`[user.deleteLicense] ${i18n.t('DB_ERROR')} ${id}`, err)
return res.status(400).send(i18n.t('DB_ERROR') + err)
}
}

/**
* Delete a temp license.
*
* @export
* @async
* @param {Request} req
* @param {Response} res
* @returns {*}
*/
export const deleteTempLicense = async (req: Request, res: Response) => {
const { file } = req.params

try {
if (!file.includes('.')) {
throw new Error('Filename not valid')
}
const licenseFile = path.join(env.CDN_TEMP_LICENSES, file)
if (await helper.exists(licenseFile)) {
await fs.unlink(licenseFile)
}

res.sendStatus(200)
} catch (err) {
logger.error(`[user.deleteTempLicense] ${i18n.t('DB_ERROR')} ${file}`, err)
res.status(400).send(i18n.t('ERROR') + err)
}
}
2 changes: 1 addition & 1 deletion api/src/lang/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const en = {
ACCOUNT_ACTIVATION_SUBJECT: 'Account Activation',
HELLO: 'Hello ',
ACCOUNT_ACTIVATION_LINK: 'Please activate your account by clicking the link:',
REGARDS: 'Kind regards,<br>BookCars team',
REGARDS: 'Kind regards,<br>ErbilCarRent team',
ACCOUNT_ACTIVATION_TECHNICAL_ISSUE: 'Technical Issue! Please click on resend to validate your email.',
ACCOUNT_ACTIVATION_LINK_EXPIRED: 'Your validation link may have expired. Please click on resend to validate your email.',
ACCOUNT_ACTIVATION_LINK_ERROR: 'We were unable to find a user for this verification. Please Sign up.',
Expand Down
Loading

0 comments on commit 2c4328f

Please sign in to comment.