adds setup for roles
This commit is contained in:
parent
8d0b32f8df
commit
694f2318bd
@ -1,9 +1,10 @@
|
|||||||
PORT=3000
|
PORT=3000
|
||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
|
USE_SSL=false
|
||||||
|
|
||||||
# in production this will be the full url
|
# in production this will be the full url
|
||||||
# but in dev it is the name of the database
|
# but in dev it is the name of the database
|
||||||
DATABASE_URL=neststarterappdevelopement
|
DATABASE_URL=neststarterappdevelopment
|
||||||
|
|
||||||
# recommend using randomkeygen.com to generate a key
|
# recommend using randomkeygen.com to generate a key
|
||||||
ENCRYPTION_KEY=yourencryptionkey
|
ENCRYPTION_KEY=yourencryptionkey
|
||||||
|
26
README.md
26
README.md
@ -24,16 +24,21 @@ $ npm install -g yarn
|
|||||||
Create a file in the root called `.env` and copy the contents of `.env.example`
|
Create a file in the root called `.env` and copy the contents of `.env.example`
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
To install the dependencies run
|
To install the server dependencies run
|
||||||
```bash
|
```bash
|
||||||
$ yarn # this is same thing as `yarn install`
|
$ yarn # this is same thing as `yarn install`
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To install the client dependencies run
|
||||||
|
```bash
|
||||||
|
$ cd client && yarn && cd ..
|
||||||
|
```
|
||||||
|
|
||||||
### Database
|
### Database
|
||||||
Create the database
|
Create the database
|
||||||
```bash
|
```bash
|
||||||
$ pc_ctl start # this starts postgres
|
$ pg_ctl start # starts postgres
|
||||||
$ createdb neststarterappdevelopement # creates a postgres database
|
$ createdb neststarterappdevelopment # creates a postgres database
|
||||||
```
|
```
|
||||||
|
|
||||||
Run the migrations
|
Run the migrations
|
||||||
@ -44,24 +49,23 @@ yarn db:migrate
|
|||||||
Migrations need to be run again everytime a new migration is created
|
Migrations need to be run again everytime a new migration is created
|
||||||
|
|
||||||
### SSL
|
### 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
|
```bash
|
||||||
$ openssl req -x509 -newkey rsa:4096 -keyout private-key.pem -out public-cert.pem -sha256 -nodes
|
$ 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
|
## Running the app
|
||||||
|
To start the server run
|
||||||
```bash
|
```bash
|
||||||
# development
|
|
||||||
$ yarn start
|
|
||||||
|
|
||||||
# watch mode
|
# watch mode
|
||||||
$ yarn start:dev
|
$ yarn start:dev
|
||||||
|
```
|
||||||
|
|
||||||
# production mode
|
To start the client run
|
||||||
$ yarn start:prod
|
```bash
|
||||||
|
$ yarn client:watch
|
||||||
```
|
```
|
||||||
|
|
||||||
## Test
|
## Test
|
||||||
|
@ -26,6 +26,8 @@ export const App = () => {
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// before displaying anything try getting a token using cookies,
|
||||||
|
// can display a loading screen here if desired
|
||||||
if (loading) return null;
|
if (loading) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -12,7 +12,7 @@ export const Router = () => {
|
|||||||
<Routes>
|
<Routes>
|
||||||
<Route
|
<Route
|
||||||
path="/"
|
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="signin" element={<SignIn />} />
|
||||||
<Route path="signup" element={<SignUp />} />
|
<Route path="signup" element={<SignUp />} />
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
"db:migration:create": "cd server/database/migrations && typeorm migration:create -n ",
|
"db:migration:create": "cd server/database/migrations && typeorm migration:create -n ",
|
||||||
"db:migrate": "yarn db:start && yarn typeorm migration:run",
|
"db:migrate": "yarn db:start && yarn typeorm migration:run",
|
||||||
"db:migrate:undo": "yarn db:start && yarn typeorm migration:revert",
|
"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",
|
"prebuild": "rimraf dist",
|
||||||
"build": "nest build && yarn client:build",
|
"build": "nest build && yarn client:build",
|
||||||
"format": "prettier --write \"server/**/*.ts\" \"test/**/*.ts\"",
|
"format": "prettier --write \"server/**/*.ts\" \"test/**/*.ts\"",
|
||||||
@ -40,6 +41,7 @@
|
|||||||
"dotenv": "^10.0.0",
|
"dotenv": "^10.0.0",
|
||||||
"hbs": "^4.1.2",
|
"hbs": "^4.1.2",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
|
"morgan": "^1.10.0",
|
||||||
"pg": "^8.7.1",
|
"pg": "^8.7.1",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
|
73
server/database/migrations/1638307700052-AddRoles.ts
Normal file
73
server/database/migrations/1638307700052-AddRoles.ts
Normal 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');
|
||||||
|
}
|
||||||
|
}
|
21
server/entities/role.entity.ts
Normal file
21
server/entities/role.entity.ts
Normal 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[];
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from 'typeorm';
|
import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from 'typeorm';
|
||||||
import { RefreshToken } from './refresh_token.entity';
|
import { RefreshToken } from './refresh_token.entity';
|
||||||
|
import { UserRole } from './user_role.entity';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class User {
|
export class User {
|
||||||
@ -17,4 +18,7 @@ export class User {
|
|||||||
|
|
||||||
@OneToMany(() => RefreshToken, (token) => token.user)
|
@OneToMany(() => RefreshToken, (token) => token.user)
|
||||||
refreshTokens: RefreshToken[];
|
refreshTokens: RefreshToken[];
|
||||||
|
|
||||||
|
@OneToMany(() => UserRole, (userRole) => userRole.user)
|
||||||
|
userRoles: UserRole[];
|
||||||
}
|
}
|
||||||
|
15
server/entities/user_role.entity.ts
Normal file
15
server/entities/user_role.entity.ts
Normal 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;
|
||||||
|
}
|
@ -1,14 +1,16 @@
|
|||||||
import './env';
|
import './env';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import { NestFactory } from '@nestjs/core';
|
import { NestFactory } from '@nestjs/core';
|
||||||
|
import { Logger } from '@nestjs/common';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { NestExpressApplication } from '@nestjs/platform-express';
|
import { NestExpressApplication } from '@nestjs/platform-express';
|
||||||
import * as cookieParser from 'cookie-parser';
|
import * as cookieParser from 'cookie-parser';
|
||||||
import { AppModule } from './app.module';
|
import { AppModule } from './app.module';
|
||||||
|
import * as morgan from 'morgan';
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
let httpsOptions;
|
let httpsOptions;
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.USE_SSL === 'true') {
|
||||||
httpsOptions = {
|
httpsOptions = {
|
||||||
key: fs.readFileSync('./private-key.pem'),
|
key: fs.readFileSync('./private-key.pem'),
|
||||||
cert: fs.readFileSync('./public-cert.pem'),
|
cert: fs.readFileSync('./public-cert.pem'),
|
||||||
@ -18,11 +20,18 @@ async function bootstrap() {
|
|||||||
httpsOptions,
|
httpsOptions,
|
||||||
logger: ['verbose'],
|
logger: ['verbose'],
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(cookieParser());
|
app.use(cookieParser());
|
||||||
app.useStaticAssets(join(__dirname, '..', 'static'));
|
app.useStaticAssets(join(__dirname, '..', 'static'));
|
||||||
app.setBaseViewsDir(join(__dirname, '..', 'views'));
|
app.setBaseViewsDir(join(__dirname, '..', 'views'));
|
||||||
app.setViewEngine('hbs');
|
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);
|
await app.listen(process.env.PORT);
|
||||||
}
|
}
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
0
server/providers/guards/roles.guard.ts
Normal file
0
server/providers/guards/roles.guard.ts
Normal file
28
yarn.lock
28
yarn.lock
@ -1499,6 +1499,13 @@ base64-js@^1.3.1:
|
|||||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||||
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
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:
|
bcrypt@^5.0.1:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.0.1.tgz#f1a2c20f208e2ccdceea4433df0c8b2c54ecdf71"
|
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"
|
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
|
||||||
integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
|
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:
|
destroy@~1.0.4:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
|
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"
|
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
||||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
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:
|
ms@2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
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:
|
dependencies:
|
||||||
ee-first "1.1.1"
|
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:
|
once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||||
|
Loading…
Reference in New Issue
Block a user