summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.env.example3
-rw-r--r--README.md26
-rw-r--r--client/app.jsx2
-rw-r--r--client/components/router.jsx2
-rw-r--r--package.json2
-rw-r--r--server/database/migrations/1638307700052-AddRoles.ts73
-rw-r--r--server/entities/role.entity.ts21
-rw-r--r--server/entities/user.entity.ts4
-rw-r--r--server/entities/user_role.entity.ts15
-rw-r--r--server/main.ts13
-rw-r--r--server/providers/guards/roles.guard.ts0
-rw-r--r--yarn.lock28
12 files changed, 174 insertions, 15 deletions
diff --git a/.env.example b/.env.example
index 82059dc..2e8b00d 100644
--- a/.env.example
+++ b/.env.example
@@ -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
diff --git a/README.md b/README.md
index b069c1f..edb27da 100644
--- a/README.md
+++ b/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
diff --git a/client/app.jsx b/client/app.jsx
index a7de3cf..e1e4c03 100644
--- a/client/app.jsx
+++ b/client/app.jsx
@@ -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 (
diff --git a/client/components/router.jsx b/client/components/router.jsx
index fa552fc..ccdb83a 100644
--- a/client/components/router.jsx
+++ b/client/components/router.jsx
@@ -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 />} />
diff --git a/package.json b/package.json
index 27dc29c..b30d5b9 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/server/database/migrations/1638307700052-AddRoles.ts b/server/database/migrations/1638307700052-AddRoles.ts
new file mode 100644
index 0000000..237fb53
--- /dev/null
+++ b/server/database/migrations/1638307700052-AddRoles.ts
@@ -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');
+ }
+}
diff --git a/server/entities/role.entity.ts b/server/entities/role.entity.ts
new file mode 100644
index 0000000..35b4ac3
--- /dev/null
+++ b/server/entities/role.entity.ts
@@ -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[];
+}
diff --git a/server/entities/user.entity.ts b/server/entities/user.entity.ts
index 6ddbeeb..faf054f 100644
--- a/server/entities/user.entity.ts
+++ b/server/entities/user.entity.ts
@@ -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[];
}
diff --git a/server/entities/user_role.entity.ts b/server/entities/user_role.entity.ts
new file mode 100644
index 0000000..0a6c5c6
--- /dev/null
+++ b/server/entities/user_role.entity.ts
@@ -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;
+}
diff --git a/server/main.ts b/server/main.ts
index b4f319b..a8dc0ae 100644
--- a/server/main.ts
+++ b/server/main.ts
@@ -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();
diff --git a/server/providers/guards/roles.guard.ts b/server/providers/guards/roles.guard.ts
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/server/providers/guards/roles.guard.ts
diff --git a/yarn.lock b/yarn.lock
index 9de60e8..b3c2377 100644
--- a/yarn.lock
+++ b/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"