Skip to content

Merge hotfix/android into main #31

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 0 additions & 21 deletions .github/workflows/firebase-hosting-merge.yml

This file was deleted.

2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.

firebase-debug.log
*-debug.log
.firebase/*.cache
# api/firebase-service-account.json

Expand Down
63 changes: 11 additions & 52 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,16 @@
# Gatekeeper

<a alt="Nx logo" href="https://nx.dev" target="_blank" rel="noreferrer"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-logo.png" width="45"></a>

✨ **This workspace has been generated by [Nx, a Smart, fast and extensible build system.](https://nx.dev)** ✨

## Generate code

If you happen to use Nx plugins, you can leverage code generators that might come with it.

Run `nx list` to get a list of available plugins and whether they have generators. Then run `nx list <plugin-name>` to see what generators are available.

Learn more about [Nx generators on the docs](https://nx.dev/plugin-features/use-code-generators).

## Running tasks

To execute tasks with Nx use the following syntax:

```
nx <target> <project> <...options>
```
[![Build and test](https://github.com/CodeLog-Development/gatekeeper/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/CodeLog-Development/gatekeeper/actions/workflows/build-and-test.yml)

You can also run multiple targets:

```
nx run-many -t <target1> <target2>
```

..or add `-p` to filter specific projects

```
nx run-many -t <target1> <target2> -p <proj1> <proj2>
```

Targets can be defined in the `package.json` or `projects.json`. Learn more [in the docs](https://nx.dev/core-features/run-tasks).

## Want better Editor Integration?

Have a look at the [Nx Console extensions](https://nx.dev/nx-console). It provides autocomplete support, a UI for exploring and running tasks & generators, and more! Available for VSCode, IntelliJ and comes with a LSP for Vim users.

## Ready to deploy?

Just run `nx build demoapp` to build the application. The build artifacts will be stored in the `dist/` directory, ready to be deployed.

## Set up CI!
# Gatekeeper

Nx comes with local caching already built-in (check your `nx.json`). On CI you might want to go a step further.
## Building

- [Set up remote caching](https://nx.dev/core-features/share-your-cache)
- [Set up task distribution across multiple machines](https://nx.dev/core-features/distribute-task-execution)
- [Learn more how to setup CI](https://nx.dev/recipes/ci)
1. The first step is to run `yarn install` in the project root to download and build
all dependencies.
2. Next, you can run `yarn nx run app:build:production` and `yarn nx run api:build:production`
to build the projects.

## Connect with us!
## Development

- [Join the community](https://nx.dev/community)
- [Subscribe to the Nx Youtube Channel](https://www.youtube.com/@nxdevtools)
- [Follow us on Twitter](https://twitter.com/nxdevtools)
- To run the backend and frontend in a dev environment, start up the emulators for the backend using
`yarn nx run api:emulators`.
- You can then start the frontend with `yarn nx serve app`
7 changes: 4 additions & 3 deletions api/package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "@gatekeeper/api",
"version": "0.0.1",
"version": "0.1.0",
"dependencies": {
"@aws-sdk/client-ec2": "3.422.0",
"@aws-sdk/client-ec2": "^3.422.0",
"@nestjs/common": "^10.2.6",
"@nestjs/config": "^3.1.1",
"@nestjs/core": "^10.2.6",
Expand All @@ -16,7 +16,8 @@
"tslib": "^2.3.0"
},
"scripts": {
"serve": "cd .. && yarn nx build api && yarn firebase serve"
"serve": "cd .. && yarn nx build api && yarn firebase serve",
"emulators": "cd .. && yarn nx build api && yarn firebase emulators:start"
},
"type": "commonjs",
"main": "./src/index.js",
Expand Down
23 changes: 10 additions & 13 deletions api/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
"targets": {
"build": {
"executor": "@nx/js:tsc",
"outputs": [
"{options.outputPath}"
],
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/api",
"tsConfig": "api/tsconfig.lib.json",
Expand All @@ -27,21 +25,14 @@
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": [
"{options.outputFile}"
],
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": [
"api/**/*.ts",
"api/package.json"
]
"lintFilePatterns": ["api/**/*.ts", "api/package.json"]
}
},
"test": {
"executor": "@nx/jest:jest",
"outputs": [
"{workspaceRoot}/coverage/{projectRoot}"
],
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "api/jest.config.ts",
"passWithNoTests": true
Expand All @@ -58,6 +49,12 @@
"options": {
"script": "serve"
}
},
"emulators": {
"executor": "nx:run-script",
"options": {
"script": "emulators"
}
}
},
"tags": []
Expand Down
16 changes: 7 additions & 9 deletions api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,26 @@ export * from './lib/index';
const expressServer = express();

const createFunction = async (
expressInstance: express.Express
expressInstance: express.Express,
): Promise<void> => {
const app = await NestFactory.create(
ApiModule,
new ExpressAdapter(expressInstance),
{
cors: {
origin: [
'https://codelog-mc.web.app',
'https://us-central1-codelog-mc.cloudfunctions.net',
'http://localhost',
],
methods: ['GET', 'POST', 'OPTIONS', 'PUT', 'DELETE'],
origin: true,
methods: ['GET', 'POST', 'OPTIONS', 'PUT', 'DELETE', 'PATCH'],
credentials: true,
},
}
},
);
app.use(cookieParser());
await app.init();
};

