adds setup for roles

This commit is contained in:
Joseph Ditton 2021-11-30 15:40:07 -07:00
parent 8d0b32f8df
commit 694f2318bd
12 changed files with 174 additions and 15 deletions

View File

@ -1,9 +1,10 @@
PORT=3000
NODE_ENV=development
USE_SSL=false
# in production this will be the full url
# but in dev it is the name of the database
DATABASE_URL=neststarterappdevelopement
DATABASE_URL=neststarterappdevelopment
# recommend using randomkeygen.com to generate a key
ENCRYPTION_KEY=yourencryptionkey

View File

@ -24,16 +24,21 @@ $ npm install -g yarn
Create a file in the root called `.env` and copy the contents of `.env.example`
### Dependencies
To install the dependencies run
To install the server dependencies run
```bash
$ yarn # this is same thing as `yarn install`
```
To install the client dependencies run
```bash
$ cd client && yarn && cd ..
```
### Database
Create the database
```bash
$ pc_ctl start # this starts postgres
$ createdb neststarterappdevelopement # creates a postgres database
$ pg_ctl start # starts postgres
$ createdb neststarterappdevelopment # creates a postgres database
```
Run the migrations
@ -44,24 +49,23 @@ yarn db:migrate
Migrations need to be run again everytime a new migration is created
### SSL
Create a ssl key and certificate an place them in the root directory
Create a ssl key and certificate and place them in the root directory
```bash
$ openssl req -x509 -newkey rsa:4096 -keyout private-key.pem -out public-cert.pem -sha256 -nodes
```
Where this key will only be used for development you can leave all of the information blank.
Enter `US` for the country code. Where this key will only be used for development you can leave all of the rest of information blank.
## Running the app
To start the server run
```bash
# development
$ yarn start
# watch mode
$ yarn start:dev
```
# production mode
$ yarn start:prod
To start the client run
```bash
$ yarn client:watch
```
## Test

View File

