Browse Source

Organized web service. Added detailed frontend/backend form validation.

master
David Ludwig 4 years ago
parent
commit
a0b74795f3
28 changed files with 558 additions and 94 deletions
  1. +2
    -0
      .env.example
  2. +2
    -2
      nodemon.json
  3. +5
    -1
      package.json
  4. +2
    -2
      src/app/App.vue
  5. +75
    -15
      src/app/components/TextBox.vue
  6. +5
    -0
      src/app/util.ts
  7. +0
    -0
      src/app/validation.ts
  8. +1
    -1
      src/app/views/Login.vue
  9. +104
    -24
      src/app/views/Register.vue
  10. +53
    -0
      src/common/validation.ts
  11. +6
    -4
      src/server/Application.ts
  12. +7
    -0
      src/server/database/entities/RegisterToken.ts
  13. +3
    -6
      src/server/database/entities/User.ts
  14. +8
    -0
      src/server/services/Database.ts
  15. +20
    -5
      src/server/services/DiscordBot.ts
  16. +5
    -2
      src/server/services/Service.ts
  17. +5
    -5
      src/server/services/TorrentClientIpc.ts
  18. +35
    -12
      src/server/services/WebServer/index.ts
  19. +37
    -0
      src/server/services/WebServer/requests/RegisterRequest.ts
  20. +56
    -0
      src/server/services/WebServer/requests/Request.ts
  21. +24
    -0
      src/server/services/WebServer/requests/index.ts
  22. +24
    -0
      src/server/services/WebServer/validators.ts
  23. +11
    -0
      src/server/util.ts
  24. +1
    -1
      tailwind.config.js
  25. +7
    -3
      tsconfig.json
  26. +7
    -3
      tsconfig.server.json
  27. +0
    -1
      vite.config.ts
  28. +53
    -7
      yarn.lock

+ 2
- 0
.env.example View File

@ -7,3 +7,5 @@ DB_DATABASE = autoplex_request
SERVER_PORT = 3200
TORRENT_CLIENT_IPC_SOCKET = /tmp/torrent_client.sock
DISCORD_BOT_TOKEN=

+ 2
- 2
nodemon.json View File

