Skip to content

Commit

Permalink
feat(#19) user story view current locationas a driver i want to see m…
Browse files Browse the repository at this point in the history
…y current location on a map in real time so that i can track my position and navigate the area effectively 1 backend (#27)

* feat(#19): view current location as a driver i want to see my current location on a map in real time so that i can track my position and navigate the area effectively

- refactor docker (separate mqtt listener and api cause they don't need to be together)
- moved docker to root directory
- using docker base to reuse common code
- basic nearby drivers util

* feat(#19): view current location as a driver i want to see my current location on a map in real time so that i can track my position and navigate the area effectively
- adding JWT auth token support (refresh token, auth token)

* feat(#19): view current location as a driver i want to see my current location on a map in real time so that i can track my position and navigate the area effectively
     - modifying map screen ui
     - using auth token as channel name when publishing location
     - changes in store
     - test screen (token display)
  • Loading branch information
chriscoderdr authored Nov 2, 2024
1 parent 27618ae commit 1046728
Show file tree
Hide file tree
Showing 56 changed files with 4,698 additions and 426 deletions.
6 changes: 6 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
**/node_modules
.yarn
.yarnrc.yml
dist
.git
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ Thumbs.db

.nx/cache
.nx/workspace-data
apps/api/logs/app.log
11 changes: 11 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"trailingComma": "none",
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"endOfLine": "lf",
"printWidth": 80,
"proseWrap": "never",
"trimTrailingWhitespace": true
}
9 changes: 9 additions & 0 deletions Dockerfile.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# syntax = edrevo/dockerfile-plus

INCLUDE+ Dockerfile.base

EXPOSE 3000

WORKDIR /app/apps/api

CMD ["yarn", "dev"]
14 changes: 14 additions & 0 deletions Dockerfile.base
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM node:18-alpine

RUN apk add --no-cache git

WORKDIR /app

COPY package.json yarn.lock ./

COPY . .

WORKDIR /app/apps/api

RUN yarn install --frozen-lockfile

9 changes: 9 additions & 0 deletions Dockerfile.mqtt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# syntax = edrevo/dockerfile-plus

INCLUDE+ Dockerfile.base

EXPOSE 1883

WORKDIR /app/apps/api

CMD ["yarn", "start-mqtt"]
3 changes: 0 additions & 3 deletions apps/api/.dockerignore

This file was deleted.

14 changes: 0 additions & 14 deletions apps/api/Dockerfile

This file was deleted.

58 changes: 0 additions & 58 deletions apps/api/docker-compose.yml

This file was deleted.

13 changes: 10 additions & 3 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"test": "mocha -r ts-node/register test/**/*.test.ts",
"build": "tsc",
"start": "node --es-module-specifier-resolution=node",
"dev": "concurrently \"yarn build\" \"nodemon --es-module-specifier-resolution=node\""
"dev": "tsx watch src/start.ts",
"start-mqtt": "tsx watch src/start-mqtt.ts"
},
"keywords": [],
"author": "",
Expand All @@ -17,19 +18,24 @@
"bcrypt": "^5.1.1",
"bcryptjs": "^2.4.3",
"ioredis": "^5.4.1",
"jsonwebtoken": "^9.0.2",
"koa": "^2.15.3",
"koa-bodyparser": "^4.4.1",
"mqtt": "^5.10.1",
"pg": "^8.13.1",
"pg-hstore": "^2.3.4",
"sequelize": "^6.37.5",
"ts-node": "^10.9.2",
"twilio": "^5.3.5",
"typescript": "^5.6.3"
"typescript": "^5.6.3",
"uuid": "^11.0.2",
"winston": "^3.15.0"
},
"devDependencies": {
"@types/bcrypt": "^5.0.2",
"@types/bcryptjs": "^2.4.6",
"@types/chai": "^5.0.1",
"@types/jsonwebtoken": "^9.0.7",
"@types/koa": "^2.15.0",
"@types/koa-bodyparser": "^4.3.12",
"@types/koa__router": "^12.0.4",
Expand All @@ -44,6 +50,7 @@
"eslint": "^9.13.0",
"mocha": "^10.7.3",
"nodemon": "^3.1.7",
"supertest": "^7.0.0"
"supertest": "^7.0.0",
"tsx": "^4.19.2"
}
}
7 changes: 4 additions & 3 deletions apps/api/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@ import Koa from 'koa';
import bodyParser from 'koa-bodyparser';
import sequelize from './config/database';
import driverRoutes from './routes';
import logger from './utils/logger';

const app = new Koa();

sequelize.sync().then(() => console.log("Connected to PostgreSQL"));
sequelize.sync().then(() => logger.info('Connected to PostgreSQL'));

app.use(bodyParser());

// Temporary test route
// TODO: remove this route
app.use(async (ctx, next) => {
if (ctx.path === '/') {
ctx.body = 'Server is up!';
} else {
console.log('Request received:', ctx.method, ctx.path);
logger.info('Request received:', ctx.method, ctx.path);
await next();
}
});
Expand Down
26 changes: 20 additions & 6 deletions apps/api/src/controllers/driver-controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Context } from 'koa';
import { Op } from 'sequelize';
import Driver from '../models/driver_tmp';
import Driver from '../models/driver';
import logger from '../utils/logger';
import { generateAccessToken, generateRefreshToken } from '../utils/token-utils';

export const registerDriver = async (ctx: Context) => {
const { name, email, phone, password } = ctx.request.body as {
Expand All @@ -9,12 +11,10 @@ export const registerDriver = async (ctx: Context) => {
phone: string;
password: string;
};
console.log('registerDriver', name, email, phone, password);

if (!name || !email || !phone || !password) {
console.log(`registerDriver: ${name}, ${email}, ${phone}, ${password}`);
ctx.status = 400;
ctx.body = { error: `registerDriver: ${name}, ${email}, ${phone}, ${password}`};
ctx.body = { error: 'All fields are required.' };
return;
}
if (password.length < 8) {
Expand All @@ -33,11 +33,25 @@ export const registerDriver = async (ctx: Context) => {
return;
}

// Create the new driver
const newDriver = await Driver.create({ name, email, phone, password });

// Generate tokens
const accessToken = generateAccessToken(newDriver.dataValues.id);
const refreshToken = generateRefreshToken();

// Store the refresh token in the database
await newDriver.update({ refreshToken });

ctx.status = 201;
ctx.body = { message: 'Driver registered successfully.', driverId: newDriver.dataValues.id };
ctx.body = {
message: 'Driver registered successfully.',
driverId: newDriver.dataValues.id,
accessToken,
refreshToken
};
} catch (error) {
console.error(error);
logger.error(error);
ctx.status = 500;
ctx.body = { error: 'Server error. Please try again later.' };
}
Expand Down
23 changes: 23 additions & 0 deletions apps/api/src/controllers/token-controlller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Context } from 'koa';
import Driver from '../models/driver';
import { generateAccessToken } from '../utils/token-utils';

export const refreshAccessToken = async (ctx: Context) => {
const { refreshToken } = ctx.request.body as any;
if (!refreshToken) {
ctx.status = 400;
ctx.body = { error: 'Refresh token is required.' };
return;
}

const driver = await Driver.findOne({ where: { refreshToken } });
if (!driver) {
ctx.status = 403;
ctx.body = { error: 'Invalid refresh token.' };
return;
}

const accessToken = generateAccessToken(driver.dataValues.id);
ctx.status = 200;
ctx.body = { accessToken };
};
29 changes: 29 additions & 0 deletions apps/api/src/middleware/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import jwt from 'jsonwebtoken';
import { Context, Next } from 'koa';
import Driver from '../models/driver';

const ACCESS_TOKEN_SECRET = process.env.ACCESS_TOKEN_SECRET || 'access-secret';

export const authenticateToken = async (ctx: Context, next: Next) => {
const token = ctx.headers['authorization']?.split(' ')[1];
if (!token) {
ctx.status = 401;
ctx.body = { error: 'Access token required.' };
return;
}

try {
const decoded = jwt.verify(token, ACCESS_TOKEN_SECRET) as { driverId: number };
const driver = await Driver.findByPk(decoded.driverId);
if (!driver) {
ctx.status = 403;
ctx.body = { error: 'Invalid access token.' };
return;
}
ctx.state.driver = driver;
await next();
} catch (error) {
ctx.status = 403;
ctx.body = { error: 'Invalid or expired access token.' };
}
};
Loading

0 comments on commit 1046728

Please sign in to comment.