@ -26,6 +26,8 @@ export const App = () => {
setLoading(false);
}, []);
// before displaying anything try getting a token using cookies,
// can display a loading screen here if desired
if (loading) return null;
return (

View File

@ -12,7 +12,7 @@ export const Router = () => {
<Routes>
<Route
path="/"
element={authToken ? <Home /> : <Navigate replace to="signin" />} // no jwt means not logged in
element={authToken ? <Home /> : <Navigate replace to="signin" />} // no token means not logged in
/>
<Route path="signin" element={<SignIn />} />
<Route path="signup" element={<SignUp />} />

View File

@ -12,6 +12,7 @@
"db:migration:create": "cd server/database/migrations && typeorm migration:create -n ",
"db:migrate": "yarn db:start && yarn typeorm migration:run",
"db:migrate:undo": "yarn db:start && yarn typeorm migration:revert",
"db:seed": "cd server/database && ts-node -r tsconfig-paths/register seeds.ts",
"prebuild": "rimraf dist",
"build": "nest build && yarn client:build",
"format": "prettier --write \"server/**/*.ts\" \"test/**/*.ts\"",
@ -40,6 +41,7 @@
"dotenv": "^10.0.0",
"hbs": "^4.1.2",
"jsonwebtoken": "^8.5.1",
"morgan": "^1.10.0",
"pg": "^8.7.1",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",

View File

@ -0,0 +1,73 @@
import { MigrationInterface, QueryRunner, Table, TableForeignKey } from 'typeorm';
export class AddRoles1638307700052 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.createTable(
new Table({
name: 'role',
columns: [
{
name: 'id',
type: 'int',
isPrimary: true,
isGenerated: true,
},
{
name: 'key',
type: 'text',
isNullable: false,
},
],
}),
);
await queryRunner.createTable(
new Table({
name: 'user_role',
columns: [
{
name: 'id',
type: 'int',
isPrimary: true,
isGenerated: true,
},
{
name: 'userId',
type: 'int',
isNullable: false,
},
{
name: 'roleId',
type: 'int',
isNullable: false,
},
],
}),
);
await queryRunner.createForeignKey(
'user_role',
new TableForeignKey({
columnNames: ['userId'],
referencedColumnNames: ['id'],
referencedTableName: 'user',
onDelete: 'CASCADE',
}),
);
await queryRunner.createForeignKey(
'user_role',
new TableForeignKey({
columnNames: ['roleId'],
referencedColumnNames: ['id'],
referencedTableName: 'role',
onDelete: 'CASCADE',
}),
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropTable('user_role');
await queryRunner.dropTable('role');
}
}

View File

@ -0,0 +1,21 @@
import { Entity, PrimaryGeneratedColumn, OneToMany, Column } from 'typeorm';
import { UserRole } from './user_role.entity';
@Entity()
export class Role {
static ADMIN = 'admin';
static USER = 'user';
// make sure add additional roles to this arraylist as it
// will be used during seeds to initiallize all roles.
static ROLES = [Role.ADMIN, Role.USER];
@PrimaryGeneratedColumn()
id: number;
@Column()
key: string;
@OneToMany(() => UserRole, (userRole) => userRole.role)
userRoles: UserRole[];
}

View File

@ -1,5 +1,6 @@
import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from 'typeorm';
import { RefreshToken } from './refresh_token.entity';
import { UserRole } from './user_role.entity';
@Entity()
export class User {
@ -17,4 +18,7 @@ export class User {
@OneToMany(() => RefreshToken, (token) => token.user)
refreshTokens: RefreshToken[];
@OneToMany(() => UserRole, (userRole) => userRole.user)
userRoles: UserRole[];
}

View File

@ -0,0 +1,15 @@
import { Entity, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { Role } from './role.entity';
import { User } from './user.entity';
@Entity()
export class UserRole {
@PrimaryGeneratedColumn()
id: number;
@ManyToOne(() => Role, (role) => role.userRoles)
role: Role;
@ManyToOne(() => User, (user) => user.userRoles)
user: User;
}

View File

@ -1,14 +1,16 @@
import './env';
import * as fs from 'fs';
import { NestFactory } from '@nestjs/core';
import { Logger } from '@nestjs/common';
import { join } from 'path';
import { NestExpressApplication } from '@nestjs/platform-express';
import * as cookieParser from 'cookie-parser';
import { AppModule } from './app.module';
import * as morgan from 'morgan';
async function bootstrap() {
let httpsOptions;
if (process.env.NODE_ENV === 'development') {
if (process.env.USE_SSL === 'true') {
httpsOptions = {
key: fs.readFileSync('./private-key.pem'),
cert: fs.readFileSync('./public-cert.pem'),
@ -18,11 +20,18 @@ async function bootstrap() {
httpsOptions,
logger: ['verbose'],
});
app.use(cookieParser());
app.useStaticAssets(join(__dirname, '..', 'static'));
app.setBaseViewsDir(join(__dirname, '..', 'views'));
app.setViewEngine('hbs');
const logger = new Logger('Request');
app.use(
morgan('tiny', {
stream: {
write: (message) => logger.log(message.replace('\n', '')),
},
}),
);
await app.listen(process.env.PORT);
}
bootstrap();

View File

View File

@ -1499,6 +1499,13 @@ base64-js@^1.3.1:
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
basic-auth@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a"
integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==
dependencies:
safe-buffer "5.1.2"
bcrypt@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.0.1.tgz#f1a2c20f208e2ccdceea4433df0c8b2c54ecdf71"
@ -2054,6 +2061,11 @@ depd@~1.1.2:
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
depd@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
destroy@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
@ -4004,6 +4016,17 @@ mkdirp@^1.0.3, mkdirp@^1.0.4:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
morgan@^1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7"
integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==
dependencies:
basic-auth "~2.0.1"
debug "2.6.9"
depd "~2.0.0"
on-finished "~2.3.0"
on-headers "~1.0.2"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@ -4164,6 +4187,11 @@ on-finished@^2.3.0, on-finished@~2.3.0:
dependencies:
ee-first "1.1.1"
on-headers@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
once@^1.3.0, once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"