webpack frontend stuff, jwt start, settings start
This commit is contained in:
parent
dc7f4215af
commit
d7fc152c8a
1
.gitignore
vendored
1
.gitignore
vendored
@ -38,6 +38,7 @@ lerna-debug.log*
|
|||||||
.env
|
.env
|
||||||
/devdocker
|
/devdocker
|
||||||
/config*.toml
|
/config*.toml
|
||||||
|
/private
|
||||||
|
|
||||||
# front-end items
|
# front-end items
|
||||||
/public/js
|
/public/js
|
||||||
|
3669
package-lock.json
generated
3669
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
17
package.json
17
package.json
@ -8,13 +8,13 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"prebuild": "rimraf dist",
|
"prebuild": "rimraf dist",
|
||||||
"build": "nest build && npm run build:fe",
|
"build": "nest build && npm run build:fe",
|
||||||
"build:fe": "rimraf public/css && sass --no-source-map --style=compressed src/scss/_index.scss:public/css/index.css",
|
"build:fe": "rimraf public/css public/js && NODE_ENV=production webpack --mode=production",
|
||||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||||
"start": "nest start",
|
"start": "nest start",
|
||||||
"start:dev": "nest start --watch",
|
"start:dev": "nest start --watch",
|
||||||
"start:debug": "nest start --debug --watch",
|
"start:debug": "nest start --debug --watch",
|
||||||
"start:prod": "node dist/main",
|
"start:prod": "node dist/main",
|
||||||
"start:fe": "rimraf public/css && sass --watch --update --style=expanded src/scss/_index.scss:public/css/index.css",
|
"start:fe": "rimraf public/css public/js && webpack --mode=development -w",
|
||||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
@ -33,6 +33,7 @@
|
|||||||
"class-validator": "^0.13.2",
|
"class-validator": "^0.13.2",
|
||||||
"dotenv": "^16.0.0",
|
"dotenv": "^16.0.0",
|
||||||
"express-session": "^1.17.2",
|
"express-session": "^1.17.2",
|
||||||
|
"jsonwebtoken": "^8.5.1",
|
||||||
"mysql2": "^2.3.3",
|
"mysql2": "^2.3.3",
|
||||||
"nodemailer": "^6.7.2",
|
"nodemailer": "^6.7.2",
|
||||||
"otplib": "^12.0.1",
|
"otplib": "^12.0.1",
|
||||||
@ -46,6 +47,8 @@
|
|||||||
"uuid": "^8.3.2"
|
"uuid": "^8.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/preset-env": "^7.16.11",
|
||||||
|
"@babel/preset-typescript": "^7.16.7",
|
||||||
"@nestjs/cli": "^8.0.0",
|
"@nestjs/cli": "^8.0.0",
|
||||||
"@nestjs/schematics": "^8.0.0",
|
"@nestjs/schematics": "^8.0.0",
|
||||||
"@nestjs/testing": "^8.0.0",
|
"@nestjs/testing": "^8.0.0",
|
||||||
@ -53,6 +56,7 @@
|
|||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
"@types/express-session": "^1.17.4",
|
"@types/express-session": "^1.17.4",
|
||||||
"@types/jest": "27.4.1",
|
"@types/jest": "27.4.1",
|
||||||
|
"@types/jsonwebtoken": "^8.5.8",
|
||||||
"@types/node": "^16.0.0",
|
"@types/node": "^16.0.0",
|
||||||
"@types/nodemailer": "^6.4.4",
|
"@types/nodemailer": "^6.4.4",
|
||||||
"@types/qrcode": "^1.4.2",
|
"@types/qrcode": "^1.4.2",
|
||||||
@ -60,19 +64,26 @@
|
|||||||
"@types/uuid": "^8.3.4",
|
"@types/uuid": "^8.3.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||||
"@typescript-eslint/parser": "^5.0.0",
|
"@typescript-eslint/parser": "^5.0.0",
|
||||||
|
"babel-loader": "^8.2.3",
|
||||||
|
"css-loader": "^6.7.1",
|
||||||
"eslint": "^8.0.1",
|
"eslint": "^8.0.1",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"jest": "^27.2.5",
|
"jest": "^27.2.5",
|
||||||
|
"mini-css-extract-plugin": "^2.6.0",
|
||||||
"prettier": "^2.3.2",
|
"prettier": "^2.3.2",
|
||||||
"sass": "^1.49.9",
|
"sass": "^1.49.9",
|
||||||
|
"sass-loader": "^12.6.0",
|
||||||
"source-map-support": "^0.5.20",
|
"source-map-support": "^0.5.20",
|
||||||
"supertest": "^6.1.3",
|
"supertest": "^6.1.3",
|
||||||
|
"text-loader": "^0.0.1",
|
||||||
"ts-jest": "^27.0.3",
|
"ts-jest": "^27.0.3",
|
||||||
"ts-loader": "^9.2.3",
|
"ts-loader": "^9.2.3",
|
||||||
"ts-node": "^10.0.0",
|
"ts-node": "^10.0.0",
|
||||||
"tsconfig-paths": "^3.10.1",
|
"tsconfig-paths": "^3.10.1",
|
||||||
"typescript": "^4.3.5"
|
"typescript": "^4.3.5",
|
||||||
|
"webpack": "^5.70.0",
|
||||||
|
"webpack-cli": "^4.9.2"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"moduleFileExtensions": [
|
"moduleFileExtensions": [
|
||||||
|
@ -8,6 +8,7 @@ import { ConfigurationModule } from './modules/config/config.module';
|
|||||||
import { LoginModule } from './modules/features/login/login.module';
|
import { LoginModule } from './modules/features/login/login.module';
|
||||||
import { OAuth2Module } from './modules/features/oauth2/oauth2.module';
|
import { OAuth2Module } from './modules/features/oauth2/oauth2.module';
|
||||||
import { RegisterModule } from './modules/features/register/register.module';
|
import { RegisterModule } from './modules/features/register/register.module';
|
||||||
|
import { SettingsModule } from './modules/features/settings/settings.module';
|
||||||
import { TwoFactorModule } from './modules/features/two-factor/two-factor.module';
|
import { TwoFactorModule } from './modules/features/two-factor/two-factor.module';
|
||||||
import { DatabaseModule } from './modules/objects/database/database.module';
|
import { DatabaseModule } from './modules/objects/database/database.module';
|
||||||
import { EmailModule } from './modules/objects/email/email.module';
|
import { EmailModule } from './modules/objects/email/email.module';
|
||||||
@ -35,6 +36,7 @@ import { UtilityModule } from './modules/utility/utility.module';
|
|||||||
RegisterModule,
|
RegisterModule,
|
||||||
OAuth2Module,
|
OAuth2Module,
|
||||||
TwoFactorModule,
|
TwoFactorModule,
|
||||||
|
SettingsModule,
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [AppService, CSRFMiddleware],
|
providers: [AppService, CSRFMiddleware],
|
||||||
|
47
src/fe/scss/_settings.scss
Normal file
47
src/fe/scss/_settings.scss
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
.settings {
|
||||||
|
max-width: 1000px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
&__nav {
|
||||||
|
padding: 2rem 0rem;
|
||||||
|
background-color: #005b74;
|
||||||
|
ul {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 1rem;
|
||||||
|
border-right: 4px solid transparent;
|
||||||
|
transition: outline 0.15s linear;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active,
|
||||||
|
&:focus-visible {
|
||||||
|
border-right-color: #519eb9;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 4px solid #00c0ff8a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
padding: 2.75rem;
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@
|
|||||||
@import 'flex';
|
@import 'flex';
|
||||||
@import 'alert';
|
@import 'alert';
|
||||||
@import 'authorize';
|
@import 'authorize';
|
||||||
|
@import 'settings';
|
||||||
|
|
||||||
*,
|
*,
|
||||||
*::before,
|
*::before,
|
0
src/fe/ts/index.ts
Normal file
0
src/fe/ts/index.ts
Normal file
@ -8,6 +8,12 @@ export interface SMTPConfiguration {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface JWTConfiguration {
|
||||||
|
algorithm: string;
|
||||||
|
issuer: string;
|
||||||
|
expiration: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface EmailConfiguration {
|
export interface EmailConfiguration {
|
||||||
from: string;
|
from: string;
|
||||||
smtp: SMTPConfiguration;
|
smtp: SMTPConfiguration;
|
||||||
@ -22,4 +28,5 @@ export interface AppConfiguration {
|
|||||||
export interface Configuration {
|
export interface Configuration {
|
||||||
app: AppConfiguration;
|
app: AppConfiguration;
|
||||||
email: EmailConfiguration;
|
email: EmailConfiguration;
|
||||||
|
jwt: JWTConfiguration;
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,11 @@ export const configProviders = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
jwt: {
|
||||||
|
algorithm: 'RS256',
|
||||||
|
issuer: 'localhost',
|
||||||
|
expiration: 3600,
|
||||||
|
},
|
||||||
} as Configuration,
|
} as Configuration,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -18,8 +18,8 @@ export class LoginModule implements NestModule {
|
|||||||
configure(consumer: MiddlewareConsumer) {
|
configure(consumer: MiddlewareConsumer) {
|
||||||
consumer
|
consumer
|
||||||
.apply(ValidateCSRFMiddleware)
|
.apply(ValidateCSRFMiddleware)
|
||||||
.forRoutes({ path: '*', method: RequestMethod.POST });
|
.forRoutes({ path: 'login*', method: RequestMethod.POST });
|
||||||
|
|
||||||
consumer.apply(FlashMiddleware).forRoutes('*');
|
consumer.apply(FlashMiddleware).forRoutes('login*');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,10 +79,16 @@ export class OAuth2Controller {
|
|||||||
uuid: user.uuid,
|
uuid: user.uuid,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
display_name: user.display_name,
|
display_name: user.display_name,
|
||||||
|
|
||||||
|
// Standard claims
|
||||||
|
name: user.display_name,
|
||||||
|
preferred_username: user.username,
|
||||||
|
nickname: user.display_name,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (token.scope.includes('email') || token.scope.includes('user:email')) {
|
if (token.scope.includes('email') || token.scope.includes('user:email')) {
|
||||||
userData.email = user.email;
|
userData.email = user.email;
|
||||||
|
userData.email_verified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -17,8 +17,8 @@ export class RegisterModule implements NestModule {
|
|||||||
configure(consumer: MiddlewareConsumer) {
|
configure(consumer: MiddlewareConsumer) {
|
||||||
consumer
|
consumer
|
||||||
.apply(ValidateCSRFMiddleware)
|
.apply(ValidateCSRFMiddleware)
|
||||||
.forRoutes({ path: '*', method: RequestMethod.POST });
|
.forRoutes({ path: 'register*', method: RequestMethod.POST });
|
||||||
|
|
||||||
consumer.apply(FlashMiddleware).forRoutes('*');
|
consumer.apply(FlashMiddleware).forRoutes('register*');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
32
src/modules/features/settings/settings.controller.ts
Normal file
32
src/modules/features/settings/settings.controller.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
Redirect,
|
||||||
|
Render,
|
||||||
|
Req,
|
||||||
|
Session,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { Request } from 'express';
|
||||||
|
import { SessionData } from 'express-session';
|
||||||
|
import { FormUtilityService } from 'src/modules/utility/services/form-utility.service';
|
||||||
|
import { SettingsService } from './settings.service';
|
||||||
|
|
||||||
|
@Controller('/account')
|
||||||
|
export class SettingsController {
|
||||||
|
constructor(
|
||||||
|
private readonly _service: SettingsService,
|
||||||
|
private readonly _form: FormUtilityService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
@Redirect('/account/general')
|
||||||
|
public redirectGeneral() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('general')
|
||||||
|
@Render('settings/general')
|
||||||
|
public general(@Req() req: Request, @Session() sess: SessionData) {
|
||||||
|
return this._form.populateTemplate(req, sess);
|
||||||
|
}
|
||||||
|
}
|
34
src/modules/features/settings/settings.module.ts
Normal file
34
src/modules/features/settings/settings.module.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import {
|
||||||
|
MiddlewareConsumer,
|
||||||
|
Module,
|
||||||
|
NestModule,
|
||||||
|
RequestMethod,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { AuthMiddleware } from 'src/middleware/auth.middleware';
|
||||||
|
import { FlashMiddleware } from 'src/middleware/flash.middleware';
|
||||||
|
import { ValidateCSRFMiddleware } from 'src/middleware/validate-csrf.middleware';
|
||||||
|
import { ConfigurationModule } from 'src/modules/config/config.module';
|
||||||
|
import { UserModule } from 'src/modules/objects/user/user.module';
|
||||||
|
import { OAuth2Module } from '../oauth2/oauth2.module';
|
||||||
|
import { SettingsController } from './settings.controller';
|
||||||
|
import { SettingsService } from './settings.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [SettingsController],
|
||||||
|
imports: [ConfigurationModule, UserModule, OAuth2Module],
|
||||||
|
providers: [SettingsService],
|
||||||
|
})
|
||||||
|
export class SettingsModule implements NestModule {
|
||||||
|
configure(consumer: MiddlewareConsumer) {
|
||||||
|
consumer
|
||||||
|
.apply(ValidateCSRFMiddleware)
|
||||||
|
.forRoutes(
|
||||||
|
{ path: '/account*', method: RequestMethod.POST },
|
||||||
|
{ path: '/account*', method: RequestMethod.PATCH },
|
||||||
|
{ path: '/account*', method: RequestMethod.DELETE },
|
||||||
|
);
|
||||||
|
|
||||||
|
consumer.apply(AuthMiddleware).forRoutes('account*');
|
||||||
|
consumer.apply(FlashMiddleware).forRoutes('account*');
|
||||||
|
}
|
||||||
|
}
|
11
src/modules/features/settings/settings.service.ts
Normal file
11
src/modules/features/settings/settings.service.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { ConfigurationService } from 'src/modules/config/config.service';
|
||||||
|
import { UserService } from 'src/modules/objects/user/user.service';
|
||||||
|
import { OAuth2Service } from '../oauth2/oauth2.service';
|
||||||
|
|
||||||
|
export class SettingsService {
|
||||||
|
constructor(
|
||||||
|
public user: UserService,
|
||||||
|
public oauth: OAuth2Service,
|
||||||
|
public config: ConfigurationService,
|
||||||
|
) {}
|
||||||
|
}
|
@ -11,7 +11,7 @@ import { TwoFactorController } from './two-factor.controller';
|
|||||||
})
|
})
|
||||||
export class TwoFactorModule implements NestModule {
|
export class TwoFactorModule implements NestModule {
|
||||||
configure(consumer: MiddlewareConsumer) {
|
configure(consumer: MiddlewareConsumer) {
|
||||||
consumer.apply(AuthMiddleware).forRoutes('/account/two-factor/activate');
|
consumer.apply(AuthMiddleware).forRoutes('account/two-factor/activate');
|
||||||
consumer.apply(FlashMiddleware).forRoutes('*');
|
consumer.apply(FlashMiddleware).forRoutes('account/two-factor/activate');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
11
src/modules/jwt/jwt.module.ts
Normal file
11
src/modules/jwt/jwt.module.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { ConfigurationModule } from '../config/config.module';
|
||||||
|
import { jwtProviders } from './jwt.providers';
|
||||||
|
import { JWTService } from './jwt.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [ConfigurationModule],
|
||||||
|
providers: [...jwtProviders, JWTService],
|
||||||
|
exports: [JWTService],
|
||||||
|
})
|
||||||
|
export class JWTModule {}
|
19
src/modules/jwt/jwt.providers.ts
Normal file
19
src/modules/jwt/jwt.providers.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { join } from 'path';
|
||||||
|
import { readFile } from 'fs/promises';
|
||||||
|
|
||||||
|
export const jwtProviders = [
|
||||||
|
{
|
||||||
|
provide: 'PRIVATE_PATH',
|
||||||
|
useValue: join(__dirname, '..', '..', '..', 'private'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: 'JWT_PRIVATE_KEY',
|
||||||
|
useFactory: async (path: string) => readFile(join(path, 'jwt.private.pem')),
|
||||||
|
inject: ['PRIVATE_PATH'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: 'JWT_PUBLIC_KEY',
|
||||||
|
useFactory: async (path: string) => readFile(join(path, 'jwt.public.pem')),
|
||||||
|
inject: ['PRIVATE_PATH'],
|
||||||
|
},
|
||||||
|
];
|
39
src/modules/jwt/jwt.service.ts
Normal file
39
src/modules/jwt/jwt.service.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { ConfigurationService } from '../config/config.service';
|
||||||
|
import * as jwt from 'jsonwebtoken';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class JWTService {
|
||||||
|
constructor(
|
||||||
|
@Inject('JWT_PRIVATE_KEY') private _privateKey: string,
|
||||||
|
@Inject('JWT_PUBLIC_KEY') private _publicKey: string,
|
||||||
|
private _config: ConfigurationService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public issue(
|
||||||
|
claims: Record<string, any>,
|
||||||
|
subject: string,
|
||||||
|
audience?: string,
|
||||||
|
): string {
|
||||||
|
return jwt.sign(claims, this._privateKey, {
|
||||||
|
algorithm: this._config.get('jwt.algorithm'),
|
||||||
|
issuer: this._config.get('jwt.issuer'),
|
||||||
|
expiresIn: this._config.get('jwt.expiration'),
|
||||||
|
subject,
|
||||||
|
audience,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public verify(
|
||||||
|
token: string,
|
||||||
|
subject?: string,
|
||||||
|
audience?: string,
|
||||||
|
): jwt.JwtPayload {
|
||||||
|
return jwt.verify(token, this._publicKey, {
|
||||||
|
algorithms: [this._config.get('jwt.algorithm')],
|
||||||
|
issuer: this._config.get('jwt.issuer'),
|
||||||
|
subject,
|
||||||
|
audience,
|
||||||
|
}) as jwt.JwtPayload;
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,9 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
|
import { ConfigurationModule } from 'src/modules/config/config.module';
|
||||||
import { databaseProviders } from './database.providers';
|
import { databaseProviders } from './database.providers';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
|
imports: [ConfigurationModule],
|
||||||
providers: [...databaseProviders],
|
providers: [...databaseProviders],
|
||||||
exports: [...databaseProviders],
|
exports: [...databaseProviders],
|
||||||
})
|
})
|
||||||
|
@ -1,19 +1,14 @@
|
|||||||
import { createConnection } from 'typeorm';
|
import { ConfigurationService } from 'src/modules/config/config.service';
|
||||||
|
import { ConnectionOptions, createConnection } from 'typeorm';
|
||||||
|
|
||||||
export const databaseProviders = [
|
export const databaseProviders = [
|
||||||
{
|
{
|
||||||
provide: 'DATABASE_CONNECTION',
|
provide: 'DATABASE_CONNECTION',
|
||||||
useFactory: async () =>
|
useFactory: async (config: ConfigurationService) => {
|
||||||
await createConnection({
|
return await createConnection({
|
||||||
type: 'mysql',
|
...config.get<ConnectionOptions>('database'),
|
||||||
host: 'localhost',
|
});
|
||||||
port: 3306,
|
},
|
||||||
username: 'icyauth',
|
inject: [ConfigurationService],
|
||||||
password: 'icyauth',
|
|
||||||
database: 'icyauth',
|
|
||||||
entities: [__dirname + '/../**/*.entity{.ts,.js}'],
|
|
||||||
synchronize: true,
|
|
||||||
logging: ['query', 'error'],
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
14
src/modules/objects/database/ormconfig.js
Normal file
14
src/modules/objects/database/ormconfig.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
|
const { join } = require('path');
|
||||||
|
const dotenv = require('dotenv');
|
||||||
|
const { readFileSync } = require('fs');
|
||||||
|
const toml = require('toml');
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
const CONFIG_ENV = process.env.NODE_ENV === 'production' ? 'prod' : 'dev';
|
||||||
|
const CONFIG_FILENAME = process.env.CONFIG || `config.${CONFIG_ENV}.toml`;
|
||||||
|
const CONFIG_PATH = join(process.cwd(), CONFIG_FILENAME);
|
||||||
|
const config = toml.parse(readFileSync(CONFIG_PATH, { encoding: 'utf-8' }));
|
||||||
|
|
||||||
|
module.exports = config.database;
|
@ -20,16 +20,25 @@ export class FormUtilityService {
|
|||||||
* Include CSRF token, messages and prefilled form values for a template with a form
|
* Include CSRF token, messages and prefilled form values for a template with a form
|
||||||
* @param req Express request
|
* @param req Express request
|
||||||
* @param session Express session
|
* @param session Express session
|
||||||
|
* @param additional Additional data to pass
|
||||||
* @returns Template locals
|
* @returns Template locals
|
||||||
*/
|
*/
|
||||||
public populateTemplate(
|
public populateTemplate(
|
||||||
req: Request,
|
req: Request,
|
||||||
session: SessionData,
|
session: SessionData,
|
||||||
|
additional: Record<string, any> = {},
|
||||||
): Record<string, any> {
|
): Record<string, any> {
|
||||||
const message = req.flash('message')[0] || {};
|
const message = req.flash('message')[0] || {};
|
||||||
const form = this.mergeObjectArray(
|
const form = this.mergeObjectArray(
|
||||||
(req.flash('form') as Record<string, any>[]) || [],
|
(req.flash('form') as Record<string, any>[]) || [],
|
||||||
);
|
);
|
||||||
return { csrf: session.csrf, message, form };
|
|
||||||
|
return {
|
||||||
|
path: req.originalUrl,
|
||||||
|
csrf: session.csrf,
|
||||||
|
message,
|
||||||
|
form,
|
||||||
|
...additional,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,5 +21,5 @@
|
|||||||
"strictBindCallApply": false,
|
"strictBindCallApply": false,
|
||||||
"forceConsistentCasingInFileNames": false,
|
"forceConsistentCasingInFileNames": false,
|
||||||
"noFallthroughCasesInSwitch": false
|
"noFallthroughCasesInSwitch": false
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
extends partials/layout.pug
|
extends partials/layout.pug
|
||||||
|
|
||||||
block title
|
block title
|
||||||
|Icy Network | Authorize application
|
|Authorize application | Icy Network
|
||||||
|
|
||||||
block body
|
block body
|
||||||
include partials/logo.pug
|
include partials/logo.pug
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
extends ../partials/layout.pug
|
extends ../partials/layout.pug
|
||||||
|
|
||||||
block title
|
block title
|
||||||
|Icy Network | Log in
|
|Log in | Icy Network
|
||||||
|
|
||||||
block body
|
block body
|
||||||
include ../partials/logo.pug
|
include ../partials/logo.pug
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
extends ../partials/layout.pug
|
extends ../partials/layout.pug
|
||||||
|
|
||||||
block title
|
block title
|
||||||
|Icy Network | Set a new password
|
|Set a new password | Icy Network
|
||||||
|
|
||||||
block body
|
block body
|
||||||
include ../partials/logo.pug
|
include ../partials/logo.pug
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
extends ../partials/layout.pug
|
extends ../partials/layout.pug
|
||||||
|
|
||||||
block title
|
block title
|
||||||
|Icy Network | Veify two-factor
|
|Veify two-factor | Icy Network
|
||||||
|
|
||||||
block body
|
block body
|
||||||
include ../partials/logo.pug
|
include ../partials/logo.pug
|
||||||
|
@ -14,6 +14,7 @@ html(lang="en")
|
|||||||
meta(name="twitter:description", content="Icy Network is a Single-Sign-On (SSO) provider")
|
meta(name="twitter:description", content="Icy Network is a Single-Sign-On (SSO) provider")
|
||||||
block links
|
block links
|
||||||
link(rel="stylesheet", type="text/css", href="/public/css/index.css")
|
link(rel="stylesheet", type="text/css", href="/public/css/index.css")
|
||||||
|
script(href="/public/js/app.bundle.js")
|
||||||
title
|
title
|
||||||
block title
|
block title
|
||||||
body
|
body
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
extends partials/layout.pug
|
extends partials/layout.pug
|
||||||
|
|
||||||
block title
|
block title
|
||||||
|Icy Network | Register
|
|Register | Icy Network
|
||||||
|
|
||||||
block body
|
block body
|
||||||
include partials/logo.pug
|
include partials/logo.pug
|
||||||
|
7
views/settings/general.pug
Normal file
7
views/settings/general.pug
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
extends ./layout.pug
|
||||||
|
|
||||||
|
block title
|
||||||
|
|General - Account settings | Icy Network
|
||||||
|
|
||||||
|
block settings
|
||||||
|
h1 General settings
|
16
views/settings/layout.pug
Normal file
16
views/settings/layout.pug
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
extends ../partials/layout.pug
|
||||||
|
|
||||||
|
block body
|
||||||
|
include ../partials/logo.pug
|
||||||
|
div.container
|
||||||
|
div.center-box.settings
|
||||||
|
nav.sidebar.settings__nav
|
||||||
|
ul
|
||||||
|
li
|
||||||
|
a(href="/account/general") General
|
||||||
|
li
|
||||||
|
a(href="/account/oauth2") Authorizations
|
||||||
|
li
|
||||||
|
a(href="/account/security") Security
|
||||||
|
section.content.settings__content
|
||||||
|
block settings
|
@ -1,7 +1,7 @@
|
|||||||
extends ../partials/layout.pug
|
extends ../partials/layout.pug
|
||||||
|
|
||||||
block title
|
block title
|
||||||
|Icy Network | Two-factor authentication
|
|Two-factor authentication | Icy Network
|
||||||
|
|
||||||
block body
|
block body
|
||||||
include ../partials/logo.pug
|
include ../partials/logo.pug
|
||||||
|
38
webpack.config.js
Normal file
38
webpack.config.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
|
const path = require('path');
|
||||||
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: ['./src/fe/ts/index.ts', './src/fe/scss/index.scss'],
|
||||||
|
plugins: [
|
||||||
|
new MiniCssExtractPlugin({
|
||||||
|
filename: '../css/index.css',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.(ts|js)?$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: {
|
||||||
|
loader: 'babel-loader',
|
||||||
|
options: {
|
||||||
|
presets: ['@babel/preset-env', '@babel/preset-typescript'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(css|scss)/,
|
||||||
|
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.ts', '.js'],
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, 'public', 'js'),
|
||||||
|
filename: 'app.bundle.js',
|
||||||
|
},
|
||||||
|
devtool: process.env.NODE_ENV !== 'production' ? 'source-map' : undefined,
|
||||||
|
};
|
Reference in New Issue
Block a user