@ -1,8 +1,8 @@
{
"watch": ["src/server"],
"watch": ["src/common", "src/server"],
"ext": "ts,json",
"ignore": ["src/**/*.spec.ts"],
"exec": "node --inspect=0.0.0.0:9229 -r ts-node/register src/server/index.ts",
"exec": "node --inspect=0.0.0.0:9229 -r tsconfig-paths/register -r ts-node/register src/server/index.ts",
"events": {
"start": "clear"
}


+ 5
- 1
package.json View File

@ -9,7 +9,7 @@
"clean": "rimraf ./build",
"dev": "vite",
"build": "yarn run build:backend && yarn run build:frontend",
"build:backend": "tsc -p ./tsconfig.server.json",
"build:backend": "ttsc -P ./tsconfig.server.json",
"build:frontend": "vue-tsc --noEmit -p ./tsconfig.vite.json && vite build",
"start": "NODE_ENV=production node .",
"start:dev": "nodemon"
@ -27,6 +27,7 @@
"node-ipc": "^9.1.4",
"tvdb-v4": "^1.0.0",
"typeorm": "^0.2.32",
"validate.js": "^0.13.1",
"vue": "^3.0.5",
"vue-router": "^4.0.6",
"vuedraggable": "^4.0.1",
@ -37,12 +38,15 @@
"@types/node-ipc": "^9.1.3",
"@vitejs/plugin-vue": "^1.2.1",
"@vue/compiler-sfc": "^3.0.5",
"@zerollup/ts-transform-paths": "^1.7.18",
"autoprefixer": "^10.2.5",
"nodemon": "^2.0.7",
"postcss": "^8.2.9",
"rimraf": "^3.0.2",
"tailwindcss": "^2.1.1",
"ts-node": "^9.1.1",
"tsconfig-paths": "^3.9.0",
"ttypescript": "^1.5.12",
"typescript": "^4.1.3",
"vite": "^2.1.5",
"vue-tsc": "^0.0.15"


+ 2
- 2
src/app/App.vue View File

@ -5,6 +5,6 @@
</template>
<script lang="ts">
// import { defineComponent } from 'vue'
// export default defineComponent({});
import { defineComponent } from 'vue';
export default defineComponent({});
</script>

+ 75
- 15
src/app/components/TextBox.vue View File

@ -4,25 +4,83 @@
{{ label }}
</label>
<div class="block relative">
<input :type="type" :placeholder="placeholder" v-model="inputValue" ref="textbox" :disabled="disabled"
<input :type="type" :placeholder="placeholder" v-model="value" ref="textbox" :disabled="disabled"
class="w-full outline-none p-2 rounded-lg bg-gray-100 border border-gray-100 text-gray-800 placeholder-gray-400 disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-gray-50 disabled:border-gray-400"
:class="{ 'pr-10 border-red-600 text-red-600': (valid || error) }">
<span class="w-10 absolute flex items-center top-0 bottom-0 right-0 justify-center">
:class="{ 'pr-10': (isValid || error), 'border-red-600 text-red-600': error }"
@blur="onBlur" @change="onChange" @input="setErrorMessage('')">
<span class="w-10 absolute flex items-center top-0 bottom-0 right-0 justify-center" :class="{ 'hidden': !error && !isValid }">
<i v-if="error" class="fas fa-exclamation-circle align-middle text-red-600"></i>
<i v-else-if="valid" class="fas fa-check-circle align-middle text-green-500"></i>
<i v-else-if="isValid" class="fas fa-check-circle align-middle text-green-500"></i>
</span>
</div>
<div class="w-full error-message text-xs text-red-600 text-center" ref="error">{{ errorMessage }}</div>
<div class="w-full error-message text-xs text-red-600 text-center" ref="error">{{ error }}</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import { validateValue } from "../util";
export default defineComponent({
emits: ["onBlur", "onChange", "update:modelValue", "update:errorValue"],
data() {
return {
inputValue: ""
error: "",
isValid: <boolean|undefined> this.valid,
value: this.modelValue
}
},
methods: {
/**
* Invoked when the input has lost focus
*/
onBlur(event: Event) {
this.$emit("onBlur", event);
this.validate();
},
/**
* Invoked when the value of the input has changed
*/
onChange(event: Event) {
let value: string = (<any>event.target).value;
this.$emit("update:modelValue", value);
this.$emit("onChange", event);
},
/**
* Set the displayed error message
*/
setErrorMessage(value: string) {
this.error = value;
this.$emit("update:errorValue", value);
if (value) {
this.isValid = false;
}
},
/**
* Validate the current input
*/
async validate() {
this.isValid = false;
if (this.validator !== undefined) {
if (typeof this.validator === "function") {
this.error = (await this.validator(this.value)) ?? "";
} else {
this.error = validateValue(this.value, this.validator);
}
if (!this.error) {
this.isValid = true;
}
} else {
this.isValid = true;
}
}
},
mounted() {
if (this.value && this.validator) {
this.validate();
}
},
props: {
@ -30,27 +88,29 @@ export default defineComponent({
type: Boolean,
default: false
},
error: {
type: Boolean,
default: false
},
errorMessage: {
errorValue: {
type: String,
default: ""
},
label: String,
modelValue: {
type: String,
default: ""
},
placeholder: {
type: String,
default: ""
},
required: [Boolean, String],
valid: {
type: Boolean,
default: undefined
},
type: {
type: String,
default: "text"
},
valid: {
type: Boolean,
default: false
}
validator: [Function, Object]
}
});
</script>

+ 5
- 0
src/app/util.ts View File

@ -0,0 +1,5 @@
import { single as validate } from "validate.js";
export function validateValue(value: string, constraints: any) {
return <string>(validate(value || null, constraints) ?? [""])[0];
}

+ 0
- 0
src/app/validation.ts View File


+ 1
- 1
src/app/views/Login.vue View File

@ -1,5 +1,5 @@
<template>
<div class="w-full max-w-sm lg:mx-auto lg:my-auto p-6 space-y-4 bg-white rounded-lg shadow-md">
<div class="w-full sm:max-w-sm sm:mx-auto sm:my-auto p-6 space-y-4 bg-white rounded-lg shadow-md">
<div class="text-center font-thin text-4xl py-4">AUTOPLEX</div>
<div class="font-medium text-center text-xl">Sign in</div>
<form>


+ 104
- 24
src/app/views/Register.vue View File

@ -1,26 +1,32 @@
<template>
<div class="w-full max-w-sm lg:mx-auto lg:my-auto p-6 space-y-4 bg-white rounded-lg shadow-md">
<div class="w-full sm:max-w-sm sm:mx-auto sm:my-auto p-6 space-y-4 bg-white rounded-lg shadow-md">
<div class="text-center font-thin text-4xl py-4">AUTOPLEX</div>
<div class="font-medium text-center text-xl">Sign Up</div>
<form>
<form @submit.prevent="register">
<div class="space-y-4">
<div>
<text-box label="Acess Token" type="text" ref="token" disabled/>
<text-box label="Access Token" type="text" disabled
v-model="fields.token" :validator="validateToken" ref="token"/>
</div>
<div>
<text-box label="Your Name" type="text" ref="name" placeholder="John Doe" :error-message="errors.email"/>
<text-box label="Your Name" type="text" placeholder="John Doe" :disabled="isSubmitting"
v-model="fields.name" :validator="constraints.name" ref="name"/>
</div>
<div>
<text-box label="Email" type="email" ref="email" placeholder="john@example.com" :error-message="errors.email"/>
<text-box label="Email" type="email" placeholder="john@example.com" :disabled="isSubmitting"
v-model="fields.email" :validator="validateEmail" ref="email"/>
</div>
<div>
<text-box label="Password" type="password" ref="password" placeholder="············" :error-message="errors.password"/>
<text-box label="Password" type="password" placeholder="············" :disabled="isSubmitting"
v-model="fields.password" :validator="constraints.password" ref="password"
/>
</div>
<div>
<text-box label="Re-type Password" type="password" ref="password" placeholder="············" :error-message="errors.password"/>
<text-box label="Re-type Password" type="password" placeholder="············" :disabled="isSubmitting"
v-model="fields.retypePassword" :validator="validateRetypePassword" ref="retypePassword"/>
</div>
<div>
<button type="submit" class="block w-full rounded-full bg-indigo-500 text-white p-2 focus:outline-none">Register</button>
<button type="submit" class="block w-full rounded-full bg-indigo-500 text-white p-3 focus:outline-none" :disabled="isSubmitting">Register</button>
</div>
</div>
</form>
@ -28,39 +34,113 @@
</template>
<script lang="ts">
import { defineComponent } from "vue";
import { defineComponent, reactive } from "vue";
import CheckBox from "../components/CheckBox.vue";
import TextBox from "../components/TextBox.vue";
import { constraints } from "../../common/validation";
import { validateValue } from "../util";
export default defineComponent({
components: {
CheckBox,
TextBox
},
computed: {
accessToken(): string {
return <string>this.$route.query["access_token"];
}
},
data() {
return {
errors: {
email: "",
password: ""
isSubmitting : false,
fields: reactive({
token : <string>this.$route.query["token"],
name : "",
email : "",
password : "",
retypePassword: "",
}),
constraints: {
name: constraints.name,
email: constraints.email,
password: constraints.password
}
}
},
methods: {
login() {
/**
* Submit the registration form
*/
async register() {
if (this.isSubmitting) {
return;
}
this.isSubmitting = true;
fetch(`/auth/register`, {
method: "post",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(this.fields)
})
.then(async response => {
this.isSubmitting = false;
if (response.status !== 200) {
let body = await response.json();
if (body.errors) {
for (let fieldName in this.fields) {
let field = <any>this.$refs[fieldName];
let message = <string>(body.errors[fieldName] ?? [""])[0];
field.setErrorMessage(message);
}
}
return;
}
// Register
})
.catch(e => {
console.error("Error occurred during submission:", e);
this.isSubmitting = false;
});
},
/**
* Validate the provided registration token
*/
async validateToken(token: string) {
let response = await fetch(`/auth/register/validate_token/${token}`, { method: "post" });
if (response.status !== 200) {
return "A valid token is required to register";
}
},
/**
* Validate the provided email address field
*/
async validateEmail(email: string) {
let error = validateValue(email, constraints.email);
if (error) {
return error;
}
// Check email availability
let response = await fetch(`/auth/register/available_email/${email}`, { method: "post" });
if (response.status !== 200) {
return "An account with that email address already exists";
}
},
/**
* Validate the provided retype-password field
*/
validateRetypePassword(value: string) {
if (value.trim().length == 0) {
return constraints.retypePassword.presence.message;
}
if (value !== this.fields.password) {
return constraints.retypePassword.equality.message;
}
}
},
mounted() {
(<any>this.$refs["token"]).inputValue = this.accessToken;
console.log("The token is", this.accessToken);
},
/**
* Navigation Guard
*/
beforeRouteEnter(to, from, next) {
if (to.query["access_token"] === undefined) {
if (to.query["token"] === undefined) {
next({ name: "Login" });
} else {
next();
@ -69,7 +149,7 @@ export default defineComponent({
});
</script>
<style>
<style lang="postcss">
button:focus-visible {
@apply ring-2 ring-indigo-500 ring-offset-2;
}


+ 53
- 0
src/common/validation.ts View File

@ -0,0 +1,53 @@
export const constraints = {
token: {
presence: {
message: "A valid token is required to register"
},
token: {
message: "A valid token is required to register"
}
},
name: {
presence: {
allowEmpty: false,
message: "Your name is required"
},
length: {
maximum: 50,
tooLong: "Your name cannot exceed 50 characters"
}
},
email: {
presence: {
allowEmpty: false,
message: "Your email is required"
},
length: {
maximum: 255,
tooLong: "An email address cannot exceed 255 characters"
},
email: {
message: "A valid email address is required"
}
},
password: {
presence: {
allowEmpty: false,
message: "A password is required"
},
length: {
minimum: 8,
tooShort: "Password should be at least 8 characters"
}
},
retypePassword: {
presence: {
allowEmpty: false,
message: "Re-type your password to confirm it"
},
equality: {
attribute: "password",
message: "Passwords must match"
}
}
};

+ 6
- 4
src/server/Application.ts View File

@ -31,22 +31,22 @@ export default class Application
/**
* The database service
*/
protected database: Database;
protected database!: Database;
/**
* The discord bot service
*/
// protected discord: DiscordBot;
protected discord!: DiscordBot;
/**
* The webserver service
*/
protected web: WebServer;
protected web!: WebServer;
/**
* The torrent client service
*/
// protected torrent: TorrentClientIpc;
protected torrent!: TorrentClientIpc;
public constructor() {
this.services = [
@ -55,6 +55,8 @@ export default class Application
this.web = new WebServer(this),
// this.torrent = new TorrentClientIpc(this)
]
// this.torrent.logging = false;
}
/**


+ 7
- 0
src/server/database/entities/RegisterToken.ts View File

@ -8,4 +8,11 @@ export class RegisterToken extends BaseEntity
@Column()
token!: string
public static async isValid(token: string) {
if (typeof token !== "string") {
return false;
}
return Boolean(token) && await RegisterToken.count({token}) > 0;
}
}

+ 3
- 6
src/server/database/entities/User.ts View File

@ -8,16 +8,13 @@ export class User extends BaseEntity
id!: number;
@Column()
imdbId!: string;
name!: string;
@Column()
firstName!: string;
email!: string;
@Column()
lastName!: string;
@Column()
number!: number;
password!: string;
@OneToMany(() => User, user => user.movieTickets)
movieTickets!: MovieTicket[];


+ 8
- 0
src/server/services/Database.ts View File

@ -2,6 +2,7 @@ import { Connection, createConnection } from "typeorm";
import * as entities from "../database/entities";
import Service from "./Service";
import { readFile } from "fs/promises";
import Application from "../Application";
export default class Database extends Service
{
@ -10,6 +11,13 @@ export default class Database extends Service
*/
protected connection!: Connection;
/**
* Create a new database instance
*/
public constructor(app: Application) {
super("Database", app);
}
/**
* Boot the database service
*/


+ 20
- 5
src/server/services/DiscordBot.ts View File

@ -1,18 +1,33 @@
import Application from "../Application";
import Service from "./Service";
import { Client } from "discord.js";
export default class DiscordBot extends Service
{
/**
* The internal Discord bot instance
*/
protected bot: Client;
/**
* Create a new Discord bot instance
*/
public constructor(app: Application) {
super(app);
super("Discord Bot", app);
this.bot = new Client();
}
/**
* Boot the discord bot
*/
public async boot() {
await this.bot.login(process.env["DISCORD_BOT_TOKEN"]);
this.log("Discord bot ready");
}
/**
* Shutdown the discord bot
*/
public async shutdown() {
this.bot.destroy();
}
}

+ 5
- 2
src/server/services/Service.ts View File

@ -2,6 +2,8 @@ import Application from "../Application";
export default abstract class Service
{
private readonly __name: string;
/**
* The application instance
*/
@ -15,8 +17,9 @@ export default abstract class Service
/**
* Create a new service
*/
public constructor(app: Application) {
public constructor(name: string, app: Application) {
this.app = app;
this.__name = name;
}
// Required Service Implementation -------------------------------------------------------------
@ -47,6 +50,6 @@ export default abstract class Service
if (!this.logging) {
return;
}
console.log(...args);
console.log(`[${this.__name}]:`, ...args);
}
}

+ 5
- 5
src/server/services/TorrentClientIpc.ts View File

@ -25,7 +25,7 @@ export default class TorrentClientIpc extends Service
* Create a new IPC client for the torrent client
*/
constructor(app: Application) {
super(app);
super("Torrent Client IPC", app);
ipc.config.id = "request";
ipc.config.retry = 1500;
ipc.config.silent = true;
@ -67,21 +67,21 @@ export default class TorrentClientIpc extends Service
// Socket Event Handlers -----------------------------------------------------------------------
protected onConnect() {
console.log("IPC: Connection established");
this.log("IPC: Connection established");
this.__isConnected = true;
}
protected onError(error: string | Error) {
console.log("IPC: Error occurred:", error);
this.log("IPC: Error occurred:", error);
}
protected onDisconnect() {
console.log("IPC: Disconnected");
this.log("IPC: Disconnected");
this.__isConnected = false;
}
protected onDestroy() {
console.log("IPC: Destroyed");
this.log("IPC: Destroyed");
}
// Methods -------------------------------------------------------------------------------------


src/server/services/WebServer.ts → src/server/services/WebServer/index.ts View File


+ 37
- 0
src/server/services/WebServer/requests/RegisterRequest.ts View File

@ -0,0 +1,37 @@
import { FastifyRequest } from "fastify";
import validate from "validate.js";
import { RegisterToken } from "@server/database/entities";
import { hasAllProperties } from "@server/util";
import { constraints } from "@common/validation";
import Request from "./Request";
interface IRegisterFormBody {
token: string,
name: string,
email: string,
password: string,
retypePassword: string
}
export default class RegisterRequest extends Request
{
/**
* Validate the request
*/
public validate(request: FastifyRequest) {
// let body = <IRegisterFormBody>request.body;
// if (!RegisterToken.isValid(body.token)) {
// return {
// "token": "Invalid token"
// }
// }
console.log("Validating a request");
return validate.async(request.body, {
token: constraints.token,
name: constraints.name,
email: constraints.email,
password: constraints.password,
retypePassword: constraints.retypePassword
}, <any>{ fullMessages: false });
}
}

+ 56
- 0
src/server/services/WebServer/requests/Request.ts View File

@ -0,0 +1,56 @@
import { FastifyRequest, FastifyReply } from "fastify";
export default class Request
{
/**
* Handle the incoming request
*/
public async handle(request: FastifyRequest, reply: FastifyReply) {
if (!this.checkFormat(request)) {
reply.status(401);
return {
status: "unauthorized"
};
}
if (!this.checkFormat(request)) {
reply.status(400);
return {
status: "bad request"
};
}
try {
await this.validate(request);
} catch(errors) {
reply.status(422);
return {
status: "unprocessable entities",
errors
};
}
}
// Overridable ---------------------------------------------------------------------------------
/**
* Check if the user is authorized to make this request
*/
public isAuthorized() {
return true;
}
/**
* Check the format of the given request
*/
public checkFormat(request: FastifyRequest) {
return true;
}
/**
* Validate the request and return any errors
*/
public async validate(request: FastifyRequest): Promise<any|undefined> {
return undefined;
}
}
Request.apply(undefined);

+ 24
- 0
src/server/services/WebServer/requests/index.ts View File

@ -0,0 +1,24 @@
import Request from "./Request";
import { RouteHandlerMethod } from "fastify";
// import { IncomingMessage, Server, ServerResponse } from "http";
// type Return = RouteHandlerMethod<Server, IncomingMessage, ServerResponse>;
type RequestConstructor = new () => Request;
export default function handle(requests: RequestConstructor[], handle: RouteHandlerMethod): RouteHandlerMethod
{
return async (fastifyRequest, fastifyReply) => {
// Request parsing
for (let requestClass of requests) {
let request = new requestClass();
let response = await request.handle(fastifyRequest, fastifyReply);
if (response) {
fastifyReply.send(response);
return;
}
}
// Requests have been parsed/handled successfully, proceed to process the request
await (<any>handle)(fastifyRequest, fastifyReply);
}
}

+ 24
- 0
src/server/services/WebServer/validators.ts View File

@ -0,0 +1,24 @@
import { RegisterToken } from "@server/database/entities";
import validate from "validate.js";
interface IValidTokenOptions {
message: string
}
/**
* Validate a registration token
*/
validate.validators.token = async function(value: any, options: IValidTokenOptions) {
if (typeof options === "boolean") {
options = <any>{};
}
Object.assign({
message: "is not a valid token"
}, options);
if (!(await RegisterToken.isValid(value))) {
return options.message;
}
}
console.log("Custom validators installed");

+ 11
- 0
src/server/util.ts View File

@ -0,0 +1,11 @@
export function hasAllProperties(obj: any, properties: string[]) {
if (Object.keys(obj).length !== properties.length) {
return false;
}
for (let key of properties) {
if (!(key in obj)) {
return false;
}
}
return true;
}

+ 1
- 1
tailwind.config.js View File

@ -1,5 +1,5 @@
module.exports = {
purge: ["./index.html", "src/app/*.{vue,ts}"],
purge: ["./index.html", "src/app/**/*.{vue,ts}"],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {


+ 7
- 3
tsconfig.json View File

@ -21,7 +21,7 @@
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
"downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
@ -44,8 +44,12 @@
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
"baseUrl": "./src", /* Base directory to resolve non-absolute module names. */
"paths": { /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
"@app/*": ["app/*"],
"@common/*": ["common/*"],
"@server/*": ["server/*"],
},
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */


+ 7
- 3
tsconfig.server.json View File

@ -3,10 +3,14 @@
"compilerOptions": {
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
"outDir": "build/server", /* Redirect output structure to the directory. */
"sourceRoot": "src/server" /* Specify the location where debugger should locate TypeScript files instead of source locations. */
"outDir": "build", /* Redirect output structure to the directory. */
"sourceRoot": "src/server", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
"plugins": [
{ "transform": "ts-transform.r-keys/transformer" },
{ "transform": "@zerollup/ts-transform-paths" }
]
},
"exclude": [
"src/app"
]
],
}

+ 0
- 1
vite.config.ts View File

@ -1,6 +1,5 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from "path";
// https://vitejs.dev/config/
export default defineConfig({


+ 53
- 7
yarn.lock View File

@ -98,6 +98,11 @@
dependencies:
defer-to-connect "^1.0.1"
"@types/json5@^0.0.29":
version "0.0.29"
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
"@types/jsonwebtoken@^8.5.1":
version "8.5.1"
resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#56958cb2d80f6d74352bd2e501a018e2506a8a84"
@ -205,6 +210,20 @@
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.0.11.tgz#20d22dd0da7d358bb21c17f9bde8628152642c77"
integrity sha512-b+zB8A2so8eCE0JsxjL24J7vdGl8rzPQ09hZNhystm+KqSbKcAej1A+Hbva1rCMmTTqA+hFnUSDc5kouEo0JzA==
"@zerollup/ts-helpers@^1.7.18":
version "1.7.18"
resolved "https://registry.yarnpkg.com/@zerollup/ts-helpers/-/ts-helpers-1.7.18.tgz#747177f6d5abc06c3a0f5dffe7362d365cf0391d"
integrity sha512-S9zN+y+i5yN/evfWquzSO3lubqPXIsPQf6p9OiPMpRxDx/0totPLF39XoRw48Dav5dSvbIE8D2eAPpXXJxvKwg==
dependencies:
resolve "^1.12.0"
"@zerollup/ts-transform-paths@^1.7.18":
version "1.7.18"
resolved "https://registry.yarnpkg.com/@zerollup/ts-transform-paths/-/ts-transform-paths-1.7.18.tgz#72f705c66690879e51d53c73dc76c4e2518a8c50"
integrity sha512-YPVUxvWQVzRx1OBN0Pmkd58+R9FcfUJuwTaPUSoi5rKxuXMtxevTXdfi0w5mEaIH8b0DfL+wg0wFDHiJE+S2zA==
dependencies:
"@zerollup/ts-helpers" "^1.7.18"
abbrev@1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
@ -902,9 +921,9 @@ ee-first@1.1.1:
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
electron-to-chromium@^1.3.712:
version "1.3.712"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.712.tgz#ae467ffe5f95961c6d41ceefe858fc36eb53b38f"
integrity sha512-3kRVibBeCM4vsgoHHGKHmPocLqtFAGTrebXxxtgKs87hNUzXrX2NuS3jnBys7IozCnw7viQlozxKkmty2KNfrw==
version "1.3.713"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.713.tgz#4583efb17f2d1e9ec07a44c8004ea73c013ad146"
integrity sha512-HWgkyX4xTHmxcWWlvv7a87RHSINEcpKYZmDMxkUlHcY+CJcfx7xEfBHuXVsO1rzyYs1WQJ7EgDp2CoErakBIow==
emoji-regex@^7.0.1:
version "7.0.3"
@ -2327,7 +2346,7 @@ require-directory@^2.1.1:
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
resolve@^1.19.0, resolve@^1.20.0:
resolve@>=1.9.0, resolve@^1.12.0, resolve@^1.19.0, resolve@^1.20.0:
version "1.20.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
@ -2372,9 +2391,9 @@ rimraf@^3.0.2:
glob "^7.1.3"
rollup@^2.38.5:
version "2.45.1"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.45.1.tgz#eae2b94dc2088b4e0a3b7197a5a1ee0bdd589d5c"
integrity sha512-vPD+JoDj3CY8k6m1bLcAFttXMe78P4CMxoau0iLVS60+S9kLsv2379xaGy4NgYWu+h2WTlucpoLPAoUoixFBag==
version "2.45.2"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.45.2.tgz#8fb85917c9f35605720e92328f3ccbfba6f78b48"
integrity sha512-kRRU7wXzFHUzBIv0GfoFFIN3m9oteY4uAsKllIpQDId5cfnkWF2J130l+27dzDju0E6MScKiV0ZM5Bw8m4blYQ==
optionalDependencies:
fsevents "~2.3.1"
@ -2614,6 +2633,11 @@ strip-ansi@^6.0.0:
dependencies:
ansi-regex "^5.0.0"
strip-bom@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=
strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
@ -2741,11 +2765,28 @@ ts-node@^9.1.1:
source-map-support "^0.5.17"
yn "3.1.1"
tsconfig-paths@^3.9.0:
version "3.9.0"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b"
integrity sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==
dependencies:
"@types/json5" "^0.0.29"
json5 "^1.0.1"
minimist "^1.2.0"
strip-bom "^3.0.0"
tslib@^2.1.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c"
integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==
ttypescript@^1.5.12:
version "1.5.12"
resolved "https://registry.yarnpkg.com/ttypescript/-/ttypescript-1.5.12.tgz#27a8356d7d4e719d0075a8feb4df14b52384f044"
integrity sha512-1ojRyJvpnmgN9kIHmUnQPlEV1gq+VVsxVYjk/NfvMlHSmYxjK5hEvOOU2MQASrbekTUiUM7pR/nXeCc8bzvMOQ==
dependencies:
resolve ">=1.9.0"
tvdb-v4@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/tvdb-v4/-/tvdb-v4-1.0.0.tgz#e62c9b28abdf78f2438af390f35f2453884d4cb2"
@ -2896,6 +2937,11 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
validate.js@^0.13.1:
version "0.13.1"
resolved "https://registry.yarnpkg.com/validate.js/-/validate.js-0.13.1.tgz#b58bfac04a0f600a340f62e5227e70d95971e92a"
integrity sha512-PnFM3xiZ+kYmLyTiMgTYmU7ZHkjBZz2/+F0DaALc/uUtVzdCt1wAosvYJ5hFQi/hz8O4zb52FQhHZRC+uVkJ+g==
vite@^2.1.5:
version "2.1.5"
resolved "https://registry.yarnpkg.com/vite/-/vite-2.1.5.tgz#4857da441c62f7982c83cbd5f42a00330f20c9c1"


Loading…
Cancel
Save