fixup! add most entity for switch to sql

This commit is contained in:
Michael Schramm 2021-04-30 15:21:48 +02:00
parent 8d05f15b4c
commit 93b6596b1b
171 changed files with 1723 additions and 2545 deletions

21
ormconfig_pg.json Normal file
View File

@ -0,0 +1,21 @@
{
"type": "postgres",
"host": "localhost",
"username": "root",
"password": "root",
"database": "ohmyform",
"synchronize": true,
"logging": false,
"entities": [
"src/entity/**/*.ts"
],
"migrations": [
"src/migrations/**/*.ts"
],
"subscribers": [
"src/subscriber/**/*.ts"
],
"cli": {
"migrationsDir": "src/migrations"
}
}

15
ormconfig_sqlite.json Normal file
View File

@ -0,0 +1,15 @@
{
"type": "sqlite",
"database": "data.sqlite",
"synchronize": true,
"logging": false,
"entities": [
"src/entity/**/*.ts"
],
"migrations": [
"src/migrations/**/*.ts"
],
"cli": {
"migrationsDir": "src/migrations"
}
}

View File

@ -24,13 +24,13 @@
"typeorm:pg": "cross-env TS_NODE_TRANSPILE_ONLY=true ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js -f ormconfig_pg.json"
},
"dependencies": {
"@apollo/gateway": "^0.27.1",
"@nestjs-modules/mailer": "^1.6.0",
"@nestjs/common": "^7.6.15",
"@nestjs/config": "^0.6.3",
"@nestjs/core": "^7.6.15",
"@nestjs/graphql": "^7.10.6",
"@nestjs/jwt": "^7.2.0",
"@nestjs/mongoose": "^7.2.4",
"@nestjs/passport": "^7.1.5",
"@nestjs/platform-express": "^7.6.15",
"@nestjs/serve-static": "^2.1.4",
@ -52,9 +52,7 @@
"inquirer": "^8.0.0",
"ioredis": "^4.27.1",
"matomo-tracker": "^2.2.4",
"migrate-mongoose": "^4.0.0",
"mjml": "^4.9.1",
"mongoose": "^5.12.6",
"mysql2": "^2.2.5",
"nestjs-console": "^4.0.0",
"nestjs-pino": "^1.4.0",
@ -75,13 +73,11 @@
"@nestjs/schematics": "^7.3.1",
"@nestjs/testing": "^7.6.15",
"@types/bcrypt": "^3.0.1",
"@types/express": "^4.17.11",
"@types/express-serve-static-core": "^4.17.19",
"@types/handlebars": "^4.1.0",
"@types/html-to-text": "^6.0.0",
"@types/inquirer": "^7.3.1",
"@types/jest": "26.0.23",
"@types/mongoose": "^5.10.5",
"@types/node": "^15.0.1",
"@types/passport-jwt": "^3.0.5",
"@types/passport-local": "^1.0.33",

View File

@ -1,21 +1,17 @@
import { MailerModule } from '@nestjs-modules/mailer';
import { HttpModule, RequestMethod } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { GraphQLModule } from '@nestjs/graphql';
import { JwtModule, JwtModuleOptions } from '@nestjs/jwt';
import { MongooseModule } from '@nestjs/mongoose';
import { MongooseModuleOptions } from '@nestjs/mongoose/dist/interfaces/mongoose-options.interface';
import { ServeStaticModule } from '@nestjs/serve-static';
import { MailerModule } from '@nestjs-modules/mailer'
import { HttpModule, RequestMethod } from '@nestjs/common'
import { ConfigModule, ConfigService } from '@nestjs/config'
import { GraphQLModule } from '@nestjs/graphql'
import { JwtModule, JwtModuleOptions } from '@nestjs/jwt'
import { ServeStaticModule } from '@nestjs/serve-static'
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm'
import crypto from 'crypto';
import { Request } from 'express-serve-static-core';
import { IncomingHttpHeaders } from 'http';
import { ConsoleModule } from 'nestjs-console';
import { LoggerModule, Params as LoggerModuleParams } from 'nestjs-pino/dist';
import { join } from 'path';
import { DatabaseType } from 'typeorm'
import crypto from 'crypto'
import { Request } from 'express-serve-static-core'
import { IncomingHttpHeaders } from 'http'
import { ConsoleModule } from 'nestjs-console'
import { LoggerModule, Params as LoggerModuleParams } from 'nestjs-pino/dist'
import { join } from 'path'
import { entities } from './entity'
import { schema } from './schema';
export const LoggerConfig: LoggerModuleParams = {
pinoHttp: {
@ -110,31 +106,19 @@ export const imports = [
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService): TypeOrmModuleOptions => ({
name: 'ohmyform',
url: configService.get<string>('DB_URI', 'sqlite://data.sqlite') as any,
entityPrefix: configService.get<string>('DB_TABLE_PREFIX', ''),
logging: configService.get<string>('DB_LOGGING', 'false') === 'true',
entities,
migrationsTableName: 'nest_migrations',
migrations: [
`${__dirname}/**/migrations/**/*{.ts,.js}`,
],
migrationsRun: configService.get<boolean>('DB_MIGRATE', true),
}),
name: 'ohmyform',
type: configService.get<string>('DB_TYPE', 'sqlite') as any,
url: configService.get<string>('DB_URI', 'sqlite://data.sqlite'),
entityPrefix: configService.get<string>('DB_TABLE_PREFIX', ''),
logging: configService.get<string>('DB_LOGGING', 'false') === 'true',
entities,
migrations: [
`${__dirname}/**/migrations/**/*{.ts,.js}`,
],
migrationsRun: configService.get<boolean>('DB_MIGRATE', true),
}),
}),
MongooseModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService): Promise<MongooseModuleOptions> => ({
uri: configService.get<string>('MONGODB_URI', 'mongodb://localhost/ohmyform'),
// takes care of deprecations from https://mongoosejs.com/docs/deprecations.html
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: false,
})
}),
MongooseModule.forFeature(schema),
TypeOrmModule.forFeature(entities),
MailerModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],

View File