export const api = functions.https.onRequest(async (request, response) => {
export const api = new functions.FunctionBuilder({
regions: ['europe-west2'],
}).https.onRequest(async (request, response) => {
await createFunction(expressServer);
expressServer(request, response);
});
7 changes: 7 additions & 0 deletions api/src/lib/api.interface.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import * as express from 'express';
import { User } from './user';

export interface ApiResponse {
success: boolean;
message: string | undefined;
}

export interface Request extends express.Request {
user?: User;
}
11 changes: 7 additions & 4 deletions api/src/lib/auth/auth.middleware.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Injectable, NestMiddleware } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
import { NextFunction, Request, Response } from 'express';
import { NextFunction, Response } from 'express';
import { UserService } from '../user/user.service';
import { User } from '../user';
import { Request } from '../api.interface';

@Injectable()
export class AuthenticationMiddleware implements NestMiddleware {
Expand All @@ -17,8 +19,9 @@ export class AuthenticationMiddleware implements NestMiddleware {
} else {
this.userService
.checkCookie(authCookie)
.then((isValid) => {
if (isValid) {
.then((user: User | undefined) => {
if (user?.verified) {
req.user = user;
next();
} else {
res.status(401).end();
Expand All @@ -27,7 +30,7 @@ export class AuthenticationMiddleware implements NestMiddleware {
.catch((e) => {
console.error(
' ~ auth.middleware.ts:25 → Failed to check cookie',
e
e,
);
res.status(500).end();
});
Expand Down
93 changes: 84 additions & 9 deletions api/src/lib/user/user.controller.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
import { Controller, Post, Body, Res } from '@nestjs/common';
import { AuthRequest, NewUser } from './user.interface';
import {
Controller,
Post,
Body,
Res,
Patch,
Get,
Param,
Req,
} from '@nestjs/common';
import {
AuthRequest,
ChangePasswordRequest,
NewUser,
UserInfoResponse,
validatePassword,
} from './user.interface';
import { UserService } from './user.service';
import { Response } from 'express';
import { Request } from '../api.interface';
import { ApiResponse } from '../api.interface';
import * as argon2 from 'argon2';

export const EMAIL_REGEX =
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
Expand All @@ -11,27 +28,85 @@ export const EMAIL_REGEX =
export class UserController {
constructor(private userService: UserService) { }

@Get('info')
async getUserInfo(@Req() request: Request): Promise<UserInfoResponse> {
console.log(' 🚀 ~ user.controller.ts:33 → User info', request.user);
if (!request.user) {
return { success: false, message: 'Not logged in' };
}
return {
success: true,
message: 'User info returned',
user: {
username: request.user.username,
email: request.user.email,
verified: request.user.verified,
},
};
}

@Patch('/password')
async changePassword(
@Body() input: ChangePasswordRequest,
): Promise<ApiResponse> {
const passwordValidation = validatePassword(input.newPassword);
if (passwordValidation !== undefined) {
return { success: false, message: passwordValidation };
}

if (input.newPassword !== input.confirm) {
return { success: false, message: "Confirmation password doesn't match" };
}

try {
const userRef = await this.userService.findUserByUsernameRef(
input.username,
);

const user = (await userRef?.get())?.data();

if (!user || !userRef) {
return {
success: false,
message: 'The specified user could not be found',
};
}

if (!(await argon2.verify(user.passwordHash, input.currentPassword))) {
return {
success: false,
message: 'The supplied current password was incorrect',
};
}

await this.userService.changePassword(userRef, input.newPassword);
return { success: true, message: 'Password updated' };
} catch (e) {
return { success: false, message: 'Failed to update password' };
}
}

@Post()
async createUser(
@Body() newUser: NewUser,
@Res({ passthrough: true }) res: Response
@Res({ passthrough: true }) res: Response,
): Promise<ApiResponse> {
try {
if (!EMAIL_REGEX.test(newUser.email)) {
return { success: false, message: 'Invalid email' };
}

const existingEmail = await this.userService.findUserByEmail(
newUser.email
newUser.email,
);
const existingUsername = await this.userService.findUserByUsername(
newUser.username
newUser.username,
);

console.log(
'🚀 ~ user.controller.ts:31 → Existing users (undefined is good): ',
existingEmail,
existingUsername
existingUsername,
);

if (existingEmail !== undefined || existingUsername !== undefined) {
Expand Down Expand Up @@ -65,10 +140,10 @@ export class UserController {
@Post('/login')
async login(
@Body() loginRequest: AuthRequest,
@Res({ passthrough: true }) res: Response
@Res({ passthrough: true }) res: Response,
): Promise<ApiResponse> {
const user = await this.userService.findUserByUsername(
loginRequest.username
loginRequest.username,
);

if (!user) {
Expand All @@ -81,7 +156,7 @@ export class UserController {

const result = await this.userService.authenticate(
loginRequest.username,
loginRequest.password
loginRequest.password,
);

if (result === undefined) {
Expand Down
Loading