adds setup for roles
This commit is contained in:
parent
8d0b32f8df
commit
694f2318bd
@ -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
|
||||
|
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`
|
||||
|
||||
### 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
|
||||
|
@ -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 (
|
||||
|
@ -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 />} />
|
||||
|
@ -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",
|
||||
|
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 { 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[];
|
||||
}
|
||||
|
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 * 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();
|
||||
|
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"
|
||||
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"
|
||||
|
Loading…
Reference in New Issue
Block a user