@ -1,7 +1,7 @@
import { Module } from '@nestjs/common';
import { imports } from './app.imports';
import { providers } from './app.providers';
import { controllers } from './controller';
import { Module } from '@nestjs/common'
import { imports } from './app.imports'
import { providers } from './app.providers'
import { controllers } from './controller'
@Module({
imports,

View File

@ -1,7 +1,7 @@
import { commands } from './command';
import { guards } from './guard';
import { resolvers } from './resolver';
import { services } from './service';
import { commands } from './command'
import { guards } from './guard'
import { resolvers } from './resolver'
import { services } from './service'
export const providers = [
...resolvers,

View File

@ -1,5 +1,5 @@
import { BootstrapConsole } from 'nestjs-console';
import { AppModule } from './app.module';
import { BootstrapConsole } from 'nestjs-console'
import { AppModule } from './app.module'
const bootstrap = new BootstrapConsole({
module: AppModule,

View File

@ -1,4 +1,4 @@
import { UserCommand } from './user.command';
import { UserCommand } from './user.command'
export const commands = [
UserCommand,

View File

@ -1,7 +1,7 @@
import inquirer from 'inquirer';
import { Command, Console } from 'nestjs-console';
import { matchType, validatePassword } from '../config/fields';
import { UserCreateService } from '../service/user/user.create.service';
import inquirer from 'inquirer'
import { Command, Console } from 'nestjs-console'
import { matchType, validatePassword } from '../config/fields'
import { UserCreateService } from '../service/user/user.create.service'
@Console({
name: 'user',

View File

@ -1,15 +1,23 @@
export const languages = [
'cn',
'de',
'en',
'es',
'ar',
'cn',
'da',
'nl',
'fr',
'de',
'hi',
'it',
'ja',
'pl',
'pt',
'pt_BR',
'pt_PT',
'ru',
'es',
'sv',
'ta',
'uk',
]
export const defaultLanguage = 'en'

View File

@ -1,4 +1,4 @@
import { Controller, Get } from '@nestjs/common';
import { Controller, Get } from '@nestjs/common'
@Controller()
export class HealthController {

View File

@ -1,4 +1,4 @@
import { HealthController } from './health.controller';
import { HealthController } from './health.controller'
export const controllers = [
HealthController,

View File

@ -1,4 +1,4 @@
import { SetMetadata } from '@nestjs/common';
import { rolesType } from '../config/roles';
import { SetMetadata } from '@nestjs/common'
import { rolesType } from '../config/roles'
export const Roles = (...roles: rolesType) => SetMetadata('roles', roles);

View File

@ -1,5 +1,5 @@
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
import { createParamDecorator, ExecutionContext } from '@nestjs/common'
import { GqlExecutionContext } from '@nestjs/graphql'
export const User = createParamDecorator(
(data: unknown, ctx: ExecutionContext) =>

View File

@ -1,4 +1,4 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { Field, ObjectType } from '@nestjs/graphql'
@ObjectType('AuthToken')
export class AuthJwtModel {

View File

@ -1,4 +1,4 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { Field, ObjectType } from '@nestjs/graphql'
@ObjectType('Deleted')
export class DeletedModel {

View File

@ -1,13 +0,0 @@
import { Field, InputType } from '@nestjs/graphql';
@InputType('NotificationInput', { isAbstract: true })
export class AbstractNotificationInput {
@Field({ nullable: true })
readonly subject?: string
@Field({ nullable: true })
readonly htmlTemplate?: string
@Field()
readonly enabled: boolean
}

View File

@ -1,20 +0,0 @@
import { Field, InterfaceType } from '@nestjs/graphql';
import { Notifications } from '../../schema/form.schema';
@InterfaceType('Notification')
export class AbstractNotificationModel {
@Field({ nullable: true })
readonly subject?: string
@Field({ nullable: true })
readonly htmlTemplate?: string
@Field()
readonly enabled: boolean
constructor(partial: Partial<Notifications>) {
this.subject = partial.subject
this.htmlTemplate = partial.htmlTemplate
this.enabled = partial.enabled
}
}

View File

@ -1,4 +1,4 @@
import { Field, InputType } from '@nestjs/graphql';
import { Field, InputType } from '@nestjs/graphql'
@InputType()
export class ButtonInput {

View File

@ -1,4 +1,5 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { Field, ObjectType } from '@nestjs/graphql'
import { PageButtonEntity } from '../../entity/page.button.entity'
@ObjectType('Button')
export class ButtonModel {
@ -20,7 +21,7 @@ export class ButtonModel {
@Field({ nullable: true })
readonly color?: string
constructor(button: Partial<ButtonModel>) {
constructor(button: Partial<PageButtonEntity>) {
this.url = button.url
this.action = button.action
this.text = button.text

View File

@ -1,22 +1,22 @@
import { Field, InputType } from '@nestjs/graphql';
import { Field, InputType } from '@nestjs/graphql'
@InputType()
export class ColorsInput {
@Field()
readonly backgroundColor: string
readonly background: string
@Field()
readonly questionColor: string
readonly question: string
@Field()
readonly answerColor: string
readonly answer: string
@Field()
readonly buttonColor: string
readonly button: string
@Field()
readonly buttonActiveColor: string
readonly buttonActive: string
@Field()
readonly buttonTextColor: string
readonly buttonText: string
}

View File

@ -1,32 +1,32 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { Colors } from '../../schema/form.schema';
import { Field, ObjectType } from '@nestjs/graphql'
import { ColorsEmbedded } from '../../entity/embedded/colors.embedded'
@ObjectType('Colors')
export class ColorsModel {
@Field()
readonly backgroundColor: string
readonly background: string
@Field()
readonly questionColor: string
readonly question: string
@Field()
readonly answerColor: string
readonly answer: string
@Field()
readonly buttonColor: string
readonly button: string
@Field()
readonly buttonActiveColor: string
readonly buttonActive: string
@Field()
readonly buttonTextColor: string
readonly buttonText: string
constructor(partial: Partial<Colors>) {
this.backgroundColor = partial.backgroundColor
this.questionColor = partial.questionColor
this.answerColor = partial.answerColor
this.buttonColor = partial.buttonColor
this.buttonActiveColor = partial.buttonActiveColor
this.buttonTextColor = partial.buttonTextColor
constructor(partial: Partial<ColorsEmbedded>) {
this.background = partial.background
this.question = partial.question
this.answer = partial.answer
this.button = partial.button
this.buttonActive = partial.buttonActive
this.buttonText = partial.buttonText
}
}

View File

@ -1,5 +1,5 @@
import { Field, InputType } from '@nestjs/graphql';
import { ColorsInput } from './colors.input';
import { Field, InputType } from '@nestjs/graphql'
import { ColorsInput } from './colors.input'
@InputType()
export class DesignInput {

View File

@ -1,6 +1,6 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { Design } from '../../schema/form.schema';
import { ColorsModel } from './colors.model';
import { Field, ObjectType } from '@nestjs/graphql'
import { DesignEmbedded } from '../../entity/embedded/design.embedded'
import { ColorsModel } from './colors.model'
@ObjectType('Design')
export class DesignModel {
@ -10,7 +10,7 @@ export class DesignModel {
@Field({ nullable: true })
readonly font?: string
constructor(partial: Partial<Design>) {
constructor(partial: Partial<DesignEmbedded>) {
this.colors = new ColorsModel(partial.colors)
this.font = partial.font
}

View File

@ -1,4 +1,4 @@
import { Field, InputType } from '@nestjs/graphql';
import { Field, InputType } from '@nestjs/graphql'
@InputType('FormCreateInput')
export class FormCreateInput {

View File

@ -1,8 +1,8 @@
import { Field, ID, InputType } from '@nestjs/graphql';
import { FormFieldOptionInput } from './form.field.option.input';
import { FormFieldRatingInput } from './form.field.rating.input';
import { LogicJumpInput } from './logic.jump.input';
import { LogicJumpModel } from './logic.jump.model';
import { Field, ID, InputType } from '@nestjs/graphql'
import { FormFieldLogicInput } from './form.field.logic.input'
import { FormFieldLogicModel } from './form.field.logic.model'
import { FormFieldOptionInput } from './form.field.option.input'
import { FormFieldRatingInput } from './form.field.rating.input'
@InputType()
export class FormFieldInput {
@ -28,10 +28,10 @@ export class FormFieldInput {
readonly value: string
@Field(() => [FormFieldOptionInput], { nullable: true })
readonly options: [FormFieldOptionInput]
readonly options: FormFieldOptionInput[]
@Field(() => LogicJumpInput, { nullable: true })
readonly logicJump: LogicJumpModel
@Field(() => [FormFieldLogicInput], { nullable: true })
readonly logic: FormFieldLogicInput[]
@Field(() => FormFieldRatingInput, { nullable: true })
readonly rating: FormFieldRatingInput

View File

@ -0,0 +1,25 @@
import { Field, ID, InputType } from '@nestjs/graphql'
@InputType()
export class FormFieldLogicInput {
@Field({ nullable: true })
readonly formula: string
@Field({ nullable: true })
readonly action: string
@Field(() => ID, { nullable: true })
readonly jumpTo?: string
@Field({ nullable: true })
readonly visible?: boolean
@Field({ nullable: true })
readonly disable?: boolean
@Field({ nullable: true })
readonly require?: boolean
@Field({ nullable: true })
readonly enabled: boolean
}

View File

@ -0,0 +1,42 @@
import { Field, ID, ObjectType } from '@nestjs/graphql'
import { FormFieldLogicEntity } from '../../entity/form.field.logic.entity'
@ObjectType('FormFieldLogic')
export class FormFieldLogicModel {
@Field(() => ID)
readonly id: string
@Field({ nullable: true })
readonly formula: string
@Field()
readonly action: string
@Field(() => ID, { nullable: true })
readonly jumpTo?: string
@Field({ nullable: true })
readonly visible?: boolean
@Field({ nullable: true })
readonly disable?: boolean
@Field({ nullable: true })
readonly require?: boolean
@Field()
readonly enabled: boolean
constructor(document: FormFieldLogicEntity) {
this.id = document.id.toString()
this.enabled = document.enabled
this.formula = document.formula
this.jumpTo = document.jumpTo?.id.toString()
this.action = document.action
this.visible = document.visible
this.disable = document.disable
this.require = document.require
}
}

View File

@ -1,8 +1,8 @@
import { Field, ID, ObjectType } from '@nestjs/graphql';
import { FormFieldDocument } from '../../schema/form.field.schema';
import { FormFieldOptionModel } from './form.field.option.model';
import { FormFieldRatingModel } from './form.field.rating.model';
import { LogicJumpModel } from './logic.jump.model';
import { Field, ID, ObjectType } from '@nestjs/graphql'
import { FormFieldEntity } from '../../entity/form.field.entity'
import { FormFieldLogicModel } from './form.field.logic.model'
import { FormFieldOptionModel } from './form.field.option.model'
import { FormFieldRatingModel } from './form.field.rating.model'
@ObjectType('FormField')
export class FormFieldModel {
@ -28,16 +28,16 @@ export class FormFieldModel {
readonly value: string
@Field(() => [FormFieldOptionModel])
readonly options: [FormFieldOptionModel]
readonly options: FormFieldOptionModel[]
@Field(() => LogicJumpModel)
readonly logicJump: LogicJumpModel
@Field(() => [FormFieldLogicModel])
readonly logic: FormFieldLogicModel[]
@Field(() => FormFieldRatingModel, { nullable: true })
readonly rating: FormFieldRatingModel
constructor(document: FormFieldDocument) {
this.id = document.id
constructor(document: FormFieldEntity) {
this.id = document.id.toString()
this.title = document.title
this.slug = document.slug
this.type = document.type
@ -45,7 +45,7 @@ export class FormFieldModel {
this.required = document.required
this.value = document.value
this.options = document.options ? document.options.map(option => new FormFieldOptionModel(option)) : []
this.logicJump = new LogicJumpModel(document.logicJump)
this.logic = document.logic ? document.logic.map(logic => new FormFieldLogicModel(logic)) : []
this.rating = document.rating ? new FormFieldRatingModel(document.rating) : null
}
}

View File

@ -1,4 +1,4 @@
import { Field, InputType } from '@nestjs/graphql';
import { Field, InputType } from '@nestjs/graphql'
@InputType()
export class FormFieldOptionInput {

View File

@ -1,5 +1,5 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { FieldOptionDocument } from '../../schema/embedded/field.option';
import { Field, ObjectType } from '@nestjs/graphql'
import { FormFieldOptionEntity } from '../../entity/form.field.option.entity'
@ObjectType('FormFieldOption')
export class FormFieldOptionModel {
@ -12,7 +12,7 @@ export class FormFieldOptionModel {
@Field()
readonly value: string
constructor(option: FieldOptionDocument) {
constructor(option: FormFieldOptionEntity) {
this.key = option.key
this.title = option.title
this.value = option.value

View File

@ -1,5 +1,5 @@
import { Field, InputType } from '@nestjs/graphql';
import { GraphQLInt } from 'graphql';
import { Field, InputType } from '@nestjs/graphql'
import { GraphQLInt } from 'graphql'
@InputType()
export class FormFieldRatingInput {

View File

@ -1,6 +1,6 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { GraphQLInt } from 'graphql';
import { RatingFieldDocument } from '../../schema/embedded/rating.field';
import { Field, ObjectType } from '@nestjs/graphql'
import { GraphQLInt } from 'graphql'
import { RatingEmbedded } from '../../entity/embedded/rating.embedded'
@ObjectType('FormFieldRating')
export class FormFieldRatingModel {
@ -10,7 +10,7 @@ export class FormFieldRatingModel {
@Field({ nullable: true })
readonly shape: string
constructor(option: RatingFieldDocument) {
constructor(option: RatingEmbedded) {
this.steps = option.steps
this.shape = option.shape
}

View File

@ -1,5 +1,4 @@
import { Field, ID, InputType } from '@nestjs/graphql'
import { GraphQLInt } from 'graphql';
@InputType()
export class FormHookInput {

View File

@ -1,5 +1,5 @@
import { Field, ID, ObjectType } from '@nestjs/graphql';
import { FormHookDocument } from '../../schema/form.hook.schema'
import { Field, ID, ObjectType } from '@nestjs/graphql'
import { FormHookEntity } from '../../entity/form.hook.entity'
@ObjectType('FormHook')
export class FormHookModel {
@ -15,8 +15,8 @@ export class FormHookModel {
@Field({ nullable: true })
readonly format?: string
constructor(hook: FormHookDocument) {
this.id = hook.id
constructor(hook: FormHookEntity) {
this.id = hook.id.toString()
this.enabled = hook.enabled
this.url = hook.url
this.format = hook.format

View File

@ -1,5 +1,5 @@
import { Field, ID, ObjectType } from '@nestjs/graphql';
import { FormDocument } from '../../schema/form.schema';
import { Field, ID, ObjectType } from '@nestjs/graphql'
import { FormEntity } from '../../entity/form.entity'
@ObjectType('Form')
export class FormModel {
@ -21,8 +21,8 @@ export class FormModel {
@Field()
readonly showFooter: boolean
constructor(form: FormDocument) {
this.id = form.id
constructor(form: FormEntity) {
this.id = form.id.toString()
this.title = form.title
this.created = form.created
this.lastModified = form.lastModified

View File

@ -0,0 +1,25 @@
import { Field, InputType } from '@nestjs/graphql'
@InputType('FormNotificationInput')
export class FormNotificationInput {
@Field({ nullable: true })
readonly subject?: string
@Field({ nullable: true })
readonly htmlTemplate?: string
@Field({ nullable: true })
readonly toField?: string
@Field({ nullable: true })
readonly fromEmail?: string
@Field({ nullable: true })
readonly fromField?: string
@Field({ nullable: true })
readonly toEmail?: string
@Field()
readonly enabled: boolean
}

View File

@ -0,0 +1,36 @@
import { Field, InterfaceType } from '@nestjs/graphql'
import { FormNotificationEntity } from '../../entity/form.notification.entity'
@InterfaceType('FormNotification')
export class FormNotificationModel {
@Field({ nullable: true })
readonly subject?: string
@Field({ nullable: true })
readonly htmlTemplate?: string
@Field({ nullable: true })
readonly toField?: string
@Field({ nullable: true })
readonly fromEmail?: string
@Field({ nullable: true })
readonly fromField?: string
@Field({ nullable: true })
readonly toEmail?: string
@Field()
readonly enabled: boolean
constructor(partial: Partial<FormNotificationEntity>) {
this.subject = partial.subject
this.htmlTemplate = partial.htmlTemplate
this.enabled = partial.enabled
this.toField = partial.toField?.id.toString()
this.toEmail = partial.toEmail
this.fromField = partial.fromField?.id.toString()
this.fromEmail = partial.fromEmail
}
}

View File

@ -1,9 +1,9 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { GraphQLInt } from 'graphql';
import { FormModel } from './form.model';
import { Field, ObjectType } from '@nestjs/graphql'
import { GraphQLInt } from 'graphql'
import { FormModel } from './form.model'
@ObjectType('PagerForm')
export class PagerFormModel {
@ObjectType('FormPager')
export class FormPagerModel {
@Field(() => [FormModel])
entries: FormModel[]

View File

@ -1,4 +1,4 @@
import { ObjectType } from '@nestjs/graphql';
import { ObjectType } from '@nestjs/graphql'
@ObjectType('FormStatistic')
export class FormStatisticModel {

View File

@ -1,10 +1,9 @@
import { Field, ID, InputType } from '@nestjs/graphql';
import { DesignInput } from './design.input';
import { FormFieldInput } from './form.field.input';
import { Field, ID, InputType } from '@nestjs/graphql'
import { DesignInput } from './design.input'
import { FormFieldInput } from './form.field.input'
import { FormHookInput } from './form.hook.input'
import { PageInput } from './page.input';
import { RespondentNotificationsInput } from './respondent.notifications.input';
import { SelfNotificationsInput } from './self.notifications.input';
import { FormNotificationInput } from './form.notification.input'
import { PageInput } from './page.input'
@InputType()
export class FormUpdateInput {
@ -38,9 +37,6 @@ export class FormUpdateInput {
@Field({ nullable: true })
readonly endPage: PageInput
@Field({ nullable: true })
readonly selfNotifications: SelfNotificationsInput
@Field({ nullable: true })
readonly respondentNotifications: RespondentNotificationsInput
@Field(() => [FormNotificationInput], { nullable: true })
readonly notifications: FormNotificationInput[]
}

View File

@ -1,19 +0,0 @@
import { Field, ID, InputType } from '@nestjs/graphql';
@InputType()
export class LogicJumpInput {
@Field(() => ID, { nullable: true })
readonly fieldA: string
@Field({ nullable: true })
readonly valueB: string
@Field({ nullable: true })
readonly expressionString: string
@Field(() => ID, { nullable: true })
readonly jumpTo: string
@Field({ nullable: true })
readonly enabled: boolean
}

View File

@ -1,33 +0,0 @@
import { Field, ID, ObjectType } from '@nestjs/graphql';
import { LogicJumpDocument } from '../../schema/embedded/logic.jump';
@ObjectType('LogicJump')
export class LogicJumpModel {
@Field(() => ID, { nullable: true })
readonly fieldA: string
@Field({ nullable: true })
readonly valueB: string
@Field({ nullable: true })
readonly expressionString: string
@Field(() => ID, { nullable: true })
readonly jumpTo: string
@Field()
readonly enabled: boolean
constructor(document: LogicJumpDocument) {
if (!document) {
this.enabled = false
return
}
this.fieldA = document.fieldA
this.valueB = document.valueB
this.expressionString = document.expressionString
this.jumpTo = document.jumpTo
this.enabled = !!document.enabled
}
}

View File

@ -1,5 +1,5 @@
import { Field, InputType } from '@nestjs/graphql';
import { ButtonInput } from './button.input';
import { Field, InputType } from '@nestjs/graphql'
import { ButtonInput } from './button.input'
@InputType()
export class PageInput {

View File

@ -1,6 +1,6 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { FormPage } from '../../schema/form.schema';
import { ButtonModel } from './button.model';
import { Field, ObjectType } from '@nestjs/graphql'
import { PageEntity } from '../../entity/page.entity'
import { ButtonModel } from './button.model'
@ObjectType('Page')
export class PageModel {
@ -19,7 +19,7 @@ export class PageModel {
@Field(() => [ButtonModel])
readonly buttons: ButtonModel[]
constructor(page: Partial<FormPage>) {
constructor(page: Partial<PageEntity>) {
this.show = page.show
this.title = page.title
this.paragraph = page.paragraph

View File

@ -1,11 +0,0 @@
import { Field, InputType } from '@nestjs/graphql';
import { AbstractNotificationInput } from './abstract.notification.input';
@InputType()
export class RespondentNotificationsInput extends AbstractNotificationInput {
@Field({ nullable: true })
readonly toField?: string
@Field({ nullable: true })
readonly fromEmail?: string
}

View File

@ -1,21 +0,0 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { RespondentNotifications } from '../../schema/form.schema';
import { AbstractNotificationModel } from './abstract.notification.model';
@ObjectType({
implements: [AbstractNotificationModel],
})
export class RespondentNotificationsModel extends AbstractNotificationModel {
@Field({ nullable: true })
readonly toField?: string
@Field({ nullable: true })
readonly fromEmail?: string
constructor(partial: Partial<RespondentNotifications>) {
super(partial);
this.toField = partial.toField
this.fromEmail = partial.fromEmail
}
}

View File

@ -1,11 +0,0 @@
import { Field, InputType } from '@nestjs/graphql';
import { AbstractNotificationInput } from './abstract.notification.input';
@InputType()
export class SelfNotificationsInput extends AbstractNotificationInput {
@Field({ nullable: true })
readonly fromField?: string
@Field({ nullable: true })
readonly toEmail?: string
}

View File

@ -1,21 +0,0 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { SelfNotifications } from '../../schema/form.schema';
import { AbstractNotificationModel } from './abstract.notification.model';
@ObjectType({
implements: [AbstractNotificationModel],
})
export class SelfNotificationsModel extends AbstractNotificationModel {
@Field({ nullable: true })
readonly fromField?: string
@Field({ nullable: true })
readonly toEmail?: string
constructor(partial: Partial<SelfNotifications>) {
super(partial);
this.fromField = partial.fromField
this.toEmail = partial.toEmail
}
}

View File

@ -1,13 +1,13 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { UserDocument } from '../../schema/user.schema';
import { UserModel } from '../user/user.model';
import { Field, ObjectType } from '@nestjs/graphql'
import { UserEntity } from '../../entity/user.entity'
import { UserModel } from '../user/user.model'
@ObjectType('Profile')
export class ProfileModel extends UserModel {
@Field(() => [String])
readonly roles: string[]
constructor(user: UserDocument) {
constructor(user: UserEntity) {
super(user)
this.roles = user.roles

View File

@ -1,4 +1,4 @@
import { Field, ID, InputType } from '@nestjs/graphql';
import { Field, ID, InputType } from '@nestjs/graphql'
@InputType()
export class ProfileUpdateInput {

View File

@ -1,4 +1,4 @@
import { Field, ID, ObjectType } from '@nestjs/graphql';
import { Field, ID, ObjectType } from '@nestjs/graphql'
@ObjectType('Setting')
export class SettingModel {

View File

@ -1,9 +1,9 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { GraphQLInt } from 'graphql';
import {SettingModel} from './setting.model'
import { Field, ObjectType } from '@nestjs/graphql'
import { GraphQLInt } from 'graphql'
import { SettingModel } from './setting.model'
@ObjectType('PagerSetting')
export class PagerSettingModel {
@ObjectType('SettingPager')
export class SettingPagerModel {
@Field(() => [SettingModel])
entries: SettingModel[]

View File

@ -1,4 +1,4 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { Field, ObjectType } from '@nestjs/graphql'
@ObjectType('Version')
export class StatusModel {

View File

@ -1,4 +1,4 @@
import { Field, InputType } from '@nestjs/graphql';
import { Field, InputType } from '@nestjs/graphql'
@InputType()
export class DeviceInput {
@ -7,4 +7,7 @@ export class DeviceInput {
@Field()
readonly name: string
@Field({ nullable: true })
readonly language: string
}

View File

@ -1,5 +1,5 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { Device } from '../../schema/submission.schema';
import { Field, ObjectType } from '@nestjs/graphql'
import { DeviceEmbedded } from '../../entity/embedded/device.embedded'
@ObjectType('Device')
export class DeviceModel {
@ -9,8 +9,12 @@ export class DeviceModel {
@Field()
readonly name: string
constructor(device: Device) {
@Field({ nullable: true })
readonly language: string
constructor(device: DeviceEmbedded) {
this.type = device.type
this.name = device.name
this.language = device.language
}
}

View File

@ -1,5 +1,5 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { GeoLocation } from '../../schema/submission.schema';
import { Field, ObjectType } from '@nestjs/graphql'
import { GeoLocationEmbedded } from '../../entity/embedded/geo.location.embedded'
@ObjectType('GeoLocation')
export class GeoLocationModel {
@ -9,7 +9,7 @@ export class GeoLocationModel {
@Field({ nullable: true })
city?: string
constructor(geo: GeoLocation) {
constructor(geo: GeoLocationEmbedded) {
this.country = geo.country
this.city = geo.city
}

View File

@ -1,5 +1,5 @@
import { Field, ID, ObjectType } from '@nestjs/graphql';
import { SubmissionFieldDocument } from '../../schema/submission.field.schema';
import { Field, ID, ObjectType } from '@nestjs/graphql'
import { SubmissionFieldEntity } from '../../entity/submission.field.entity'
@ObjectType('SubmissionField')
export class SubmissionFieldModel {
@ -12,8 +12,8 @@ export class SubmissionFieldModel {
@Field()
readonly type: string
constructor(field: SubmissionFieldDocument) {
this.id = field.id
constructor(field: SubmissionFieldEntity) {
this.id = field.id.toString()
this.value = JSON.stringify(field.fieldValue)
this.type = field.fieldType
}

View File

@ -1,12 +1,12 @@
import { Field, ID, ObjectType } from '@nestjs/graphql';
import { SubmissionDocument } from '../../schema/submission.schema';
import { DeviceModel } from './device.model';
import { GeoLocationModel } from './geo.location.model';
import { Field, ID, ObjectType } from '@nestjs/graphql'
import { SubmissionEntity } from '../../entity/submission.entity'
import { DeviceModel } from './device.model'
import { GeoLocationModel } from './geo.location.model'
@ObjectType('Submission')
export class SubmissionModel {
@Field(() => ID)
readonly id: string
readonly id: number
@Field()
readonly ipAddr: string
@ -29,7 +29,7 @@ export class SubmissionModel {
@Field({ nullable: true })
readonly lastModified?: Date
constructor(submission: SubmissionDocument) {
constructor(submission: SubmissionEntity) {
this.id = submission.id
this.ipAddr = submission.ipAddr

View File

@ -1,9 +1,9 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { GraphQLInt } from 'graphql';
import { SubmissionModel } from './submission.model';
import { Field, ObjectType } from '@nestjs/graphql'
import { GraphQLInt } from 'graphql'
import { SubmissionModel } from './submission.model'
@ObjectType('PagerSubmission')
export class PagerSubmissionModel {
@ObjectType('SubmissionPager')
export class SubmissionPagerModel {
@Field(() => [SubmissionModel])
entries: SubmissionModel[]

View File

@ -1,5 +1,5 @@
import { Field, ID, ObjectType } from '@nestjs/graphql';
import { SubmissionDocument } from '../../schema/submission.schema';
import { Field, ID, ObjectType } from '@nestjs/graphql'
import { SubmissionEntity } from '../../entity/submission.entity'
@ObjectType('SubmissionProgress')
export class SubmissionProgressModel {
@ -18,8 +18,8 @@ export class SubmissionProgressModel {
@Field({ nullable: true })
readonly lastModified?: Date
constructor(submission: Partial<SubmissionDocument>) {
this.id = submission.id
constructor(submission: Partial<SubmissionEntity>) {
this.id = submission.id.toString()
this.timeElapsed = submission.timeElapsed
this.percentageComplete = submission.percentageComplete

View File

@ -1,4 +1,4 @@
import { Field, ID, InputType } from '@nestjs/graphql';
import { Field, ID, InputType } from '@nestjs/graphql'
@InputType()
export class SubmissionSetFieldInput {

View File

@ -1,5 +1,5 @@
import { Field, InputType } from '@nestjs/graphql';
import { DeviceInput } from './device.input';
import { Field, InputType } from '@nestjs/graphql'
import { DeviceInput } from './device.input'
@InputType()
export class SubmissionStartInput {

View File

@ -1,4 +1,4 @@
import { ObjectType } from '@nestjs/graphql';
import { ObjectType } from '@nestjs/graphql'
@ObjectType('SubmissionStatistic')
export class SubmissionStatisticModel {

View File

@ -1,5 +1,5 @@
import { Field, InputType } from '@nestjs/graphql';
import { IsEmail, IsNotEmpty, MaxLength, MinLength } from 'class-validator';
import { Field, InputType } from '@nestjs/graphql'
import { IsEmail, IsNotEmpty, MaxLength, MinLength } from 'class-validator'
@InputType()
export class UserCreateInput {

View File

@ -1,5 +1,5 @@
import { Field, ID, ObjectType } from '@nestjs/graphql';
import { UserDocument } from '../../schema/user.schema';
import { Field, ID, ObjectType } from '@nestjs/graphql'
import { UserEntity } from '../../entity/user.entity'
@ObjectType('User')
export class UserModel {
@ -30,8 +30,8 @@ export class UserModel {
@Field({ nullable: true })
readonly lastModified: Date
constructor(user: UserDocument) {
this.id = user.id
constructor(user: UserEntity) {
this.id = user.id.toString()
this.username = user.username
this.email = user.email

View File

@ -1,9 +1,9 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { GraphQLInt } from 'graphql';
import { UserModel } from './user.model';
import { Field, ObjectType } from '@nestjs/graphql'
import { GraphQLInt } from 'graphql'
import { UserModel } from './user.model'
@ObjectType('PagerUser')
export class PagerUserModel {
@ObjectType('UserPager')
export class UserPagerModel {
@Field(() => [UserModel])
entries: UserModel[]

View File

@ -1,4 +1,4 @@
import { ObjectType } from '@nestjs/graphql';
import { ObjectType } from '@nestjs/graphql'
@ObjectType('UserStatistic')
export class UserStatisticModel {

View File

@ -1,5 +1,4 @@
import { Field, ID, InputType } from '@nestjs/graphql';
import { GraphQLString } from 'graphql';
import { Field, ID, InputType } from '@nestjs/graphql'
@InputType()
export class UserUpdateInput {
@ -21,7 +20,7 @@ export class UserUpdateInput {
@Field({ nullable: true })
readonly password: string
@Field(() => [GraphQLString], { nullable: true })
@Field(() => [String], { nullable: true })
readonly roles: string[]
@Field({ nullable: true })

View File

@ -1,3 +1,6 @@
import { Column } from 'typeorm'
export class AnalyticsEmbedded {
@Column({ nullable: true })
readonly gaCode?: string
}

View File

@ -1,8 +1,21 @@
import { Column } from 'typeorm'
export class ColorsEmbedded {
readonly background: string
readonly question: string
readonly answer: string
readonly button: string
readonly buttonActive: string
readonly buttonText: string
@Column({ nullable: true })
public background?: string
@Column({ nullable: true })
public question?: string
@Column({ nullable: true })
public answer?: string
@Column({ nullable: true })
public button?: string
@Column({ nullable: true })
public buttonActive?: string
@Column({ nullable: true })
public buttonText?: string
}

View File

@ -1,7 +1,10 @@
import { ColorsEmbedded } from './form.design.colors.embedded'
import { Column } from 'typeorm'
import { ColorsEmbedded } from './colors.embedded'
export class DesignEmbedded {
colors: ColorsEmbedded
@Column(() => ColorsEmbedded)
colors: ColorsEmbedded = new ColorsEmbedded()
@Column({ nullable: true })
font?: string
}

View File

@ -1,4 +1,12 @@
import { Column } from 'typeorm'
export class DeviceEmbedded {
readonly type?: string
readonly name?: string
@Column({ nullable: true })
public language?: string
@Column({ nullable: true })
public type?: string
@Column({ nullable: true })
public name?: string
}

View File

@ -1,4 +1,9 @@
import { Column } from 'typeorm'
export class GeoLocationEmbedded {
@Column({ nullable: true })
readonly country?: string
@Column({ nullable: true })
readonly city?: string
}

View File

@ -1,8 +0,0 @@
import { FormFieldEntity } from '../form.field.entity'
export class LogicEmbedded {
readonly formula?: string
readonly hide?: boolean
readonly jumpTo?: FormFieldEntity
readonly enabled?: boolean
}

View File

@ -1,4 +1,9 @@
import { Column } from 'typeorm'
export class RatingEmbedded {
@Column({ nullable: true })
readonly steps?: number
@Column({ nullable: true })
readonly shape?: string
}

View File

@ -4,17 +4,18 @@ import {
Entity,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn, UpdateDateColumn
PrimaryGeneratedColumn,
UpdateDateColumn
} from 'typeorm'
import { AnalyticsEmbedded } from './embedded/analytics.embedded'
import { DesignEmbedded } from './embedded/design.embedded'
import { FormFieldEntity } from './form.field.entity'
import { FormHookEntity } from './form.hook.entity'
import { FormNotificationEntity } from './form.notification.entity'
import { SubmissionEntity } from './submission.entity'
import { VisitorEntity } from './visitor.entity'
import { PageEntity } from './page.entity'
import { SubmissionEntity } from './submission.entity'
import { UserEntity } from './user.entity'
import { VisitorEntity } from './visitor.entity'
@Entity({ name: 'form' })
export class FormEntity {
@ -28,7 +29,7 @@ export class FormEntity {
public language: string
@Column(() => AnalyticsEmbedded)
public analytics: AnalyticsEmbedded
public analytics: AnalyticsEmbedded = new AnalyticsEmbedded()
@OneToMany(() => VisitorEntity, visitor => visitor.form)
public visitors: VisitorEntity[]
@ -36,22 +37,22 @@ export class FormEntity {
@OneToMany(() => SubmissionEntity, submission => submission.form)
public submissions: SubmissionEntity[]
@OneToMany(() => FormFieldEntity, field => field.form)
@OneToMany(() => FormFieldEntity, field => field.form, { eager: true, orphanedRowAction: 'delete' })
public fields: FormFieldEntity[]
@OneToMany(() => FormHookEntity, field => field.form)
@OneToMany(() => FormHookEntity, field => field.form, { eager: true, orphanedRowAction: 'delete' })
public hooks: FormHookEntity[]
@ManyToOne(() => UserEntity)
@ManyToOne(() => UserEntity, { eager: true })
public admin: UserEntity
@ManyToOne(() => PageEntity)
@ManyToOne(() => PageEntity, { eager: true })
public startPage: PageEntity;
@ManyToOne(() => PageEntity)
@ManyToOne(() => PageEntity, { eager: true })
public endPage: PageEntity;
@OneToMany(() => FormNotificationEntity, notification => notification.form)
@OneToMany(() => FormNotificationEntity, notification => notification.form, { eager: true, orphanedRowAction: 'delete' })
public notifications: FormNotificationEntity[]
@Column()
@ -61,7 +62,7 @@ export class FormEntity {
public isLive: boolean;
@Column(() => DesignEmbedded)
public design: DesignEmbedded;
public design: DesignEmbedded = new DesignEmbedded();
@CreateDateColumn()
public created: Date

View File

@ -1,5 +1,4 @@
import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from 'typeorm'
import { LogicEmbedded } from './embedded/logic.embedded'
import { RatingEmbedded } from './embedded/rating.embedded'
import { FormEntity } from './form.entity'
import { FormFieldLogicEntity } from './form.field.logic.entity'
@ -22,13 +21,13 @@ export class FormFieldEntity {
@Column({ nullable: true })
public slug?: string
@OneToMany(() => FormFieldLogicEntity, logic => logic.field)
@OneToMany(() => FormFieldLogicEntity, logic => logic.field, { eager: true })
public logic: FormFieldLogicEntity[]
@Column(() => RatingEmbedded)
public rating: RatingEmbedded = new RatingEmbedded()
@OneToMany(() => FormFieldOptionEntity, option => option.field)
@OneToMany(() => FormFieldOptionEntity, option => option.field, { eager: true })
public options?: FormFieldOptionEntity[]
@Column()

View File

@ -1,4 +1,4 @@
import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from 'typeorm'
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'
import { FormFieldEntity } from './form.field.entity'
type LogicAction = 'visible' | 'require' | 'disable' | 'jumpTo'

View File

@ -10,11 +10,11 @@ export class FormHookEntity {
public form: FormEntity
@Column()
readonly enabled: boolean
public enabled: boolean
@Column()
readonly url: string
public url: string
@Column({ nullable: true })
readonly format?: string
public format?: string
}

View File

@ -1,10 +1,10 @@
import { ButtonEntity } from './button.entity'
import { FormEntity } from './form.entity'
import { FormFieldEntity } from './form.field.entity'
import { FormFieldLogicEntity } from './form.field.logic.entity'
import { FormFieldOptionEntity } from './form.field.option.entity'
import { FormHookEntity } from './form.hook.entity'
import { FormNotificationEntity } from './form.notification.entity'
import { PageButtonEntity } from './page.button.entity'
import { PageEntity } from './page.entity'
import { SubmissionEntity } from './submission.entity'
import { SubmissionFieldEntity } from './submission.field.entity'
@ -12,13 +12,13 @@ import { UserEntity } from './user.entity'
import { VisitorEntity } from './visitor.entity'
export const entities = [
ButtonEntity,
FormEntity,
FormFieldEntity,
FormFieldLogicEntity,
FormFieldOptionEntity,
FormHookEntity,
FormNotificationEntity,
PageButtonEntity,
PageEntity,
SubmissionEntity,
SubmissionFieldEntity,

View File

@ -1,10 +1,14 @@
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'
import { PageEntity } from './page.entity'
@Entity({ name: 'button' })
export class ButtonEntity {
@Entity({ name: 'page_button' })
export class PageButtonEntity {
@PrimaryGeneratedColumn()
public id: number
@ManyToOne(() => PageEntity, page => page.buttons)
public page: PageEntity
@Column({ nullable: true })
readonly url?: string

View File

@ -1,5 +1,5 @@
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
import { ButtonEntity } from './button.entity'
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm'
import { PageButtonEntity } from './page.button.entity'
@Entity({ name: 'page' })
export class PageEntity {
@ -18,5 +18,6 @@ export class PageEntity {
@Column({ nullable: true })
readonly buttonText?: string
readonly buttons: ButtonEntity[]
@OneToMany(() => PageButtonEntity, button => button.page)
readonly buttons: PageButtonEntity[]
}

View File

@ -1,25 +1,57 @@
import { Entity, PrimaryGeneratedColumn } from 'typeorm'
import {
Column,
CreateDateColumn,
Entity,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
UpdateDateColumn
} from 'typeorm'
import { DeviceEmbedded } from './embedded/device.embedded'
import { GeoLocationEmbedded } from './embedded/geo.location.embedded'
import { FormEntity } from './form.entity'
import { SubmissionFieldEntity } from './submission.field.entity'
import { UserEntity } from './user.entity'
import { VisitorEntity } from './visitor.entity'
@Entity({ name: 'submission' })
export class SubmissionEntity {
@PrimaryGeneratedColumn()
public id: number
readonly fields: SubmissionFieldEntity[]
readonly form: FormEntity
readonly ipAddr: string
readonly tokenHash: string
readonly geoLocation: GeoLocationEmbedded
readonly device: DeviceEmbedded
readonly timeElapsed: number
readonly percentageComplete: number
@OneToMany(() => SubmissionFieldEntity, field => field.submission, { eager: true })
public fields: SubmissionFieldEntity[]
readonly user?: UserEntity
readonly created: Date
readonly lastModified: Date
@ManyToOne(() => FormEntity, form => form.submissions, { eager: true })
public form: FormEntity
@ManyToOne(() => VisitorEntity, visitor => visitor.submissions)
public visitor: VisitorEntity
@Column()
public ipAddr: string
@Column()
public tokenHash: string
@Column(() => GeoLocationEmbedded)
public geoLocation: GeoLocationEmbedded = new GeoLocationEmbedded()
@Column(() => DeviceEmbedded)
public device: DeviceEmbedded = new DeviceEmbedded()
@Column()
public timeElapsed: number
@Column()
public percentageComplete: number
@ManyToOne(() => UserEntity, { eager: true })
public user?: UserEntity
@CreateDateColumn()
public created: Date
@UpdateDateColumn()
public lastModified: Date
}

View File

@ -1,13 +1,21 @@
import { Entity, PrimaryGeneratedColumn } from 'typeorm'
import { FormFieldDocument } from '../schema/form.field.schema'
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'
import { FormFieldEntity } from './form.field.entity'
import { SubmissionEntity } from './submission.entity'
@Entity({ name: 'submission_field' })
export class SubmissionFieldEntity {
@PrimaryGeneratedColumn()
public id: number
readonly field: FormFieldEntity
readonly fieldType: string
readonly fieldValue: any
@ManyToOne(() => SubmissionEntity, submission => submission.fields)
public submission: SubmissionEntity
@ManyToOne(() => FormFieldEntity, { eager: true })
public field: FormFieldEntity
@Column()
public fieldType: string
@Column()
public fieldValue: string
}

View File

@ -1,4 +1,4 @@
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'
import { rolesType } from '../config/roles'
@Entity({ name: 'user' })
@ -6,7 +6,10 @@ export class UserEntity {
@PrimaryGeneratedColumn()
public id: number
@Column({ nullable: true })
public firstName?: string
@Column({ nullable: true })
public lastName?: string
@Column({ length: 255, unique: true })
@ -15,15 +18,36 @@ export class UserEntity {
@Column({ length: 255, unique: true })
public username: string
@Column()
public passwordHash: string
@Column({ nullable: true })
public salt: string
@Column()
public provider: string
@Column({ type: 'simple-array' })
public roles: rolesType
@Column()
public language: string
@Column({ nullable: true })
public resetPasswordToken?: string
@Column({ nullable: true })
public resetPasswordExpires?: Date
@Column({ nullable: true })
public token?: string
@Column({ nullable: true })
public apiKey?: string
@CreateDateColumn()
public created: Date
@UpdateDateColumn()
public lastModified: Date
}

View File

@ -1,7 +1,16 @@
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'
import { FormFieldDocument } from '../schema/form.field.schema'
import {
Column,
CreateDateColumn,
Entity,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
UpdateDateColumn
} from 'typeorm'
import { DeviceEmbedded } from './embedded/device.embedded'
import { GeoLocationEmbedded } from './embedded/geo.location.embedded'
import { FormEntity } from './form.entity'
import { FormFieldEntity } from './form.field.entity'
import { SubmissionEntity } from './submission.entity'
@Entity({ name: 'form_visitor' })
export class VisitorEntity {
@ -11,16 +20,24 @@ export class VisitorEntity {
@ManyToOne(() => FormEntity, form => form.visitors)
public form: FormEntity
@ManyToOne(() => FormEntity, form => form.visitors)
public submission: FormEntity
@OneToMany(() => SubmissionEntity, submission => submission.visitor)
public submissions: SubmissionEntity[]
@Column({ nullable: true })
readonly referrer?: string
readonly timeElapsed: number
readonly isSubmitted: boolean
readonly language: string
@Column()
readonly ipAddr: string
readonly deviceType: string
readonly userAgent: string
@Column(() => GeoLocationEmbedded)
public geoLocation: GeoLocationEmbedded = new GeoLocationEmbedded()
@Column(() => DeviceEmbedded)
public device: DeviceEmbedded = new DeviceEmbedded()
@CreateDateColumn()
public created: Date
@UpdateDateColumn()
public updated: Date
}

View File

@ -1,7 +1,7 @@
import { ExecutionContext, Injectable } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
import { AuthGuard } from '@nestjs/passport';
import { ContextCache } from '../resolver/context.cache';
import { ExecutionContext, Injectable } from '@nestjs/common'
import { GqlExecutionContext } from '@nestjs/graphql'
import { AuthGuard } from '@nestjs/passport'
import { ContextCache } from '../resolver/context.cache'
@Injectable()
export class GqlAuthGuard extends AuthGuard('jwt') {

View File

@ -1,7 +1,7 @@
import { APP_GUARD } from '@nestjs/core';
import { GqlAuthGuard } from './gql.auth.guard';
import { LocalAuthGuard } from './local.auth.guard';
import { RolesGuard } from './roles.guard';
import { APP_GUARD } from '@nestjs/core'
import { GqlAuthGuard } from './gql.auth.guard'
import { LocalAuthGuard } from './local.auth.guard'
import { RolesGuard } from './roles.guard'
export const guards = [
{

View File

@ -1,6 +1,6 @@
import { ExecutionContext, Injectable } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
import { AuthGuard } from '@nestjs/passport';
import { ExecutionContext, Injectable } from '@nestjs/common'
import { GqlExecutionContext } from '@nestjs/graphql'
import { AuthGuard } from '@nestjs/passport'
@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {

View File

@ -1,6 +1,6 @@
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { GqlExecutionContext } from '@nestjs/graphql';
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'
import { Reflector } from '@nestjs/core'
import { GqlExecutionContext } from '@nestjs/graphql'
@Injectable()
export class RolesGuard implements CanActivate {

View File

@ -1,9 +1,9 @@
import { NestApplicationOptions, ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import cors from 'cors';
import { Logger, PinoLogger } from 'nestjs-pino/dist';
import { LoggerConfig } from './app.imports';
import { AppModule } from './app.module';
import { NestApplicationOptions, ValidationPipe } from '@nestjs/common'
import { NestFactory } from '@nestjs/core'
import cors from 'cors'
import { Logger, PinoLogger } from 'nestjs-pino/dist'
import { LoggerConfig } from './app.imports'
import { AppModule } from './app.module'
(async () => {
const options: NestApplicationOptions = {

View File

@ -1,24 +1,35 @@
import { MigrationInterface, QueryRunner, Table } from "typeorm"
import { MigrationInterface, QueryRunner } from "typeorm"
export class initial1619723437787 implements MigrationInterface {
name = 'initial1619723437787'
public async up(queryRunner: QueryRunner): Promise<void> {
if (queryRunner.connection.driver.options.type !== 'sqlite') {
await queryRunner.query(`CREATE TABLE "button" ("id" SERIAL NOT NULL, "url" VARCHAR, "action" VARCHAR, "text" VARCHAR NOT NULL, "bgColor" VARCHAR, "activeColor" VARCHAR, "color" VARCHAR, CONSTRAINT "PK_a4df4e4f7a5882bc94442d3f209" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "form_field_logic" ("id" SERIAL NOT NULL, "formula" VARCHAR NOT NULL, "action" VARCHAR(10) NOT NULL, "visible" boolean, "require" boolean, "disable" boolean, "enabled" boolean NOT NULL, "fieldId" integer, "jumpToId" integer, CONSTRAINT "PK_c40e7f583854ff1b60900d8cf1b" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "form_field_option" ("id" SERIAL NOT NULL, "key" VARCHAR, "title" VARCHAR, "value" VARCHAR NOT NULL, "fieldId" integer, CONSTRAINT "PK_812955356e516819e37b64bf39b" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "form_field" ("id" SERIAL NOT NULL, "title" VARCHAR NOT NULL, "description" text NOT NULL, "slug" VARCHAR, "required" boolean NOT NULL, "disabled" boolean NOT NULL, "type" VARCHAR NOT NULL, "value" VARCHAR NOT NULL, "formId" integer, CONSTRAINT "PK_135904ddb60085b07254ea4f485" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "form_hook" ("id" SERIAL NOT NULL, "enabled" boolean NOT NULL, "url" VARCHAR NOT NULL, "format" VARCHAR, "formId" integer, CONSTRAINT "PK_4b63bd9ff09f7b3e5c4a41fcbec" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "form_notification" ("id" SERIAL NOT NULL, "subject" VARCHAR, "htmlTemplate" VARCHAR, "enabled" boolean NOT NULL, "toEmail" VARCHAR, "fromEmail" VARCHAR, "formId" integer, "fromFieldId" integer, "toFieldId" integer, CONSTRAINT "PK_935306529aed07c9f6628f6e24f" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "submission" ("id" SERIAL NOT NULL, CONSTRAINT "PK_7faa571d0e4a7076e85890c9bd0" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "form_visitor" ("id" SERIAL NOT NULL, "referrer" VARCHAR, "formId" integer, "submissionId" integer, CONSTRAINT "PK_74224dc63e13cf5cb5f0420e65b" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "page" ("id" SERIAL NOT NULL, "show" boolean NOT NULL, "title" VARCHAR, "paragraph" text, "buttonText" VARCHAR, CONSTRAINT "PK_742f4117e065c5b6ad21b37ba1f" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "user" ("id" SERIAL NOT NULL, "email" VARCHAR(255) NOT NULL, "username" VARCHAR(255) NOT NULL, CONSTRAINT "UQ_e12875dfb3b1d92d7d7c5377e22" UNIQUE ("email"), CONSTRAINT "UQ_78a916df40e02a9deb1c4b75edb" UNIQUE ("username"), CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "form" ("id" SERIAL NOT NULL, "title" VARCHAR NOT NULL, "language" VARCHAR(10) NOT NULL, "showFooter" boolean NOT NULL, "isLive" boolean NOT NULL, "created" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "lastModified" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "adminId" integer, "startPageId" integer, "endPageId" integer, CONSTRAINT "PK_8f72b95aa2f8ba82cf95dc7579e" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "submission_field" ("id" SERIAL NOT NULL, CONSTRAINT "PK_5443f5f769fce3107982c16e0b5" PRIMARY KEY ("id"))`);
if (queryRunner.connection.driver.options.type === 'sqlite') {
await queryRunner.query(`CREATE TABLE "user" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "firstName" varchar, "lastName" varchar, "email" varchar(255) NOT NULL, "username" varchar(255) NOT NULL, "passwordHash" varchar NOT NULL, "salt" varchar, "provider" varchar NOT NULL, "roles" text NOT NULL, "language" varchar NOT NULL, "resetPasswordToken" varchar, "resetPasswordExpires" datetime, "token" varchar, "apiKey" varchar, "created" datetime NOT NULL DEFAULT (datetime('now')), "lastModified" datetime NOT NULL DEFAULT (datetime('now')), CONSTRAINT "UQ_78a916df40e02a9deb1c4b75edb" UNIQUE ("username"), CONSTRAINT "UQ_e12875dfb3b1d92d7d7c5377e22" UNIQUE ("email"))`);
await queryRunner.query(`CREATE TABLE "page" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "show" boolean NOT NULL, "title" varchar, "paragraph" text, "buttonText" varchar)`);
await queryRunner.query(`CREATE TABLE "form_field_logic" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "formula" varchar NOT NULL, "action" varchar(10) NOT NULL, "visible" boolean, "require" boolean, "disable" boolean, "enabled" boolean NOT NULL, "fieldId" integer, "jumpToId" integer, CONSTRAINT "FK_6098b83f6759445d8cfdd03d545" FOREIGN KEY ("fieldId") REFERENCES "form_field" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_4a8019f2b753cfb3216dc3001a6" FOREIGN KEY ("jumpToId") REFERENCES "form_field" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`CREATE TABLE "form_field_option" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "key" varchar, "title" varchar, "value" varchar NOT NULL, "fieldId" integer, CONSTRAINT "FK_c4484ad12c2c56db31dffdbfe97" FOREIGN KEY ("fieldId") REFERENCES "form_field" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`CREATE TABLE "form_field" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar NOT NULL, "description" text NOT NULL, "slug" varchar, "required" boolean NOT NULL, "disabled" boolean NOT NULL, "type" varchar NOT NULL, "value" varchar NOT NULL, "formId" integer, "ratingSteps" integer, "ratingShape" varchar, CONSTRAINT "FK_2d83d8a334dd66445db13f92b77" FOREIGN KEY ("formId") REFERENCES "form" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`CREATE TABLE "form_hook" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "enabled" boolean NOT NULL, "url" varchar NOT NULL, "format" varchar, "formId" integer, CONSTRAINT "FK_bbeb4d224d8857fd5a458538a30" FOREIGN KEY ("formId") REFERENCES "form" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`CREATE TABLE "form_notification" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "subject" varchar, "htmlTemplate" varchar, "enabled" boolean NOT NULL, "toEmail" varchar, "fromEmail" varchar, "formId" integer, "fromFieldId" integer, "toFieldId" integer, CONSTRAINT "FK_a9ed55144108ded893b502d6321" FOREIGN KEY ("formId") REFERENCES "form" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_0876741ce2acdaee4553d7a3bbd" FOREIGN KEY ("fromFieldId") REFERENCES "form_field" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_4915ebae53e09b732322d0ff6ed" FOREIGN KEY ("toFieldId") REFERENCES "form_field" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`CREATE TABLE "submission_field" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "fieldType" varchar NOT NULL, "fieldValue" varchar NOT NULL, "submissionId" integer, "fieldId" integer, CONSTRAINT "FK_16fae661ce5b10f27abe2e524a0" FOREIGN KEY ("submissionId") REFERENCES "submission" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_5befa92da2370b7eb1cab6ae30a" FOREIGN KEY ("fieldId") REFERENCES "form_field" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`CREATE TABLE "form_visitor" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "referrer" varchar, "ipAddr" varchar NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "updated" datetime NOT NULL DEFAULT (datetime('now')), "formId" integer, "geoLocationCountry" varchar, "geoLocationCity" varchar, "deviceLanguage" varchar, "deviceType" varchar, "deviceName" varchar, CONSTRAINT "FK_72ade6c3a3e55d1fce94300f8b6" FOREIGN KEY ("formId") REFERENCES "form" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`CREATE TABLE "submission" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "ipAddr" varchar NOT NULL, "tokenHash" varchar NOT NULL, "timeElapsed" integer NOT NULL, "percentageComplete" integer NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "lastModified" datetime NOT NULL DEFAULT (datetime('now')), "formId" integer, "visitorId" integer, "userId" integer, "geoLocationCountry" varchar, "geoLocationCity" varchar, "deviceLanguage" varchar, "deviceType" varchar, "deviceName" varchar, CONSTRAINT "FK_7bd626272858ef6464aa2579094" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_95b73c7faf2c199f005fda5e8c8" FOREIGN KEY ("visitorId") REFERENCES "form_visitor" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_6090e1d5cbf3433ffd14e3b53e7" FOREIGN KEY ("formId") REFERENCES "form" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`CREATE TABLE "page_button" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "url" varchar, "action" varchar, "text" varchar NOT NULL, "bgColor" varchar, "activeColor" varchar, "color" varchar, "pageId" integer, CONSTRAINT "FK_d9f099286b75fa0034dcd8cf7c2" FOREIGN KEY ("pageId") REFERENCES "page" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`CREATE TABLE "form" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar NOT NULL, "language" varchar(10) NOT NULL, "showFooter" boolean NOT NULL, "isLive" boolean NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "lastModified" datetime NOT NULL DEFAULT (datetime('now')), "adminId" integer, "startPageId" integer, "endPageId" integer, "analyticsGacode" varchar, "designFont" varchar, "designColorsBackground" varchar, "designColorsQuestion" varchar, "designColorsAnswer" varchar, "designColorsButton" varchar, "designColorsButtonactive" varchar, "designColorsButtontext" varchar, CONSTRAINT "FK_a7cb33580bca2b362e5e34fdfcd" FOREIGN KEY ("adminId") REFERENCES "user" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_023d9cf1d97e93facc96c86ca70" FOREIGN KEY ("startPageId") REFERENCES "page" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_e5d158932e43cfbf9958931ee01" FOREIGN KEY ("endPageId") REFERENCES "page" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
} else {
await queryRunner.query(`CREATE TABLE "form_field_logic" ("id" SERIAL NOT NULL, "formula" character varying NOT NULL, "action" character varying(10) NOT NULL, "visible" boolean, "require" boolean, "disable" boolean, "enabled" boolean NOT NULL, "fieldId" integer, "jumpToId" integer, CONSTRAINT "PK_c40e7f583854ff1b60900d8cf1b" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "form_field_option" ("id" SERIAL NOT NULL, "key" character varying, "title" character varying, "value" character varying NOT NULL, "fieldId" integer, CONSTRAINT "PK_812955356e516819e37b64bf39b" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "form_field" ("id" SERIAL NOT NULL, "title" character varying NOT NULL, "description" text NOT NULL, "slug" character varying, "required" boolean NOT NULL, "disabled" boolean NOT NULL, "type" character varying NOT NULL, "value" character varying NOT NULL, "formId" integer, "ratingSteps" integer, "ratingShape" character varying, CONSTRAINT "PK_135904ddb60085b07254ea4f485" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "form_hook" ("id" SERIAL NOT NULL, "enabled" boolean NOT NULL, "url" character varying NOT NULL, "format" character varying, "formId" integer, CONSTRAINT "PK_4b63bd9ff09f7b3e5c4a41fcbec" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "form_notification" ("id" SERIAL NOT NULL, "subject" character varying, "htmlTemplate" character varying, "enabled" boolean NOT NULL, "toEmail" character varying, "fromEmail" character varying, "formId" integer, "fromFieldId" integer, "toFieldId" integer, CONSTRAINT "PK_935306529aed07c9f6628f6e24f" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "submission_field" ("id" SERIAL NOT NULL, "fieldType" character varying NOT NULL, "fieldValue" character varying NOT NULL, "submissionId" integer, "fieldId" integer, CONSTRAINT "PK_5443f5f769fce3107982c16e0b5" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "user" ("id" SERIAL NOT NULL, "firstName" character varying, "lastName" character varying, "email" character varying(255) NOT NULL, "username" character varying(255) NOT NULL, "passwordHash" character varying NOT NULL, "salt" character varying, "provider" character varying NOT NULL, "roles" character varying(10) NOT NULL, "language" character varying NOT NULL, "resetPasswordToken" character varying, "resetPasswordExpires" TIMESTAMP, "token" character varying, "apiKey" character varying, "created" TIMESTAMP NOT NULL DEFAULT now(), "lastModified" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "UQ_e12875dfb3b1d92d7d7c5377e22" UNIQUE ("email"), CONSTRAINT "UQ_78a916df40e02a9deb1c4b75edb" UNIQUE ("username"), CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "form_visitor" ("id" SERIAL NOT NULL, "referrer" character varying, "ipAddr" character varying NOT NULL, "created" TIMESTAMP NOT NULL DEFAULT now(), "updated" TIMESTAMP NOT NULL DEFAULT now(), "formId" integer, "geoLocationCountry" character varying, "geoLocationCity" character varying, CONSTRAINT "PK_74224dc63e13cf5cb5f0420e65b" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "submission" ("id" SERIAL NOT NULL, "ipAddr" character varying NOT NULL, "tokenHash" character varying NOT NULL, "timeElapsed" integer NOT NULL, "percentageComplete" integer NOT NULL, "created" TIMESTAMP NOT NULL DEFAULT now(), "lastModified" TIMESTAMP NOT NULL DEFAULT now(), "formId" integer, "visitorId" integer, "userId" integer, "geoLocationCountry" character varying, "geoLocationCity" character varying, CONSTRAINT "PK_7faa571d0e4a7076e85890c9bd0" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "page_button" ("id" SERIAL NOT NULL, "url" character varying, "action" character varying, "text" character varying NOT NULL, "bgColor" character varying, "activeColor" character varying, "color" character varying, "pageId" integer, CONSTRAINT "PK_6609a75a7d82775aac8af1a591c" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "page" ("id" SERIAL NOT NULL, "show" boolean NOT NULL, "title" character varying, "paragraph" text, "buttonText" character varying, CONSTRAINT "PK_742f4117e065c5b6ad21b37ba1f" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "form" ("id" SERIAL NOT NULL, "title" character varying NOT NULL, "language" character varying(10) NOT NULL, "showFooter" boolean NOT NULL, "isLive" boolean NOT NULL, "created" TIMESTAMP NOT NULL DEFAULT now(), "lastModified" TIMESTAMP NOT NULL DEFAULT now(), "adminId" integer, "startPageId" integer, "endPageId" integer, "analyticsGacode" character varying, "designFont" character varying, "designColorsBackground" character varying, "designColorsQuestion" character varying, "designColorsAnswer" character varying, "designColorsButton" character varying, "designColorsButtonactive" character varying, "designColorsButtontext" character varying, CONSTRAINT "PK_8f72b95aa2f8ba82cf95dc7579e" PRIMARY KEY ("id"))`);
await queryRunner.query(`ALTER TABLE "form_field_logic" ADD CONSTRAINT "FK_6098b83f6759445d8cfdd03d545" FOREIGN KEY ("fieldId") REFERENCES "form_field"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "form_field_logic" ADD CONSTRAINT "FK_4a8019f2b753cfb3216dc3001a6" FOREIGN KEY ("jumpToId") REFERENCES "form_field"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "form_field_option" ADD CONSTRAINT "FK_c4484ad12c2c56db31dffdbfe97" FOREIGN KEY ("fieldId") REFERENCES "form_field"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
@ -27,24 +38,27 @@ export class initial1619723437787 implements MigrationInterface {
await queryRunner.query(`ALTER TABLE "form_notification" ADD CONSTRAINT "FK_a9ed55144108ded893b502d6321" FOREIGN KEY ("formId") REFERENCES "form"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "form_notification" ADD CONSTRAINT "FK_0876741ce2acdaee4553d7a3bbd" FOREIGN KEY ("fromFieldId") REFERENCES "form_field"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "form_notification" ADD CONSTRAINT "FK_4915ebae53e09b732322d0ff6ed" FOREIGN KEY ("toFieldId") REFERENCES "form_field"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "submission_field" ADD CONSTRAINT "FK_16fae661ce5b10f27abe2e524a0" FOREIGN KEY ("submissionId") REFERENCES "submission"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "submission_field" ADD CONSTRAINT "FK_5befa92da2370b7eb1cab6ae30a" FOREIGN KEY ("fieldId") REFERENCES "form_field"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "form_visitor" ADD CONSTRAINT "FK_72ade6c3a3e55d1fce94300f8b6" FOREIGN KEY ("formId") REFERENCES "form"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "form_visitor" ADD CONSTRAINT "FK_cc7e7beb67c46f65d57289b121d" FOREIGN KEY ("submissionId") REFERENCES "form"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "submission" ADD CONSTRAINT "FK_6090e1d5cbf3433ffd14e3b53e7" FOREIGN KEY ("formId") REFERENCES "form"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "submission" ADD CONSTRAINT "FK_95b73c7faf2c199f005fda5e8c8" FOREIGN KEY ("visitorId") REFERENCES "form_visitor"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "submission" ADD CONSTRAINT "FK_7bd626272858ef6464aa2579094" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "page_button" ADD CONSTRAINT "FK_d9f099286b75fa0034dcd8cf7c2" FOREIGN KEY ("pageId") REFERENCES "page"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "form" ADD CONSTRAINT "FK_a7cb33580bca2b362e5e34fdfcd" FOREIGN KEY ("adminId") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "form" ADD CONSTRAINT "FK_023d9cf1d97e93facc96c86ca70" FOREIGN KEY ("startPageId") REFERENCES "page"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "form" ADD CONSTRAINT "FK_e5d158932e43cfbf9958931ee01" FOREIGN KEY ("endPageId") REFERENCES "page"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
} else {
await queryRunner.query(`CREATE TABLE "button" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "url" varchar, "action" varchar, "text" varchar NOT NULL, "bgColor" varchar, "activeColor" varchar, "color" varchar)`);
await queryRunner.query(`CREATE TABLE "submission" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL)`);
await queryRunner.query(`CREATE TABLE "page" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "show" boolean NOT NULL, "title" varchar, "paragraph" text, "buttonText" varchar)`);
await queryRunner.query(`CREATE TABLE "user" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "email" varchar(255) NOT NULL, "username" varchar(255) NOT NULL, CONSTRAINT "UQ_78a916df40e02a9deb1c4b75edb" UNIQUE ("username"), CONSTRAINT "UQ_e12875dfb3b1d92d7d7c5377e22" UNIQUE ("email"))`);
await queryRunner.query(`CREATE TABLE "form" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar NOT NULL, "language" varchar(10) NOT NULL, "showFooter" boolean NOT NULL, "isLive" boolean NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "lastModified" datetime NOT NULL DEFAULT (datetime('now')), "adminId" integer, "startPageId" integer, "endPageId" integer, CONSTRAINT "FK_a7cb33580bca2b362e5e34fdfcd" FOREIGN KEY ("adminId") REFERENCES "user" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_023d9cf1d97e93facc96c86ca70" FOREIGN KEY ("startPageId") REFERENCES "page" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_e5d158932e43cfbf9958931ee01" FOREIGN KEY ("endPageId") REFERENCES "page" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`CREATE TABLE "submission_field" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL)`);
await queryRunner.query(`CREATE TABLE "form_field_logic" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "formula" varchar NOT NULL, "action" varchar(10) NOT NULL, "visible" boolean, "require" boolean, "disable" boolean, "enabled" boolean NOT NULL, "fieldId" integer, "jumpToId" integer, CONSTRAINT "FK_6098b83f6759445d8cfdd03d545" FOREIGN KEY ("fieldId") REFERENCES "form_field" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_4a8019f2b753cfb3216dc3001a6" FOREIGN KEY ("jumpToId") REFERENCES "form_field" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`CREATE TABLE "form_field_option" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "key" varchar, "title" varchar, "value" varchar NOT NULL, "fieldId" integer, CONSTRAINT "FK_c4484ad12c2c56db31dffdbfe97" FOREIGN KEY ("fieldId") REFERENCES "form_field" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`CREATE TABLE "form_field" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar NOT NULL, "description" text NOT NULL, "slug" varchar, "required" boolean NOT NULL, "disabled" boolean NOT NULL, "type" varchar NOT NULL, "value" varchar NOT NULL, "formId" integer, CONSTRAINT "FK_2d83d8a334dd66445db13f92b77" FOREIGN KEY ("formId") REFERENCES "form" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`CREATE TABLE "form_hook" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "enabled" boolean NOT NULL, "url" varchar NOT NULL, "format" varchar, "formId" integer, CONSTRAINT "FK_bbeb4d224d8857fd5a458538a30" FOREIGN KEY ("formId") REFERENCES "form" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`CREATE TABLE "form_notification" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "subject" varchar, "htmlTemplate" varchar, "enabled" boolean NOT NULL, "toEmail" varchar, "fromEmail" varchar, "formId" integer, "fromFieldId" integer, "toFieldId" integer, CONSTRAINT "FK_a9ed55144108ded893b502d6321" FOREIGN KEY ("formId") REFERENCES "form" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_0876741ce2acdaee4553d7a3bbd" FOREIGN KEY ("fromFieldId") REFERENCES "form_field" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_4915ebae53e09b732322d0ff6ed" FOREIGN KEY ("toFieldId") REFERENCES "form_field" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`CREATE TABLE "form_visitor" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "referrer" varchar, "formId" integer, "submissionId" integer, CONSTRAINT "FK_72ade6c3a3e55d1fce94300f8b6" FOREIGN KEY ("formId") REFERENCES "form" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_cc7e7beb67c46f65d57289b121d" FOREIGN KEY ("submissionId") REFERENCES "form" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`ALTER TABLE "form_visitor" ADD "deviceLanguage" character varying`);
await queryRunner.query(`ALTER TABLE "form_visitor" ADD "deviceType" character varying`);
await queryRunner.query(`ALTER TABLE "form_visitor" ADD "deviceName" character varying`);
await queryRunner.query(`ALTER TABLE "submission" ADD "deviceLanguage" character varying`);
await queryRunner.query(`ALTER TABLE "submission" ADD "deviceType" character varying`);
await queryRunner.query(`ALTER TABLE "submission" ADD "deviceName" character varying`);
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "roles"`);
await queryRunner.query(`ALTER TABLE "user" ADD "roles" text NOT NULL`);
}
}
@ -53,8 +67,13 @@ export class initial1619723437787 implements MigrationInterface {
await queryRunner.query(`ALTER TABLE "form" DROP CONSTRAINT "FK_e5d158932e43cfbf9958931ee01"`);
await queryRunner.query(`ALTER TABLE "form" DROP CONSTRAINT "FK_023d9cf1d97e93facc96c86ca70"`);
await queryRunner.query(`ALTER TABLE "form" DROP CONSTRAINT "FK_a7cb33580bca2b362e5e34fdfcd"`);
await queryRunner.query(`ALTER TABLE "form_visitor" DROP CONSTRAINT "FK_cc7e7beb67c46f65d57289b121d"`);
await queryRunner.query(`ALTER TABLE "page_button" DROP CONSTRAINT "FK_d9f099286b75fa0034dcd8cf7c2"`);
await queryRunner.query(`ALTER TABLE "submission" DROP CONSTRAINT "FK_7bd626272858ef6464aa2579094"`);
await queryRunner.query(`ALTER TABLE "submission" DROP CONSTRAINT "FK_95b73c7faf2c199f005fda5e8c8"`);
await queryRunner.query(`ALTER TABLE "submission" DROP CONSTRAINT "FK_6090e1d5cbf3433ffd14e3b53e7"`);
await queryRunner.query(`ALTER TABLE "form_visitor" DROP CONSTRAINT "FK_72ade6c3a3e55d1fce94300f8b6"`);
await queryRunner.query(`ALTER TABLE "submission_field" DROP CONSTRAINT "FK_5befa92da2370b7eb1cab6ae30a"`);
await queryRunner.query(`ALTER TABLE "submission_field" DROP CONSTRAINT "FK_16fae661ce5b10f27abe2e524a0"`);
await queryRunner.query(`ALTER TABLE "form_notification" DROP CONSTRAINT "FK_4915ebae53e09b732322d0ff6ed"`);
await queryRunner.query(`ALTER TABLE "form_notification" DROP CONSTRAINT "FK_0876741ce2acdaee4553d7a3bbd"`);
await queryRunner.query(`ALTER TABLE "form_notification" DROP CONSTRAINT "FK_a9ed55144108ded893b502d6321"`);
@ -65,18 +84,17 @@ export class initial1619723437787 implements MigrationInterface {
await queryRunner.query(`ALTER TABLE "form_field_logic" DROP CONSTRAINT "FK_6098b83f6759445d8cfdd03d545"`);
}
await queryRunner.query(`DROP TABLE "submission_field"`);
await queryRunner.query(`DROP TABLE "form"`);
await queryRunner.query(`DROP TABLE "user"`);
await queryRunner.query(`DROP TABLE "page"`);
await queryRunner.query(`DROP TABLE "form_visitor"`);
await queryRunner.query(`DROP TABLE "page_button"`);
await queryRunner.query(`DROP TABLE "submission"`);
await queryRunner.query(`DROP TABLE "form_visitor"`);
await queryRunner.query(`DROP TABLE "user"`);
await queryRunner.query(`DROP TABLE "submission_field"`);
await queryRunner.query(`DROP TABLE "form_notification"`);
await queryRunner.query(`DROP TABLE "form_hook"`);
await queryRunner.query(`DROP TABLE "form_field"`);
await queryRunner.query(`DROP TABLE "form_field_option"`);
await queryRunner.query(`DROP TABLE "form_field_logic"`);
await queryRunner.query(`DROP TABLE "button"`);
}
}

View File

@ -1,7 +1,7 @@
import { Injectable } from '@nestjs/common';
import { Args, Mutation } from '@nestjs/graphql';
import { AuthJwtModel } from '../../dto/auth/auth.jwt.model';
import { AuthService } from '../../service/auth/auth.service';
import { Injectable } from '@nestjs/common'
import { Args, Mutation } from '@nestjs/graphql'
import { AuthJwtModel } from '../../dto/auth/auth.jwt.model'
import { AuthService } from '../../service/auth/auth.service'
@Injectable()
export class AuthLoginResolver {
@ -12,8 +12,8 @@ export class AuthLoginResolver {
@Mutation(() => AuthJwtModel)
async authLogin(
@Args({ name: 'username', type: () => String }) username,
@Args({ name: 'password', type: () => String }) password,
@Args({ name: 'username', type: () => String }) username: string,
@Args({ name: 'password', type: () => String }) password: string,
): Promise<AuthJwtModel> {
const user = await this.auth.validateUser(username, password)

View File

@ -1,11 +1,11 @@
import { Injectable } from '@nestjs/common';
import { Args, Mutation } from '@nestjs/graphql';
import { PinoLogger } from 'nestjs-pino/dist';
import { AuthJwtModel } from '../../dto/auth/auth.jwt.model';
import { UserCreateInput } from '../../dto/user/user.create.input';
import { AuthService } from '../../service/auth/auth.service';
import {SettingService} from '../../service/setting.service'
import { UserCreateService } from '../../service/user/user.create.service';
import { Injectable } from '@nestjs/common'
import { Args, Mutation } from '@nestjs/graphql'
import { PinoLogger } from 'nestjs-pino/dist'
import { AuthJwtModel } from '../../dto/auth/auth.jwt.model'
import { UserCreateInput } from '../../dto/user/user.create.input'
import { AuthService } from '../../service/auth/auth.service'
import { SettingService } from '../../service/setting.service'
import { UserCreateService } from '../../service/user/user.create.service'
@Injectable()
export class AuthRegisterResolver {

View File

@ -1,5 +1,5 @@
import { AuthLoginResolver } from './auth.login.resolver';
import { AuthRegisterResolver } from './auth.register.resolver';
import { AuthLoginResolver } from './auth.login.resolver'
import { AuthRegisterResolver } from './auth.register.resolver'
export const authServices = [
AuthRegisterResolver,

View File

@ -1,67 +1,29 @@
import { FormFieldDocument } from '../schema/form.field.schema';
import { FormDocument } from '../schema/form.schema';
import { SubmissionFieldDocument } from '../schema/submission.field.schema';
import { SubmissionDocument } from '../schema/submission.schema';
import { UserDocument } from '../schema/user.schema';
export class ContextCache {
private users: {
[id: string]: UserDocument,
type ID = string | number
export class ContextCache<A = any> {
private cache: {
[key: string]: any
} = {}
private forms: {
[id: string]: FormDocument,
} = {}
private submissions: {
[id: string]: SubmissionDocument,
} = {}
private submissionFields: {
[id: string]: SubmissionFieldDocument,
} = {}
private formFields: {
[id: string]: FormFieldDocument,
} = {}
public addUser(user: UserDocument) {
this.users[user.id] = user;
public getCacheKey(type: string, id: ID): string {
return `${type}:${id}`
}
public async getUser(id: any): Promise<UserDocument> {
return this.users[id]
public add<B = A>(key: string, element: B): void {
this.cache[key] = element
}
public addForm(form: FormDocument) {
this.forms[form.id] = form
}
public get<B = A>(key: string, init?: () => Promise<B>): B | Promise<B> {
if (!this.cache[key] && init) {
const result = init()
void result.then(r => {
this.cache[key] = r
})
public async getForm(id: any): Promise<FormDocument> {
return this.forms[id]
}
return result
}
public addSubmission(submission: SubmissionDocument) {
this.submissions[submission.id] = submission
}
public async getSubmission(id: any): Promise<SubmissionDocument> {
return this.submissions[id]
}
public addFormField(field: FormFieldDocument) {
this.formFields[field.id] = field
}
public async getFormField(id: any): Promise<FormFieldDocument> {
return this.formFields[id]
}
public addSubmissionField(field: SubmissionFieldDocument) {
this.submissionFields[field.id] = field
}
public async getSubmissionField(id: any): Promise<SubmissionFieldDocument> {
return this.submissionFields[id]
return this.cache[key]
}
}

View File

@ -1,12 +1,13 @@
import { Injectable } from '@nestjs/common';
import { Args, Context, Mutation } from '@nestjs/graphql';
import { Roles } from '../../decorator/roles.decorator';
import { User } from '../../decorator/user.decorator';
import { FormCreateInput } from '../../dto/form/form.create.input';
import { FormModel } from '../../dto/form/form.model';
import { UserDocument } from '../../schema/user.schema';
import { FormCreateService } from '../../service/form/form.create.service';
import { ContextCache } from '../context.cache';
import { Injectable } from '@nestjs/common'
import { Args, Context, Mutation } from '@nestjs/graphql'
import { Roles } from '../../decorator/roles.decorator'
import { User } from '../../decorator/user.decorator'
import { FormCreateInput } from '../../dto/form/form.create.input'
import { FormModel } from '../../dto/form/form.model'
import { FormEntity } from '../../entity/form.entity'
import { UserEntity } from '../../entity/user.entity'
import { FormCreateService } from '../../service/form/form.create.service'
import { ContextCache } from '../context.cache'
@Injectable()
export class FormCreateMutation {
@ -18,13 +19,13 @@ export class FormCreateMutation {
@Mutation(() => FormModel)
@Roles('admin')
async createForm(
@User() user: UserDocument,
@User() user: UserEntity,
@Args({ name: 'form', type: () => FormCreateInput }) input: FormCreateInput,
@Context('cache') cache: ContextCache,
): Promise<FormModel> {
const form = await this.createService.create(user, input)
cache.addForm(form)
cache.add(cache.getCacheKey(FormEntity.name, form.id), form)
return new FormModel(form)
}

View File

@ -1,11 +1,11 @@
import { Injectable } from '@nestjs/common';
import { Args, ID, Mutation } from '@nestjs/graphql';
import { Roles } from '../../decorator/roles.decorator';
import { User } from '../../decorator/user.decorator';
import { DeletedModel } from '../../dto/deleted.model';
import { UserDocument } from '../../schema/user.schema';
import { FormDeleteService } from '../../service/form/form.delete.service';
import { FormService } from '../../service/form/form.service';
import { Injectable } from '@nestjs/common'
import { Args, ID, Mutation } from '@nestjs/graphql'
import { Roles } from '../../decorator/roles.decorator'
import { User } from '../../decorator/user.decorator'
import { DeletedModel } from '../../dto/deleted.model'
import { UserEntity } from '../../entity/user.entity'
import { FormDeleteService } from '../../service/form/form.delete.service'
import { FormService } from '../../service/form/form.service'
@Injectable()
export class FormDeleteMutation {
@ -18,9 +18,9 @@ export class FormDeleteMutation {
@Mutation(() => DeletedModel)
@Roles('admin')
async deleteForm(
@User() user: UserDocument,
@User() user: UserEntity,
@Args({ name: 'id', type: () => ID}) id: string,
) {
): Promise<DeletedModel> {
const form = await this.formService.findById(id)
if (!form.isLive && !await this.formService.isAdmin(form, user)) {

View File

@ -0,0 +1,39 @@
import { Args, Context, ID, Parent, Query, ResolveField, Resolver } from '@nestjs/graphql'
import { Roles } from '../../decorator/roles.decorator'
import { User } from '../../decorator/user.decorator'
import { DesignModel } from '../../dto/form/design.model'
import { FormFieldModel } from '../../dto/form/form.field.model'
import { FormHookModel } from '../../dto/form/form.hook.model'
import { FormModel } from '../../dto/form/form.model'
import { FormNotificationModel } from '../../dto/form/form.notification.model'
import { PageModel } from '../../dto/form/page.model'
import { UserModel } from '../../dto/user/user.model'
import { FormEntity } from '../../entity/form.entity'
import { UserEntity } from '../../entity/user.entity'
import { FormService } from '../../service/form/form.service'
import { ContextCache } from '../context.cache'
@Resolver(() => FormModel)
export class FormResolver {
constructor(
private readonly formService: FormService,
) {
}
@Query(() => FormModel)
async getFormById(
@User() user: UserEntity,
@Args('id', {type: () => ID}) id,
@Context('cache') cache: ContextCache,
): Promise<FormModel> {
const form = await this.formService.findById(id)
if (!form.isLive && !await this.formService.isAdmin(form, user)) {
throw new Error('invalid form')
}
cache.add(cache.getCacheKey(FormEntity.name, form.id), form)
return new FormModel(form)
}
}

Some files were not shown because too many files have changed in this diff Show More