├── LICENSE ├── assets └── .gitignore ├── .DS_Store ├── storage └── .DS_Store ├── src ├── Resources │ ├── Styles │ │ └── app.css │ ├── Ts │ │ ├── types.ts │ │ ├── shims-vue.d.ts │ │ ├── app.ts │ │ ├── Pages │ │ │ ├── Dashboard.vue │ │ │ ├── Hello.vue │ │ │ └── Authentication │ │ │ │ ├── Login.vue │ │ │ │ └── Register.vue │ │ └── Layouts │ │ │ ├── ApplicationGuest.vue │ │ │ ├── ApplicationLayout.vue │ │ │ └── Elements │ │ │ ├── GuestNavigation.vue │ │ │ └── MainNavigation.vue │ └── Views │ │ ├── InertiaApp.edge │ │ ├── Welcome.edge │ │ └── Exceptions │ │ └── exception.edge ├── App │ ├── Exceptions │ │ ├── ExceptionHandler.ts │ │ ├── UnauthorisedException.ts │ │ └── ValidationException.ts │ ├── DataTransferObjects │ │ └── Authentication │ │ │ ├── LoginRequestDto.ts │ │ │ └── RegistrationRequestDto.ts │ ├── Http │ │ ├── Controllers │ │ │ ├── DashboardController.ts │ │ │ ├── WelcomeController.ts │ │ │ └── Auth │ │ │ │ ├── AuthApiController.ts │ │ │ │ └── FrontendAuthenticationController.ts │ │ ├── Resources │ │ │ └── UserResource.ts │ │ ├── Middleware │ │ │ └── SetInertiaSharedDataMiddleware.ts │ │ └── Sockets │ │ │ └── HelloWorldSocketListener.ts │ └── Models │ │ └── User.ts ├── Config │ ├── InertiaConfiguration.ts │ ├── ServicesConfiguration.ts │ ├── RedisConfiguration.ts │ ├── DatabaseConfiguration.ts │ ├── Configuration.ts │ ├── QueueConfiguration.ts │ ├── SerializationConfiguration.ts │ ├── StaticAssetConfiguration.ts │ ├── FilesystemPathsConfiguration.ts │ ├── StorageConfiguration.ts │ ├── SessionConfiguration.ts │ ├── AuthConfiguration.ts │ ├── AppConfiguration.ts │ ├── WebsocketsConfiguration.ts │ └── ServerConfiguration.ts ├── Seeders │ ├── ExampleSeeder.ts │ └── Seeders.ts ├── index.ts └── envuso.d.ts ├── .gitignore ├── webpack.mix.js ├── example.env ├── tailwind.config.js ├── envuso.json ├── jest.config.ts ├── babel.config.js ├── tests └── example.test.ts ├── tsconfig.json ├── README.md └── package.json /LICENSE: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Envuso/framework/HEAD/.DS_Store -------------------------------------------------------------------------------- /storage/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Envuso/framework/HEAD/storage/.DS_Store -------------------------------------------------------------------------------- /src/Resources/Styles/app.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /src/Resources/Ts/types.ts: -------------------------------------------------------------------------------- 1 | 2 | export type UserObject = { 3 | _id: string; 4 | createdAt: string; 5 | email: string; 6 | name: string; 7 | avatar: string; 8 | } 9 | -------------------------------------------------------------------------------- /src/Resources/Ts/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | 2 | declare module "*.vue" { 3 | import {defineComponent} from "vue"; 4 | const component: ReturnType; 5 | export default component; 6 | } 7 | -------------------------------------------------------------------------------- /src/App/Exceptions/ExceptionHandler.ts: -------------------------------------------------------------------------------- 1 | import {ExceptionHandler as BaseExceptionHandler} from '@envuso/core/Common/Exception/ExceptionHandler'; 2 | 3 | export class ExceptionHandler extends BaseExceptionHandler { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/Config/InertiaConfiguration.ts: -------------------------------------------------------------------------------- 1 | import {ConfigurationCredentials} from "@envuso/core/AppContainer"; 2 | 3 | export default class InertiaConfiguration extends ConfigurationCredentials { 4 | rootView = 'InertiaApp'; 5 | } 6 | -------------------------------------------------------------------------------- /src/Seeders/ExampleSeeder.ts: -------------------------------------------------------------------------------- 1 | import {Seeder} from "@envuso/core/Database"; 2 | 3 | export class ExampleSeeder extends Seeder { 4 | public async seed(): Promise { 5 | console.log('Hello from example seeder.'); 6 | } 7 | 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .idea 3 | /node_modules/ 4 | /storage/logs/ 5 | /storage/temp/ 6 | /dist/ 7 | .env 8 | package-lock.json 9 | yarn.lock 10 | .yalc 11 | yalc.lock 12 | /yarn-error.log 13 | /assets/ 14 | /src/Meta/ConfigContracts/ 15 | /src/Meta/ 16 | -------------------------------------------------------------------------------- /src/Seeders/Seeders.ts: -------------------------------------------------------------------------------- 1 | import {DatabaseSeeder} from "@envuso/core/Database"; 2 | import {ExampleSeeder} from "./ExampleSeeder"; 3 | 4 | export class Seeders extends DatabaseSeeder { 5 | 6 | public registerSeeders() { 7 | this.add(ExampleSeeder); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /webpack.mix.js: -------------------------------------------------------------------------------- 1 | const mix = require('laravel-mix'); 2 | 3 | mix 4 | .setPublicPath('assets') 5 | .vue({version : 3}) 6 | .ts('src/Resources/Ts/app.ts', 'assets/app.js') 7 | .postCss("src/Resources/Styles/app.css", "assets/app.css", [ 8 | require("tailwindcss"), 9 | ]) 10 | .disableNotifications() 11 | ; 12 | -------------------------------------------------------------------------------- /src/App/Exceptions/UnauthorisedException.ts: -------------------------------------------------------------------------------- 1 | import {Exception} from "@envuso/core/Common/Exception/Exception"; 2 | import { StatusCodes } from "@envuso/core/Common"; 3 | 4 | export class UnauthorisedException extends Exception { 5 | 6 | constructor(message?: string) { 7 | super(message ?? 'Unauthorised.', StatusCodes.UNAUTHORIZED); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/Config/ServicesConfiguration.ts: -------------------------------------------------------------------------------- 1 | import {ConfigurationCredentials} from "@envuso/core/AppContainer/Config/ConfigurationCredentials"; 2 | //import {OAuthProviders} from "@envuso/core/OAuthProvider/OAuthService"; 3 | 4 | 5 | export class ServicesConfiguration extends ConfigurationCredentials { 6 | 7 | // oauthProviders: OAuthProviders = { 8 | // 9 | // }; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/App/DataTransferObjects/Authentication/LoginRequestDto.ts: -------------------------------------------------------------------------------- 1 | 2 | import { DataTransferObject } from "@envuso/core/Routing"; 3 | import {IsEmail, IsString, MinLength} from "class-validator"; 4 | 5 | export class LoginRequestDto extends DataTransferObject { 6 | 7 | @IsEmail() 8 | @IsString() 9 | email: string; 10 | 11 | @MinLength(6) 12 | password: string; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/Resources/Ts/app.ts: -------------------------------------------------------------------------------- 1 | import {createApp, h} from 'vue'; 2 | import {createInertiaApp} from '@inertiajs/inertia-vue3'; 3 | 4 | createInertiaApp({ 5 | id: 'app', 6 | resolve : name => require(`./Pages/${name}.vue`), 7 | setup({el, app, props, plugin}) 8 | { 9 | createApp({render : () => h(app, props)}) 10 | .use(plugin) 11 | .mount(el); 12 | }, 13 | }); 14 | 15 | -------------------------------------------------------------------------------- /src/Resources/Ts/Pages/Dashboard.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | 15 | 18 | -------------------------------------------------------------------------------- /src/Config/RedisConfiguration.ts: -------------------------------------------------------------------------------- 1 | import {ConfigurationCredentials} from "@envuso/core/AppContainer/Config/ConfigurationCredentials"; 2 | import {RedisOptions} from "ioredis"; 3 | 4 | export default class RedisConfiguration extends ConfigurationCredentials implements RedisOptions { 5 | host = "127.0.0.1"; 6 | port = 6379; 7 | password = null; 8 | db = 0; 9 | keyPrefix = "envuso-"; 10 | } 11 | -------------------------------------------------------------------------------- /example.env: -------------------------------------------------------------------------------- 1 | PORT=8081 2 | NODE_ENV=development 3 | 4 | APP_KEY=some-random-string 5 | APP_HOST=http://localhost:8081 6 | 7 | DB_URL="mongodb://127.0.0.1:27017" 8 | DB_NAME="envuso" 9 | 10 | SPACES_KEY= 11 | SPACES_SECRET= 12 | SPACES_ENDPOINT= 13 | SPACES_URL= 14 | SPACES_REGION=fra1 15 | SPACES_BUCKET= 16 | 17 | REDIS_HOST=127.0.0.1 18 | REDIS_PORT=6379 19 | REDIS_USERNAME= 20 | REDIS_PASSWORD= 21 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | purge: [ 3 | './src/Resources/Views/**/*.edge', 4 | './src/Resources/Ts/**/*.ts', 5 | './src/Resources/Ts/**/*.vue', 6 | ], 7 | darkMode: false, // or 'media' or 'class' 8 | theme: { 9 | extend: {}, 10 | }, 11 | variants: { 12 | extend: {}, 13 | }, 14 | plugins: [ 15 | require('@tailwindcss/forms') 16 | ], 17 | } 18 | -------------------------------------------------------------------------------- /envuso.json: -------------------------------------------------------------------------------- 1 | { 2 | "projectRoot": "./", 3 | "moduleMetaDirectory": "src", 4 | "controllersDirectory": "src/App/Http/Controllers", 5 | "configurationDirectory": "src/Config", 6 | "ignoredFiles": [ 7 | "dist", 8 | "src/Meta/ApplicationModules.ts", 9 | "src/Meta/ApplicationRouteHelpers.ts", 10 | "src/Meta/ApplicationRouteMeta.ts", 11 | "src/Meta/ConfigContracts/**.ts" 12 | ], 13 | "outputCompiledFiles": false, 14 | "tsConfigPath": "tsconfig.json" 15 | } 16 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "roots": [ 3 | "/src" 4 | ], 5 | "testMatch": [ 6 | "**/tests/**/*.test.ts", 7 | ], 8 | "transform": { 9 | "^.+\\.(ts|tsx)$": "ts-jest" 10 | }, 11 | "moduleNameMapper": { 12 | "^@Providers/(.*)": "/src/Core/Providers/$1", 13 | "^@App/(.*)": "/src/App/$1", 14 | "^@AppControllers/(.*)": "/src/App/Http/Controller/$1", 15 | "^@AppMiddlewares/(.*)": "/src/App/Http/Middleware/$1", 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | // babel.config.js 2 | module.exports = { 3 | presets : [ 4 | ['@babel/preset-env', {targets : {node : 'current'}}], 5 | '@babel/preset-typescript', 6 | ], 7 | plugins : [ 8 | ["@babel/plugin-proposal-decorators", {decoratorsBeforeExport : true}], 9 | ["@babel/plugin-proposal-class-properties"], 10 | ["@babel/plugin-proposal-private-property-in-object"], 11 | ["babel-plugin-replace-ts-export-assignment"], 12 | ["@babel/plugin-transform-classes", { 13 | "loose": true 14 | }] 15 | ], 16 | }; 17 | -------------------------------------------------------------------------------- /src/Resources/Views/InertiaApp.edge: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Welcome 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/App/Http/Controllers/DashboardController.ts: -------------------------------------------------------------------------------- 1 | import {Inertia} from "@envuso/core/Packages/Inertia/Inertia"; 2 | import {Controller, controller, get, middleware} from "@envuso/core/Routing"; 3 | import {SessionAuthenticationMiddleware} from "@envuso/core/Routing/Middleware/Middlewares/SessionAuthenticationMiddleware"; 4 | 5 | @middleware(new SessionAuthenticationMiddleware()) 6 | @controller('/dashboard') 7 | export class DashboardController extends Controller { 8 | 9 | @get('/') 10 | viewDashboard() { 11 | return Inertia.render('Dashboard', {}); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/App/Models/User.ts: -------------------------------------------------------------------------------- 1 | import {Exclude, Expose} from "class-transformer"; 2 | import {id} from "@envuso/core/Database"; 3 | import {Authenticatable} from "@envuso/core"; 4 | import {IsEmail, IsNotEmpty} from "class-validator"; 5 | import {ObjectId} from "mongodb"; 6 | 7 | export class User extends Authenticatable { 8 | 9 | @id 10 | _id: ObjectId; 11 | 12 | @IsEmail() 13 | @IsNotEmpty() 14 | email: string; 15 | 16 | @Expose() 17 | name: string; 18 | 19 | @Exclude({toPlainOnly : true}) 20 | password?: string; 21 | 22 | createdAt: Date; 23 | } 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import "reflect-metadata"; 2 | import Environment from "@envuso/core/AppContainer/Config/Environment"; 3 | import path from "path"; 4 | 5 | Environment.load(path.join(__dirname, '..', '.env')); 6 | 7 | import Configuration from "./Config/Configuration"; 8 | import {Envuso} from "@envuso/core"; 9 | import {Log} from "@envuso/core/Common"; 10 | 11 | const envuso = new Envuso(); 12 | 13 | Configuration.initiate() 14 | .then(() => envuso.boot()) 15 | .then(() => envuso.serve()) 16 | .catch(error => { 17 | Log.error(error); 18 | console.trace(error); 19 | }); 20 | 21 | -------------------------------------------------------------------------------- /src/App/DataTransferObjects/Authentication/RegistrationRequestDto.ts: -------------------------------------------------------------------------------- 1 | 2 | import { DataTransferObject } from "@envuso/core/Routing"; 3 | import {Confirmed} from "@envuso/core/Routing/DataTransferObject/Validators"; 4 | import {IsEmail, IsNotEmpty, IsString, MinLength} from "class-validator"; 5 | 6 | export class RegistrationRequestDto extends DataTransferObject { 7 | @IsEmail() 8 | @IsString() 9 | email: string; 10 | 11 | @IsNotEmpty() 12 | @IsString() 13 | @MinLength(3) 14 | name: string; 15 | 16 | @MinLength(6) 17 | @Confirmed() 18 | password: string; 19 | @MinLength(6) 20 | password_confirmation: string; 21 | } 22 | -------------------------------------------------------------------------------- /src/Resources/Ts/Pages/Hello.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 23 | 24 | 27 | -------------------------------------------------------------------------------- /src/App/Http/Resources/UserResource.ts: -------------------------------------------------------------------------------- 1 | import {RequestContextContract} from "@envuso/core/Contracts/Routing/Context/RequestContextContract"; 2 | import {ApiResource} from "@envuso/core/Routing"; 3 | import {User} from "../../Models/User"; 4 | 5 | export class UserResource extends ApiResource { 6 | 7 | public transform(request: RequestContextContract): any { 8 | return { 9 | _id : this.data._id, 10 | email : this.data.email, 11 | name : this.data.name, 12 | createdAt : this.data.createdAt, 13 | avatar : `https://eu.ui-avatars.com/api/?name=${encodeURIComponent(this.data.name)}` 14 | }; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/Resources/Ts/Layouts/ApplicationGuest.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 28 | 29 | 32 | -------------------------------------------------------------------------------- /tests/example.test.ts: -------------------------------------------------------------------------------- 1 | //import "@Core/Bootstrap"; 2 | //import {App} from "@envuso/core/src/Core"; 3 | // 4 | // 5 | ////@ts-ignore 6 | //global.disableConsoleLogs = false; 7 | // 8 | //const app = new App(); 9 | // 10 | //beforeAll(() => { 11 | // return async function prepare() { 12 | // app.registerProviders(); 13 | // await app.registerProviderBindings(); 14 | // await app.bootProviders(); 15 | // } 16 | //}); 17 | // 18 | //afterAll(() => { 19 | // return async function unPrepare() { 20 | // app.down(); 21 | // } 22 | //}) 23 | // 24 | // 25 | //describe('some test group', () => { 26 | // test('it is true', async () => { 27 | // expect(true).toBeTruthy(); 28 | // }); 29 | //}) 30 | // 31 | // 32 | // 33 | -------------------------------------------------------------------------------- /src/App/Http/Middleware/SetInertiaSharedDataMiddleware.ts: -------------------------------------------------------------------------------- 1 | import {RequestContextContract} from "@envuso/core/Contracts/Routing/Context/RequestContextContract"; 2 | import {InertiaMiddleware} from "@envuso/core/Packages/Inertia/Middleware/InertiaMiddleware"; 3 | import {User} from "../../Models/User"; 4 | import {UserResource} from "../Resources/UserResource"; 5 | 6 | 7 | export class SetInertiaSharedDataMiddleware extends InertiaMiddleware { 8 | 9 | async handle(context: RequestContextContract) { 10 | const userId = context.session.store().get('user_id', null); 11 | let user = null; 12 | if (userId) { 13 | user = await User.find(userId, '_id'); 14 | } 15 | 16 | await super.handle(context); 17 | 18 | context.inertia.share('user', user ? UserResource.from(user) : null); 19 | } 20 | 21 | share(context: RequestContextContract) { 22 | return { 23 | errors : context.session.store().get('errors', null), 24 | }; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/App/Http/Controllers/WelcomeController.ts: -------------------------------------------------------------------------------- 1 | import {Str} from "@envuso/core/Common"; 2 | import {Inertia} from "@envuso/core/Packages/Inertia/Inertia"; 3 | import { 4 | Controller, 5 | controller, 6 | get, post, view 7 | } from "@envuso/core/Routing"; 8 | 9 | @controller('/') 10 | export class WelcomeController extends Controller { 11 | 12 | @get('*') 13 | public async welcome() { 14 | 15 | return view('welcome'); 16 | } 17 | 18 | @get('/hello') 19 | public async hello() { 20 | // This will render an Inertia/Vue frontend. 21 | // You can change the Inertia App layout in /src/Resources/Views/InertiaApp.edge 22 | // You can also change this default layout in /src/Config/InertiaConfiguration.ts 23 | return Inertia.render('Hello', { 24 | message : 'Hello World!' 25 | }); 26 | } 27 | 28 | @post('/hello') 29 | public async randomHello() { 30 | return Inertia.render('Hello', { 31 | message : `Hello ${Str.random(10)}!` 32 | }); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/Config/DatabaseConfiguration.ts: -------------------------------------------------------------------------------- 1 | import {ConfigurationCredentials} from "@envuso/core/AppContainer/Config/ConfigurationCredentials"; 2 | import Environment from "@envuso/core/AppContainer/Config/Environment"; 3 | import { 4 | DatabaseConfiguration as DbConfig, 5 | MongoConnectionConfiguration, 6 | } from '@envuso/core/Contracts/Configuration/DatabaseConfigurationContracts'; 7 | 8 | export class DatabaseConfiguration extends ConfigurationCredentials implements DbConfig { 9 | 10 | mongo: MongoConnectionConfiguration = { 11 | name : Environment.get('DB_NAME', 'envuso'), 12 | url : Environment.get('DB_URL', 'mongodb://127.0.0.1:27017'), 13 | clientOptions : { 14 | ssl : true, 15 | readPreference : "primaryPreferred", 16 | } 17 | }; 18 | 19 | /** 20 | * Your user defined seeder manager 21 | * This is where you will register all of your seeder instances 22 | * They will all be looped through and seeded. 23 | */ 24 | // seeder = Seeders; 25 | seeder = null; 26 | } 27 | -------------------------------------------------------------------------------- /src/App/Http/Sockets/HelloWorldSocketListener.ts: -------------------------------------------------------------------------------- 1 | import {injectable} from "@envuso/core/AppContainer"; 2 | import {WebSocketConnectionContract} from "@envuso/core/Contracts/WebSockets/WebSocketConnectionContract"; 3 | import {Middleware} from "@envuso/core/Routing"; 4 | import {UserMessageSocketPacket} from "@envuso/core/WebSockets/SocketEventTypes"; 5 | import {WebSocketChannelListener} from "@envuso/core/WebSockets/WebSocketChannelListener"; 6 | import {User} from "../../Models/User"; 7 | 8 | @injectable() 9 | export class HelloWorldSocketListener extends WebSocketChannelListener { 10 | 11 | public channelName(): string { 12 | return "hello-world"; 13 | } 14 | 15 | public async isAuthorised(connection: WebSocketConnectionContract): Promise { 16 | return true; 17 | } 18 | 19 | public middlewares(): Middleware[] { 20 | return []; 21 | } 22 | 23 | async message(connection: WebSocketConnectionContract, packet: UserMessageSocketPacket): Promise { 24 | this.broadcast('hello', { 25 | message : 'Hey there.' 26 | }); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Config/Configuration.ts: -------------------------------------------------------------------------------- 1 | import ConfigurationFile from "@envuso/core/AppContainer/Config/ConfigurationFile"; 2 | 3 | export default class Configuration extends ConfigurationFile { 4 | 5 | load() { 6 | this.add('app', import("./AppConfiguration")); 7 | this.add('auth', import("./AuthConfiguration")); 8 | this.add('database', import("./DatabaseConfiguration")); 9 | this.add('redis', import("./RedisConfiguration")); 10 | this.add('paths', import("./FilesystemPathsConfiguration")); 11 | this.add('serialization', import("./SerializationConfiguration")); 12 | this.add('server', import("./ServerConfiguration")); 13 | this.add('services', import("./ServicesConfiguration")); 14 | this.add('session', import("./SessionConfiguration")); 15 | this.add('storage', import("./StorageConfiguration")); 16 | this.add('websockets', import("./WebsocketsConfiguration")); 17 | this.add('inertia', import("./InertiaConfiguration")); 18 | this.add('static', import("./StaticAssetConfiguration")); 19 | this.add('queue', import("./QueueConfiguration")); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/Resources/Ts/Layouts/ApplicationLayout.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 41 | 42 | 45 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Node 14", 4 | "compilerOptions": { 5 | "lib": [ 6 | "ESNext" 7 | ], 8 | "module": "commonjs", 9 | "target": "ESNext", 10 | "moduleResolution": "node", 11 | "outDir": "./dist", 12 | "importHelpers": true, 13 | "esModuleInterop": true, 14 | "allowSyntheticDefaultImports": true, 15 | "rootDir": "./src/", 16 | "baseUrl": "./src/", 17 | "strict": false, 18 | "sourceMap": true, 19 | "types": [ 20 | "node", 21 | "reflect-metadata", 22 | "jest" 23 | ], 24 | "typeRoots": [ 25 | "node_modules/@types" 26 | ], 27 | "experimentalDecorators": true, 28 | "emitDecoratorMetadata": true, 29 | "resolveJsonModule": true, 30 | "skipLibCheck": true, 31 | "paths": { 32 | "class-transformer": [ 33 | "./../node_modules/class-transformer" 34 | ] 35 | } 36 | }, 37 | "exclude": [ 38 | "node_modules", 39 | "jest.config.ts", 40 | "tests/example.test.ts" 41 | ], 42 | "include": [ 43 | "./src/Config/**/*", 44 | "./src/**/*" 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /src/Config/QueueConfiguration.ts: -------------------------------------------------------------------------------- 1 | import {ConfigurationCredentials} from "@envuso/core/AppContainer/Config/ConfigurationCredentials"; 2 | import {ServiceProviderContract} from "@envuso/core/Contracts/AppContainer/ServiceProviderContract"; 3 | import {QueueConfiguration as QueueConfig} from "@envuso/core/Contracts/Configuration/QueueConfigurationContracts"; 4 | import {EncryptionServiceProvider} from "@envuso/core/Crypt"; 5 | import {DatabaseServiceProvider} from "@envuso/core/Database"; 6 | import {RedisServiceProvider} from "@envuso/core/Redis/RedisServiceProvider"; 7 | import {StorageServiceProvider} from "@envuso/core/Storage"; 8 | 9 | export default class QueueConfiguration extends ConfigurationCredentials implements QueueConfig { 10 | waitTimeMs = 1_000; 11 | 12 | /** 13 | * Service providers to load into the container with your queue workers 14 | * 15 | * Without these defined/loaded, you won't have access to things 16 | * like the database, file storage, redis, etc... 17 | * 18 | * @type {{new(): ServiceProviderContract}[]} 19 | */ 20 | providers: (new () => ServiceProviderContract)[] = [ 21 | DatabaseServiceProvider, 22 | RedisServiceProvider, 23 | EncryptionServiceProvider, 24 | StorageServiceProvider, 25 | ]; 26 | } 27 | -------------------------------------------------------------------------------- /src/Config/SerializationConfiguration.ts: -------------------------------------------------------------------------------- 1 | import {ClassTransformOptions} from "class-transformer/types/interfaces/class-transformer-options.interface"; 2 | import {ValidatorOptions} from "class-validator/types/validation/ValidatorOptions"; 3 | import {ConfigurationCredentials} from "@envuso/core/AppContainer/Config/ConfigurationCredentials"; 4 | import {DataTransferObjectSerialization} from "@envuso/core/Routing"; 5 | 6 | 7 | export class SerializationConfiguration extends ConfigurationCredentials { 8 | 9 | /** 10 | * When a data transfer object is used with the @dto() 11 | * decorator on a controller method. 12 | */ 13 | dataTransferObjects: DataTransferObjectSerialization = { 14 | /** 15 | * These are the options used to map the request body to your data transfer object. 16 | * 17 | * This is run when we process the request and call the controller method with your DTO. 18 | */ 19 | requestSerializationOptions : { 20 | strategy : 'exposeAll', 21 | excludePrefixes : ['__'] 22 | }, 23 | /** 24 | * These are the options used for validation after creating your DTO. 25 | */ 26 | validationOptions : { 27 | whitelist : true, 28 | forbidUnknownValues : true, 29 | enableDebugMessages : true, 30 | } 31 | }; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |

6 | npm (scoped) 7 | GitHub 8 | Wakatime 9 |

10 | 11 | ## Envuso framework 12 | 13 | Envuso is a backend framework focusing building apis using [Fastify](https://www.fastify.io/) and [MongoDB](https://www.mongodb.com/) support. 14 | 15 | You can also build monolith applications using inertiajs/edge.js views. 16 | 17 | I'd like to think it's almost a full batteries included framework, but there's still a couple of bits missing. 18 | 19 | ## Credits: 20 | 21 | - [Inversify Express Utils](https://github.com/inversify/inversify-express-utils) 22 | We used inversify express utils in a setup without fastify, which was very barebones but enjoyable. We used this as a small starting point, 23 | converting it to fastify and re-writing a lot of it, to fix any pain points we had. 24 | - [MongoDB Typescript](https://github.com/aljazerzen/mongodb-typescript) 25 | We used mongodb-typescript as it was a nice starting point for mongo implementation with class transformer 26 | -------------------------------------------------------------------------------- /src/Config/StaticAssetConfiguration.ts: -------------------------------------------------------------------------------- 1 | import {ConfigurationCredentials} from "@envuso/core/AppContainer"; 2 | import {WatchOptions} from "chokidar"; 3 | 4 | export default class StaticAssetConfiguration extends ConfigurationCredentials { 5 | 6 | /** 7 | * This is the raw folder name in your application root directory 8 | * 9 | * For example, if we have our project at /EnvusoProject 10 | * Our asset's directory is registered as /EnvusoProject/assets 11 | * 12 | * @type {string} 13 | */ 14 | public assetsPath: string = 'assets'; 15 | 16 | /** 17 | * Essentially, by setting this to true, when our assets registered in our above asset's path 18 | * the server will be re-booted automatically. 19 | * 20 | * without this, the server won't have your routes/asset's registered if you were to 21 | * (for example) boot the server, then build your frontend asset's. 22 | * 23 | * By default, this is disabled so that you don't consume extra RAM in development 24 | * It will also be disabled in production(you shouldn't be using this in production) 25 | * 26 | * 27 | * @type {boolean} 28 | */ 29 | public watch: boolean = false; 30 | 31 | /** 32 | * When the above "watch" option is set to true, we can define any options here which will be passed to chokidar 33 | * 34 | * @type {WatchOptions} 35 | */ 36 | public watchOptions: WatchOptions = { 37 | ignoreInitial : true, 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /src/Config/FilesystemPathsConfiguration.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import {ConfigurationCredentials} from "@envuso/core/AppContainer/Config/ConfigurationCredentials"; 3 | 4 | 5 | export class FilesystemPathsConfiguration extends ConfigurationCredentials { 6 | public root: string = process.cwd(); 7 | public src: string = path.join(this.root, 'src'); 8 | public controllers: string = path.join(this.root, 'src', 'App', 'Http', 'Controllers'); 9 | public middleware: string = path.join(this.root, 'src', 'App', 'Http', 'Middleware'); 10 | public socketListeners: string = path.join(this.root, 'src', 'App', 'Http', 'Sockets'); 11 | public eventDispatchers: string = path.join(this.root, 'src', 'App', 'Events', 'Dispatchers'); 12 | public eventListeners: string = path.join(this.root, 'src', 'App', 'Events', 'Listeners'); 13 | public providers: string = path.join(this.root, 'src', 'App', 'Providers'); 14 | public models: string = path.join(this.root, 'src', 'App', 'Models'); 15 | public policies: string = path.join(this.root, 'src', 'Policies'); 16 | public storage: string = path.join(this.root, 'storage'); 17 | public temp: string = path.join(this.root, 'storage', 'temp'); 18 | public views: string = path.join(this.root, 'src', 'Resources', 'Views'); 19 | public assets: string = path.join(this.root, 'src', 'Resources', 'Assets'); 20 | 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src/Config/StorageConfiguration.ts: -------------------------------------------------------------------------------- 1 | import Environment from "@envuso/core/AppContainer/Config/Environment"; 2 | import path from "path"; 3 | import {ConfigurationCredentials} from "@envuso/core/AppContainer/Config/ConfigurationCredentials"; 4 | import {DisksList, DriversList, LocalFileProvider} from "@envuso/core/Storage"; 5 | import {S3Provider} from "@envuso/core/Storage"; 6 | import {StorageConfiguration as StorageConfig} from '@envuso/core/Storage/StorageProviderContract'; 7 | 8 | export class StorageConfiguration extends ConfigurationCredentials implements StorageConfig { 9 | 10 | /** 11 | * The default storage provider to use on the request() helper 12 | * or when using Storage.get(), Storage.put() etc 13 | */ 14 | defaultDisk: keyof DisksList = 'local'; 15 | 16 | disks: DisksList = { 17 | s3 : { 18 | driver : 's3', 19 | bucket : Environment.get('SPACES_BUCKET'), 20 | url : Environment.get('SPACES_URL'), 21 | endpoint : Environment.get('SPACES_ENDPOINT'), 22 | credentials : { 23 | accessKeyId : Environment.get('SPACES_KEY'), 24 | secretAccessKey : Environment.get('SPACES_SECRET'), 25 | } 26 | }, 27 | temp : { 28 | driver : 'local', 29 | root : path.join(process.cwd(), 'storage', 'temp'), 30 | }, 31 | local : { 32 | driver : 'local', 33 | root : path.join(process.cwd(), 'storage', 'local'), 34 | }, 35 | storage : { 36 | driver : 'local', 37 | root : path.join(process.cwd(), 'storage'), 38 | }, 39 | }; 40 | 41 | drivers: DriversList = { 42 | local : LocalFileProvider, 43 | s3 : S3Provider, 44 | }; 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/Config/SessionConfiguration.ts: -------------------------------------------------------------------------------- 1 | import {ConfigurationCredentials} from "@envuso/core/AppContainer/Config/ConfigurationCredentials"; 2 | import {DateTime} from "@envuso/date-time-helper"; 3 | import {CookieConfiguration, SessionConfiguration as SessionConfig, SessionCookie} from "@envuso/core/Contracts/Session/Types"; 4 | import {RedisSessionDriver} from "@envuso/core/Session/Drivers/RedisSessionDriver"; 5 | import {SessionStorageDriver} from "@envuso/core/Session/Drivers/SessionStorageDriver"; 6 | 7 | 8 | export class SessionConfiguration extends ConfigurationCredentials implements SessionConfig { 9 | 10 | /** 11 | * All cookies are stored by default with this configuration 12 | */ 13 | cookie: CookieConfiguration = { 14 | path : '/', 15 | httpOnly : true, 16 | secure : false, 17 | expires : DateTime.now().addDays(7), 18 | maxAge : DateTime.now().addDays(7), 19 | sameSite : "Lax", 20 | encrypted : true, 21 | // domain : null, 22 | }; 23 | 24 | /** 25 | * The driver used to handle session data 26 | * 27 | * Available drivers are: 28 | * - RedisSessionDriver 29 | * - Import: ../Session/Drivers/RedisSessionDriver 30 | * - FileSessionDriver 31 | * - Import: ../Session/Drivers/FileSessionDriver 32 | */ 33 | // sessionStorageDriver: new () => SessionStorageDriver | null = FileSessionDriver; 34 | sessionStorageDriver: new () => SessionStorageDriver | null = RedisSessionDriver; 35 | 36 | /** 37 | * Configuration for session cookies 38 | * These settings affect how SessionAuthenticationProvider works. 39 | */ 40 | sessionCookie: SessionCookie = { 41 | name : 'sessionId', 42 | }; 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/App/Exceptions/ValidationException.ts: -------------------------------------------------------------------------------- 1 | //import {Exception} from "@envuso/core/Common"; 2 | //import {ValidationError} from "class-validator"; 3 | //import {StatusCodes} from "http-status-codes"; 4 | // 5 | //export class ValidationException extends Exception { 6 | // 7 | // private errors: any = {}; 8 | // 9 | // constructor(errors: Array | { [key: string]: string }) { 10 | // super("Woops something went wrong.", StatusCodes.UNPROCESSABLE_ENTITY); 11 | // 12 | // this.errors = errors; 13 | // 14 | // this.response = { 15 | // message : this.message, 16 | // errors : this.processErrors() 17 | // }; 18 | // } 19 | // 20 | // static message(message) { 21 | // const exception = new ValidationException([ 22 | // message 23 | // ]); 24 | // 25 | // exception.message = message; 26 | // 27 | // return exception; 28 | // } 29 | // 30 | // /** 31 | // * Format the different types of validation exception messages 32 | // * 33 | // * @private 34 | // */ 35 | // private processErrors() { 36 | // let errors = {}; 37 | // 38 | // if (Array.isArray(this.errors)) { 39 | // if (!this.errors.length) { 40 | // return errors; 41 | // } 42 | // 43 | // const firstError = this.errors[0] || null; 44 | // 45 | // if (!firstError) { 46 | // return errors; 47 | // } 48 | // 49 | // if (firstError instanceof ValidationError) { 50 | // for (let error of this.errors) { 51 | // errors[error.property] = Object.values(error.constraints)[0] || null; 52 | // } 53 | // 54 | // return errors; 55 | // } 56 | // 57 | // return errors; 58 | // } 59 | // 60 | // errors = {...this.errors, ...errors}; 61 | // 62 | // return errors; 63 | // } 64 | // 65 | // 66 | //} 67 | -------------------------------------------------------------------------------- /src/Config/AuthConfiguration.ts: -------------------------------------------------------------------------------- 1 | import {SignOptions, VerifyOptions} from "jsonwebtoken"; 2 | import {ConfigurationCredentials} from "@envuso/core/AppContainer/Config/ConfigurationCredentials"; 3 | import {JwtAuthenticationProvider, ModelUserProvider, SessionAuthenticationProvider} from "@envuso/core/Authentication"; 4 | import {AuthenticationIdentifier} from "@envuso/core/Contracts/Authentication/UserProvider/AuthCredentials"; 5 | 6 | export class AuthConfiguration extends ConfigurationCredentials { 7 | 8 | userModel = 'User'; 9 | 10 | /** 11 | * This will allow you to swap out authentication handling 12 | * and build your own custom providers for different things 13 | */ 14 | authenticationProviders = [JwtAuthenticationProvider, SessionAuthenticationProvider]; 15 | 16 | /** 17 | * This will allow you to change how the user is acquired 18 | * For example, you could write a provider to get user 19 | * information from an api endpoint, database etc 20 | */ 21 | userProvider = ModelUserProvider; 22 | 23 | /** 24 | * This will allow users authentication to use email for primary login. 25 | * For example, you could change this to "username" instead if 26 | * you didn't want to use email registration and login. 27 | */ 28 | primaryIdentifier: AuthenticationIdentifier = 'email'; 29 | 30 | jwt = { 31 | /** 32 | * The prefix used in authorization header checks 33 | */ 34 | authorizationHeaderPrefix : 'Bearer', 35 | 36 | /** 37 | * Used to sign JWT 38 | */ 39 | jwtSigningOptions : { 40 | expiresIn : "24h", 41 | algorithm : "HS256", 42 | } as SignOptions, 43 | 44 | /** 45 | * Used to verify JWT are valid 46 | */ 47 | jwtVerifyOptions : { 48 | ignoreExpiration : false, 49 | algorithms : ["HS256"], 50 | } as VerifyOptions 51 | }; 52 | } 53 | -------------------------------------------------------------------------------- /src/Resources/Views/Welcome.edge: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Welcome 9 | 10 | 66 | 67 | 68 |

69 | Welcome! 70 |

71 | 72 |

73 | Thanks for using Envuso, don't forget you can check out the documentation at: https://envuso.com 74 |

75 |

76 | Check out the Inertia.JS integration: on this page 77 |

78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /src/App/Http/Controllers/Auth/AuthApiController.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, 3 | controller, 4 | DataTransferObject, 5 | dto, 6 | get, 7 | JwtAuthenticationMiddleware, 8 | middleware, 9 | post, 10 | user, 11 | } from "@envuso/core/Routing"; 12 | import {Hash} from "@envuso/core/Crypt"; 13 | import {IsEmail, IsString, Length} from "class-validator"; 14 | import {Auth} from "@envuso/core/Authentication"; 15 | import {User} from "../../../Models/User"; 16 | 17 | class LoginDTO extends DataTransferObject { 18 | @IsEmail() 19 | @IsString() 20 | email: string; 21 | 22 | @Length(6, 250) 23 | password: string; 24 | } 25 | 26 | class RegisterDTO extends LoginDTO { 27 | 28 | @Length(1) 29 | name: string; 30 | 31 | createdAt: Date; 32 | } 33 | 34 | @controller('/api/auth') 35 | export class AuthApiController extends Controller { 36 | 37 | @post('/login') 38 | public async login(@dto() loginDto: LoginDTO) { 39 | 40 | if (!await Auth.attempt(loginDto)) { 41 | return { 42 | error : 'Invalid credentials.' 43 | }; 44 | } 45 | 46 | const user = Auth.user(); 47 | 48 | return { 49 | user : user, 50 | token : user.generateToken() 51 | }; 52 | } 53 | 54 | @post('/register') 55 | public async register(@dto(false) registerDto: RegisterDTO) { 56 | 57 | await registerDto.validate(); 58 | 59 | registerDto.createdAt = new Date(); 60 | 61 | const user = new User(); 62 | user.email = registerDto.email; 63 | user.name = registerDto.name; 64 | user.password = await Hash.make(registerDto.password); 65 | user.createdAt = registerDto.createdAt; 66 | await user.save(); 67 | 68 | Auth.authoriseAs(user); 69 | 70 | return { 71 | user : Auth.user() 72 | }; 73 | } 74 | 75 | @middleware(new JwtAuthenticationMiddleware()) 76 | @get('/user') 77 | public async user(@user user: User) { 78 | return { 79 | user, 80 | id : Auth.id() 81 | }; 82 | } 83 | 84 | @get('/user/:user') 85 | async getUser(user: User) { 86 | 87 | return user; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/envuso.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * *INSERT FLASHING WARNING LIGHTS* 3 | * 4 | * THIS FILE IS AUTOMATICALLY GENERATED 5 | * DO NOT EDIT IT, YOUR CHANGES WILL BE OVER-WRITTEN ON BUILD. 6 | */ 7 | import { ResponseContract } from "@envuso/core/Contracts/Routing/Context/Response/ResponseContract"; 8 | 9 | import { ApplicationRouteAttributeObject } from "./Meta/ApplicationRouteMeta"; 10 | 11 | import { ConfigHelperKeys } from "./Meta/Configuration"; 12 | 13 | declare module "@envuso/core/Routing" { 14 | declare function redirect(): RedirectResponseContract; 15 | declare function view(templatePath: string, data?: any): ResponseContract; 16 | declare interface RedirectResponseContract { 17 | view(templatePath: string, data?: any): ResponseContract; 18 | route(routeStr: T, attributes?: Partial): RedirectResponseContract; 19 | route(routeStr: T, attributes?: any): RedirectResponseContract; 20 | } 21 | } 22 | 23 | declare module "@envuso/core/AppContainer" { 24 | declare interface ConfigRepositoryContract { 25 | get(key: T, _default?: any): R; 26 | get(key: T, _default?: any): R; 27 | file(file: T, _default?: any): R; 28 | file(key: T, _default?: any): R; 29 | set(key: T, value: any): void; 30 | set(key: string, value: any): void; 31 | put(key: T, value: any): void; 32 | put(key: string, value: any): void; 33 | has(key: T): boolean; 34 | has(key: string): boolean; 35 | } 36 | declare function config(): ConfigRepositoryContract; 37 | declare function config(key: T): ConfigHelperKeys[T]; 38 | declare function config(key: string): any; 39 | } -------------------------------------------------------------------------------- /src/App/Http/Controllers/Auth/FrontendAuthenticationController.ts: -------------------------------------------------------------------------------- 1 | import {Auth} from "@envuso/core/Authentication"; 2 | import {Hash} from "@envuso/core/Crypt"; 3 | import {Inertia} from "@envuso/core/Packages/Inertia/Inertia"; 4 | import {back, Controller, controller, dto, get, post, redirect, response} from "@envuso/core/Routing"; 5 | import {session} from "@envuso/core/Session"; 6 | import {LoginRequestDto} from "../../../DataTransferObjects/Authentication/LoginRequestDto"; 7 | import {RegistrationRequestDto} from "../../../DataTransferObjects/Authentication/RegistrationRequestDto"; 8 | import {User} from "../../../Models/User"; 9 | 10 | @controller('/auth') 11 | export class FrontendAuthenticationController extends Controller { 12 | 13 | @post('/logout') 14 | async logout() { 15 | await session().invalidate(); 16 | 17 | return redirect('/auth/login') 18 | } 19 | 20 | @get('/login') 21 | showLogin() { 22 | return Inertia.render('Authentication/Login'); 23 | } 24 | 25 | @post('/login') 26 | async login(@dto() body: LoginRequestDto) { 27 | 28 | if (!await Auth.attempt(body)) { 29 | return back().with('errors', {email : 'The provided credentials do not match our records.'}); 30 | } 31 | 32 | const user = await User.find(body.email, 'email'); 33 | 34 | Auth.authoriseAs(user); 35 | 36 | return response().redirect('/dashboard'); 37 | } 38 | 39 | @get('/register') 40 | showRegister() { 41 | return Inertia.render('Authentication/Register'); 42 | } 43 | 44 | @post('/register') 45 | async register(@dto(false) body: RegistrationRequestDto) { 46 | 47 | if (await User.query().where('email', body.email.toLowerCase()).first()) { 48 | return back().with('errors', {email : 'This email has already been taken.'}); 49 | } 50 | 51 | await body.validate(); 52 | 53 | const user = new User(); 54 | user.email = body.email; 55 | user.name = body.name; 56 | user.password = await Hash.make(body.password); 57 | user.createdAt = new Date(); 58 | await user.save(); 59 | 60 | Auth.authoriseAs(user); 61 | 62 | return response().redirect('/dashboard'); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Resources/Views/Exceptions/exception.edge: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{title}} 9 | 10 | 72 | 73 | 74 |

{{ code ?? 500 }}

75 |

76 | {{title ?? "Well... this is awkward."}} 77 |

78 |

79 | {{ message ?? 'It looks like something is broken. We\'re sorry for that.' }} 80 |

81 | 82 | @if(stack !== null && isDevelopment()) 83 |
84 | {{ stack }} 85 |
86 | @end 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@envuso/framework", 3 | "version": "2.0.1", 4 | "scripts": { 5 | "front:build": "npx mix", 6 | "front:watch": "npx mix watch", 7 | "front:hot": "npx mix watch --hot", 8 | "start": "node dist/index.js", 9 | "serve": "ts-node-dev --respawn --rs --transpile-only ./src/index.ts ", 10 | "build": "tsc -b --clean && tsc -b && npx mix", 11 | "run": "ts-node ./src", 12 | "test": "jest" 13 | }, 14 | "dependencies": { 15 | "@envuso/core": "^2.0.6", 16 | "@headlessui/vue": "^1.4.2", 17 | "@heroicons/vue": "^1", 18 | "@tailwindcss/forms": "^0.3.4", 19 | "axios": "^0.24.0", 20 | "class-transformer": "^0.5.1", 21 | "class-validator": "^0.13.2", 22 | "fastify": "^3.25.0", 23 | "fastify-cors": "^6.0.2", 24 | "fastify-helmet": "^5.3.2", 25 | "fastify-multipart": "^5.2.1", 26 | "ioredis": "^4.28.2", 27 | "jsonwebtoken": "^8.5.1", 28 | "lodash": "^4.17.21", 29 | "random-bytes": "^1.0.0", 30 | "reflect-metadata": "^0.1.13", 31 | "regenerator-runtime": "^0.13.9", 32 | "tslib": "^2.3.1", 33 | "mongodb": "4.2.2" 34 | }, 35 | "devDependencies": { 36 | "@babel/plugin-proposal-decorators": "^7.16.5", 37 | "uWebSockets.js": "uNetworking/uWebSockets.js#v19.3.0", 38 | "@babel/preset-typescript": "^7.16.5", 39 | "@inertiajs/inertia": "^0.10.1", 40 | "@inertiajs/inertia-vue3": "^0.5.2", 41 | "@tsconfig/node14": "^1.0.1", 42 | "@types/es6-shim": "^0.31.42", 43 | "@types/generic-pool": "^3.1.10", 44 | "@types/jest": "^26.0.24", 45 | "@types/lodash": "^4.14.178", 46 | "@types/node": "^16.0.0", 47 | "@types/redis": "^2.8.32", 48 | "@vue/compiler-sfc": "^3.2.26", 49 | "autoprefixer": "^10.4.0", 50 | "babel-loader": "^8.2.3", 51 | "babel-plugin-replace-ts-export-assignment": "^0.0.2", 52 | "jest": "^26.6.3", 53 | "laravel-mix": "^6.0.39", 54 | "postcss": "^8.3.11", 55 | "tailwindcss": "^2.2.19", 56 | "ts-jest": "^26.5.6", 57 | "ts-loader": "^9.2.6", 58 | "ts-node": "^9.1.1", 59 | "ts-node-dev": "^1.1.8", 60 | "typescript": "^4.5.4", 61 | "vue": "^3.2.26", 62 | "vue-loader": "^16.8.3" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Config/AppConfiguration.ts: -------------------------------------------------------------------------------- 1 | import {ConfigurationCredentials} from "@envuso/core/AppContainer/Config/ConfigurationCredentials"; 2 | import Environment from "@envuso/core/AppContainer/Config/Environment"; 3 | import {ApplicationConfiguration} from "@envuso/core/Contracts/AppContainer/AppContract"; 4 | import { 5 | EncryptionServiceProvider, 6 | AuthenticationServiceProvider, 7 | RouteServiceProvider, 8 | StorageServiceProvider, 9 | DatabaseServiceProvider, 10 | ServerServiceProvider, 11 | SecurityServiceProvider, 12 | SessionServiceProvider, 13 | EventServiceProvider, 14 | AuthorizationServiceProvider, 15 | WebSocketsServiceProvider, 16 | } from "@envuso/core"; 17 | import {ServiceProviderContract} from "@envuso/core/Contracts/AppContainer/ServiceProviderContract"; 18 | import {ExceptionHandlerConstructorContract} from "@envuso/core/Contracts/Common/Exception/ExceptionHandlerContract"; 19 | import {InertiaServiceProvider} from "@envuso/core/Packages/Inertia/InertiaServiceProvider"; 20 | import {RedisServiceProvider} from "@envuso/core/Redis/RedisServiceProvider"; 21 | import {ExceptionHandler} from "../App/Exceptions/ExceptionHandler"; 22 | 23 | 24 | export class AppConfiguration extends ConfigurationCredentials implements ApplicationConfiguration { 25 | 26 | environment: string = Environment.get('NODE_ENV'); 27 | 28 | appKey = Environment.get('APP_KEY'); 29 | 30 | url: string = Environment.get('APP_URL', 'http://127.0.0.1:' + Environment.get('PORT', 3000)); 31 | 32 | providers: (new () => ServiceProviderContract)[] = [ 33 | SecurityServiceProvider, 34 | SessionServiceProvider, 35 | EventServiceProvider, 36 | RedisServiceProvider, 37 | DatabaseServiceProvider, 38 | EncryptionServiceProvider, 39 | AuthenticationServiceProvider, 40 | AuthorizationServiceProvider, 41 | RouteServiceProvider, 42 | StorageServiceProvider, 43 | ServerServiceProvider, 44 | InertiaServiceProvider, 45 | WebSocketsServiceProvider 46 | ]; 47 | 48 | exceptionHandler: ExceptionHandlerConstructorContract = ExceptionHandler; 49 | 50 | logging = { 51 | middleware : false, 52 | routes : false, 53 | controllers : false, 54 | providers : false, 55 | serverHooks : false, 56 | socketChannels : false, 57 | }; 58 | 59 | isDev() { 60 | return this.environment === 'development'; 61 | } 62 | 63 | isProd() { 64 | return this.environment === 'production'; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/Config/WebsocketsConfiguration.ts: -------------------------------------------------------------------------------- 1 | import {ServerOptions} from "ws"; 2 | import {AppOptions, CompressOptions, SHARED_COMPRESSOR} from "uWebSockets.js"; 3 | import {ConfigurationCredentials} from "@envuso/core/AppContainer/Config/ConfigurationCredentials"; 4 | import {MiddlewareContract} from "@envuso/core/Contracts/Routing/Middleware/MiddlewareContract"; 5 | 6 | export type uWsBehaviour = { 7 | /** Maximum length of received message. If a client tries to send you a message larger than this, the connection is immediately closed. Defaults to 16 * 1024. */ 8 | maxPayloadLength?: number; 9 | /** Maximum amount of seconds that may pass without sending or getting a message. Connection is closed if this timeout passes. Resolution (granularity) for timeouts are typically 4 seconds, rounded to closest. 10 | * Disable by using 0. Defaults to 120. 11 | */ 12 | idleTimeout?: number; 13 | /** What permessage-deflate compression to use. uWS.DISABLED, uWS.SHARED_COMPRESSOR or any of the uWS.DEDICATED_COMPRESSOR_xxxKB. Defaults to uWS.DISABLED. */ 14 | compression?: CompressOptions; 15 | /** Maximum length of allowed backpressure per socket when publishing or sending messages. Slow receivers with too high backpressure will be skipped until they catch up or timeout. Defaults to 1024 * 1024. */ 16 | maxBackpressure?: number; 17 | } 18 | 19 | export default class WebsocketsConfiguration extends ConfigurationCredentials { 20 | 21 | port: number = 3335; 22 | 23 | /** 24 | * Disable the websocket implementation 25 | * @deprecated You can just disable the WebSocketServiceProvider in AppConfiguration.ts 26 | */ 27 | enabled: boolean = true; 28 | 29 | middleware: (new () => MiddlewareContract)[] = [ 30 | // JwtAuthenticationMiddleware 31 | // AuthenticatedMiddleware, 32 | ]; 33 | 34 | options: ServerOptions = { 35 | clientTracking : false 36 | }; 37 | 38 | uWebsocketAppOptions: AppOptions = {}; 39 | 40 | uWebsocketServerBehaviour: uWsBehaviour = { 41 | /** Maximum length of received message. If a client tries to send you a message larger than this, the connection is immediately closed. Defaults to 16 * 1024. */ 42 | maxPayloadLength : 16 * 1024 * 1024, 43 | /** Maximum amount of seconds that may pass without sending or getting a message. Connection is closed if this timeout passes. Resolution (granularity) for timeouts are typically 4 seconds, rounded to closest. 44 | * Disable by using 0. Defaults to 120. 45 | */ 46 | idleTimeout : 32, 47 | /** What permessage-deflate compression to use. uWS.DISABLED, uWS.SHARED_COMPRESSOR or any of the uWS.DEDICATED_COMPRESSOR_xxxKB. Defaults to uWS.DISABLED. */ 48 | compression : SHARED_COMPRESSOR, 49 | /** Maximum length of allowed backpressure per socket when publishing or sending messages. Slow receivers with too high backpressure will be skipped until they catch up or timeout. Defaults to 1024 * 1024. */ 50 | maxBackpressure : null 51 | }; 52 | 53 | } 54 | 55 | -------------------------------------------------------------------------------- /src/Resources/Ts/Layouts/Elements/GuestNavigation.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | 91 | 92 | 95 | -------------------------------------------------------------------------------- /src/Resources/Ts/Pages/Authentication/Login.vue: -------------------------------------------------------------------------------- 1 | 68 | 69 | 103 | 104 | 107 | -------------------------------------------------------------------------------- /src/Config/ServerConfiguration.ts: -------------------------------------------------------------------------------- 1 | import Environment from "@envuso/core/AppContainer/Config/Environment"; 2 | import {ClassTransformOptions} from "class-transformer/types/interfaces"; 3 | import {FastifyPlugin, FastifyPluginOptions, FastifyServerOptions} from "fastify"; 4 | import {FastifyCorsOptions} from "fastify-cors"; 5 | import {default as FastifyMultipart, FastifyMultipartOptions} from "fastify-multipart"; 6 | import {ConfigurationCredentials} from "@envuso/core/AppContainer/Config/ConfigurationCredentials"; 7 | import {ServerConfiguration as ServerConfig} from "@envuso/core/Contracts/Server/ServerContract"; 8 | import {InjectViewGlobals} from "@envuso/core/Routing/Views/InjectViewGlobals"; 9 | import {StartSessionMiddleware} from "@envuso/core/Session/Middleware/StartSessionMiddleware"; 10 | import {SetInertiaSharedDataMiddleware} from "../App/Http/Middleware/SetInertiaSharedDataMiddleware"; 11 | import {BindRequestContextHook} from "@envuso/core/Server/InternalHooks/BindRequestContextHook"; 12 | import {ConvertEmptyStringsToNullHook} from "@envuso/core/Server/InternalHooks/ConvertEmptyStringsToNullHook"; 13 | import {InitiateRequestContextHook} from "@envuso/core/Server/InternalHooks/InitiateRequestContextHook"; 14 | import {ProcessUploadedFilesHook} from "@envuso/core/Server/InternalHooks/ProcessUploadedFilesHook"; 15 | import {SaveSessionHook} from "@envuso/core/Server/InternalHooks/SaveSessionHook"; 16 | import {SetResponseCookiesHook} from "@envuso/core/Server/InternalHooks/SetResponseCookiesHook"; 17 | 18 | export class ServerConfiguration extends ConfigurationCredentials implements ServerConfig { 19 | 20 | /** 21 | * The port that fastify will listen on 22 | */ 23 | port = Environment.get('PORT', 3000); 24 | 25 | /** 26 | * Global middleware that will run on every application request 27 | */ 28 | middleware = [ 29 | StartSessionMiddleware, 30 | InjectViewGlobals, 31 | SetInertiaSharedDataMiddleware, 32 | ]; 33 | 34 | /** 35 | * We have a custom wrapper of fastify's server hooks 36 | * This will allow us to extend fastify/framework logic a little 37 | * 38 | * Be warned, removing some of these may break some core logic handling of the server. 39 | * 40 | * @type {Array} 41 | */ 42 | hooks = [ 43 | BindRequestContextHook, 44 | InitiateRequestContextHook, 45 | ConvertEmptyStringsToNullHook, 46 | ProcessUploadedFilesHook, 47 | SetResponseCookiesHook, 48 | SaveSessionHook, 49 | ]; 50 | 51 | /** 52 | * Any cookie names that you wish to not encrypt/decrypt 53 | */ 54 | disableCookieEncryption = []; 55 | 56 | /** 57 | * Cors is automatically configured internally due to some framework 58 | * configuration that needs to align. But you can also adjust the 59 | * configuration you wish to use here. 60 | */ 61 | cors = { 62 | enabled : true, 63 | options : { 64 | origin : (origin: string, callback) => { 65 | callback(null, true); 66 | }, 67 | credentials : true, 68 | } as FastifyCorsOptions 69 | }; 70 | 71 | /** 72 | * Server providers are Fastify Plugins that you register to the server when it's booted. 73 | */ 74 | fastifyPlugins: Array<[FastifyPlugin, FastifyPluginOptions]> = [ 75 | [FastifyMultipart, {} as FastifyMultipartOptions], 76 | [require('fastify-helmet'), {contentSecurityPolicy : false}] 77 | ]; 78 | 79 | /** 80 | * Any options to pass to fastify when it boots 81 | * 82 | */ 83 | fastifyOptions: FastifyServerOptions = {}; 84 | 85 | /** 86 | * Before we return a response we serialize the result, mainly 87 | * so that class transformer can do it's work, but also to help 88 | * with random errors that occur from circular references. 89 | * 90 | * excludeExtraneousValues can induce results that you might not 91 | * expect but helps prevent internal references used in your code 92 | * and the framework from being returned in a response. 93 | */ 94 | responseSerialization: ClassTransformOptions = { 95 | enableCircularCheck : true, 96 | strategy : "exposeAll", 97 | // excludeExtraneousValues : true, 98 | }; 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/Resources/Ts/Pages/Authentication/Register.vue: -------------------------------------------------------------------------------- 1 | 121 | 122 | 158 | 159 | 162 | -------------------------------------------------------------------------------- /src/Resources/Ts/Layouts/Elements/MainNavigation.vue: -------------------------------------------------------------------------------- 1 | 109 | 110 | 167 | 168 | 171 | --------------------------------------------------------------------------------