add new notification logic
This commit is contained in:
parent
ddc1a9b378
commit
468ec6c637
@ -4,7 +4,7 @@
|
||||
"username": "root",
|
||||
"password": "root",
|
||||
"database": "ohmyform",
|
||||
"synchronize": true,
|
||||
"synchronize": false,
|
||||
"logging": false,
|
||||
"entities": [
|
||||
"src/entity/**/*.ts"
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "sqlite",
|
||||
"database": "data.sqlite",
|
||||
"synchronize": true,
|
||||
"synchronize": false,
|
||||
"logging": false,
|
||||
"entities": [
|
||||
"src/entity/**/*.ts"
|
||||
|
||||
@ -63,6 +63,7 @@
|
||||
"pg": "^8.6.0",
|
||||
"pino-pretty": "^4.7.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"request-ip": "^2.1.3",
|
||||
"rimraf": "^3.0.2",
|
||||
"rxjs": "^6.6.7",
|
||||
"sqlite3": "^5.0.2",
|
||||
@ -81,6 +82,7 @@
|
||||
"@types/node": "^15.0.1",
|
||||
"@types/passport-jwt": "^3.0.5",
|
||||
"@types/passport-local": "^1.0.33",
|
||||
"@types/request-ip": "^0.0.35",
|
||||
"@types/supertest": "^2.0.11",
|
||||
"@typescript-eslint/eslint-plugin": "^4.22.0",
|
||||
"@typescript-eslint/parser": "^4.22.0",
|
||||
|
||||
@ -107,6 +107,7 @@ export const imports = [
|
||||
inject: [ConfigService],
|
||||
useFactory: (configService: ConfigService): TypeOrmModuleOptions => ({
|
||||
name: 'ohmyform',
|
||||
synchronize: false,
|
||||
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', ''),
|
||||
|
||||
19
src/decorator/ip.address.decorator.ts
Normal file
19
src/decorator/ip.address.decorator.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { createParamDecorator, ExecutionContext } from '@nestjs/common'
|
||||
import { GqlExecutionContext } from '@nestjs/graphql'
|
||||
import { getClientIp } from 'request-ip'
|
||||
|
||||
export const IpAddress = createParamDecorator((data: string, ctx: ExecutionContext) => {
|
||||
let req
|
||||
|
||||
if (ctx.getType<string>() === 'graphql') {
|
||||
req = GqlExecutionContext.create(ctx).getContext().req
|
||||
} else {
|
||||
req = ctx.switchToHttp().getRequest()
|
||||
}
|
||||
|
||||
if (req.clientIp) {
|
||||
return req.clientIp
|
||||
}
|
||||
|
||||
return getClientIp(req)
|
||||
})
|
||||
@ -14,7 +14,7 @@ export class SubmissionFieldModel {
|
||||
|
||||
constructor(field: SubmissionFieldEntity) {
|
||||
this.id = field.id.toString()
|
||||
this.value = JSON.stringify(field.fieldValue)
|
||||
this.value = field.fieldValue
|
||||
this.type = field.fieldType
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Field, ID, ObjectType } from '@nestjs/graphql'
|
||||
import { Field, Float, ID, ObjectType } from '@nestjs/graphql'
|
||||
import { SubmissionEntity } from '../../entity/submission.entity'
|
||||
import { DeviceModel } from './device.model'
|
||||
import { GeoLocationModel } from './geo.location.model'
|
||||
|
||||
@ -4,7 +4,7 @@ import {
|
||||
Entity,
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
PrimaryGeneratedColumn,
|
||||
PrimaryGeneratedColumn, RelationId,
|
||||
UpdateDateColumn
|
||||
} from 'typeorm'
|
||||
import { DeviceEmbedded } from './embedded/device.embedded'
|
||||
@ -25,7 +25,10 @@ export class SubmissionEntity {
|
||||
@ManyToOne(() => FormEntity, form => form.submissions, { eager: true })
|
||||
public form: FormEntity
|
||||
|
||||
@ManyToOne(() => VisitorEntity, visitor => visitor.submissions)
|
||||
@RelationId('form')
|
||||
readonly formId: number
|
||||
|
||||
@ManyToOne(() => VisitorEntity, visitor => visitor.submissions, { eager: true })
|
||||
public visitor: VisitorEntity
|
||||
|
||||
@Column()
|
||||
@ -40,10 +43,10 @@ export class SubmissionEntity {
|
||||
@Column(() => DeviceEmbedded)
|
||||
public device: DeviceEmbedded = new DeviceEmbedded()
|
||||
|
||||
@Column()
|
||||
@Column({ type: 'numeric' })
|
||||
public timeElapsed: number
|
||||
|
||||
@Column()
|
||||
@Column({ type: 'numeric' })
|
||||
public percentageComplete: number
|
||||
|
||||
@ManyToOne(() => UserEntity, { eager: true })
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'
|
||||
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn, RelationId } from 'typeorm'
|
||||
import { FormEntity } from './form.entity'
|
||||
import { FormFieldEntity } from './form.field.entity'
|
||||
import { SubmissionEntity } from './submission.entity'
|
||||
|
||||
@ -13,6 +14,9 @@ export class SubmissionFieldEntity {
|
||||
@ManyToOne(() => FormFieldEntity, { eager: true })
|
||||
public field: FormFieldEntity
|
||||
|
||||
@RelationId('field')
|
||||
readonly fieldId: number
|
||||
|
||||
@Column()
|
||||
public fieldType: string
|
||||
|
||||
|
||||
@ -5,17 +5,17 @@ export class initial1619723437787 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
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 "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_e12875dfb3b1d92d7d7c5377e22" UNIQUE ("email"), CONSTRAINT "UQ_78a916df40e02a9deb1c4b75edb" UNIQUE ("username"))`);
|
||||
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 "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 "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 "submission" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "ipAddr" varchar NOT NULL, "tokenHash" varchar NOT NULL, "timeElapsed" numeric NOT NULL, "percentageComplete" numeric 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_6090e1d5cbf3433ffd14e3b53e7" FOREIGN KEY ("formId") REFERENCES "form" ("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_7bd626272858ef6464aa2579094" FOREIGN KEY ("userId") REFERENCES "user" ("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"))`);
|
||||
@ -23,12 +23,12 @@ export class initial1619723437787 implements MigrationInterface {
|
||||
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 "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" text 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, "deviceLanguage" character varying, "deviceType" character varying, "deviceName" 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" numeric NOT NULL, "percentageComplete" numeric 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, "deviceLanguage" character varying, "deviceType" character varying, "deviceName" character varying, CONSTRAINT "PK_7faa571d0e4a7076e85890c9bd0" 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`);
|
||||
@ -38,27 +38,16 @@ 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 "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 "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 "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`);
|
||||
|
||||
|
||||
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`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,13 +56,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 "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 "page_button" DROP CONSTRAINT "FK_d9f099286b75fa0034dcd8cf7c2"`);
|
||||
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"`);
|
||||
@ -85,12 +74,12 @@ export class initial1619723437787 implements MigrationInterface {
|
||||
}
|
||||
|
||||
await queryRunner.query(`DROP TABLE "form"`);
|
||||
await queryRunner.query(`DROP TABLE "page"`);
|
||||
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 "page"`);
|
||||
await queryRunner.query(`DROP TABLE "page_button"`);
|
||||
await queryRunner.query(`DROP TABLE "form_notification"`);
|
||||
await queryRunner.query(`DROP TABLE "form_hook"`);
|
||||
await queryRunner.query(`DROP TABLE "form_field"`);
|
||||
|
||||
@ -26,9 +26,9 @@ export class FormResolver {
|
||||
@Parent() parent: FormModel,
|
||||
@Context('cache') cache: ContextCache,
|
||||
): Promise<FormFieldModel[]> {
|
||||
const form = await cache.get(cache.getCacheKey(FormEntity.name, parent.id))
|
||||
const form = await cache.get<FormEntity>(cache.getCacheKey(FormEntity.name, parent.id))
|
||||
|
||||
return form.fields.map(field => new FormFieldModel(field))
|
||||
return form.fields?.map(field => new FormFieldModel(field)) || []
|
||||
}
|
||||
|
||||
@ResolveField('hooks', () => [FormHookModel])
|
||||
@ -37,9 +37,9 @@ export class FormResolver {
|
||||
@Parent() parent: FormModel,
|
||||
@Context('cache') cache: ContextCache,
|
||||
): Promise<FormHookModel[]> {
|
||||
const form = await cache.get(cache.getCacheKey(FormEntity.name, parent.id))
|
||||
const form = await cache.get<FormEntity>(cache.getCacheKey(FormEntity.name, parent.id))
|
||||
|
||||
return form.hooks.map(hook => new FormHookModel(hook))
|
||||
return form.hooks?.map(hook => new FormHookModel(hook)) || []
|
||||
}
|
||||
|
||||
@ResolveField('isLive', () => Boolean)
|
||||
@ -49,7 +49,7 @@ export class FormResolver {
|
||||
@Parent() parent: FormModel,
|
||||
@Context('cache') cache: ContextCache,
|
||||
): Promise<boolean> {
|
||||
const form = await cache.get(cache.getCacheKey(FormEntity.name, parent.id))
|
||||
const form = await cache.get<FormEntity>(cache.getCacheKey(FormEntity.name, parent.id))
|
||||
|
||||
if (!await this.formService.isAdmin(form, user)) {
|
||||
throw new Error('no access to field')
|
||||
@ -71,7 +71,7 @@ export class FormResolver {
|
||||
throw new Error('no access to field')
|
||||
}
|
||||
|
||||
return form.notifications.map(notification => new FormNotificationModel(notification))
|
||||
return form.notifications?.map(notification => new FormNotificationModel(notification)) || []
|
||||
}
|
||||
|
||||
@ResolveField('design', () => DesignModel)
|
||||
@ -80,7 +80,7 @@ export class FormResolver {
|
||||
@Parent() parent: FormModel,
|
||||
@Context('cache') cache: ContextCache,
|
||||
): Promise<DesignModel> {
|
||||
const form = await cache.get(cache.getCacheKey(FormEntity.name, parent.id))
|
||||
const form = await cache.get<FormEntity>(cache.getCacheKey(FormEntity.name, parent.id))
|
||||
|
||||
return new DesignModel(form.design)
|
||||
}
|
||||
@ -90,7 +90,7 @@ export class FormResolver {
|
||||
@Parent() parent: FormModel,
|
||||
@Context('cache') cache: ContextCache,
|
||||
): Promise<PageModel> {
|
||||
const form = await cache.get(cache.getCacheKey(FormEntity.name, parent.id))
|
||||
const form = await cache.get<FormEntity>(cache.getCacheKey(FormEntity.name, parent.id))
|
||||
|
||||
return new PageModel(form.startPage)
|
||||
}
|
||||
@ -100,7 +100,7 @@ export class FormResolver {
|
||||
@Parent() parent: FormModel,
|
||||
@Context('cache') cache: ContextCache,
|
||||
): Promise<PageModel> {
|
||||
const form = await cache.get(cache.getCacheKey(FormEntity.name, parent.id))
|
||||
const form = await cache.get<FormEntity>(cache.getCacheKey(FormEntity.name, parent.id))
|
||||
|
||||
return new PageModel(form.endPage)
|
||||
}
|
||||
@ -111,7 +111,7 @@ export class FormResolver {
|
||||
@Parent() parent: FormModel,
|
||||
@Context('cache') cache: ContextCache,
|
||||
): Promise<UserModel> {
|
||||
const form = await cache.get(cache.getCacheKey(FormEntity.name, parent.id))
|
||||
const form = await cache.get<FormEntity>(cache.getCacheKey(FormEntity.name, parent.id))
|
||||
|
||||
if (!form.admin) {
|
||||
return null
|
||||
|
||||
@ -1,11 +1,18 @@
|
||||
import { Context, Parent, ResolveField, Resolver } from '@nestjs/graphql'
|
||||
import { FormFieldModel } from '../../dto/form/form.field.model'
|
||||
import { SubmissionFieldModel } from '../../dto/submission/submission.field.model'
|
||||
import { FormFieldEntity } from '../../entity/form.field.entity'
|
||||
import { SubmissionFieldEntity } from '../../entity/submission.field.entity'
|
||||
import { FormFieldService } from '../../service/form/form.field.service'
|
||||
import { ContextCache } from '../context.cache'
|
||||
|
||||
@Resolver(() => SubmissionFieldModel)
|
||||
export class SubmissionFieldResolver {
|
||||
constructor(
|
||||
private readonly formFieldService: FormFieldService,
|
||||
) {
|
||||
}
|
||||
|
||||
@ResolveField('field', () => FormFieldModel, { nullable: true })
|
||||
async getFields(
|
||||
@Parent() parent: SubmissionFieldModel,
|
||||
@ -13,10 +20,12 @@ export class SubmissionFieldResolver {
|
||||
): Promise<FormFieldModel> {
|
||||
const submissionField = await cache.get<SubmissionFieldEntity>(cache.getCacheKey(SubmissionFieldEntity.name, parent.id))
|
||||
|
||||
if (!submissionField.field) {
|
||||
const field = await cache.get<FormFieldEntity>(cache.getCacheKey(FormFieldEntity.name, submissionField.fieldId), () => this.formFieldService.findById(submissionField.fieldId, submissionField.field))
|
||||
|
||||
if (!field) {
|
||||
return null
|
||||
}
|
||||
|
||||
return new FormFieldModel(submissionField.field)
|
||||
return new FormFieldModel(field)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,8 +2,6 @@ import { Context, Parent, ResolveField, Resolver } from '@nestjs/graphql'
|
||||
import { User } from '../../decorator/user.decorator'
|
||||
import { SubmissionFieldModel } from '../../dto/submission/submission.field.model'
|
||||
import { SubmissionModel } from '../../dto/submission/submission.model'
|
||||
import { FormEntity } from '../../entity/form.entity'
|
||||
import { FormFieldEntity } from '../../entity/form.field.entity'
|
||||
import { SubmissionEntity } from '../../entity/submission.entity'
|
||||
import { SubmissionFieldEntity } from '../../entity/submission.field.entity'
|
||||
import { UserEntity } from '../../entity/user.entity'
|
||||
@ -19,12 +17,6 @@ export class SubmissionResolver {
|
||||
): Promise<SubmissionFieldModel[]> {
|
||||
const submission = await cache.get<SubmissionEntity>(cache.getCacheKey(SubmissionEntity.name, parent.id))
|
||||
|
||||
cache.add(cache.getCacheKey(FormEntity.name, submission.form.id), submission.form)
|
||||
|
||||
submission.form.fields.forEach(field => {
|
||||
cache.add(cache.getCacheKey(FormFieldEntity.name, field.id), field)
|
||||
})
|
||||
|
||||
return submission.fields.map(field => {
|
||||
cache.add(cache.getCacheKey(SubmissionFieldEntity.name, field.id), field)
|
||||
return new SubmissionFieldModel(field)
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { Args, Context, ID, Mutation } from '@nestjs/graphql'
|
||||
import { IpAddress } from '../../decorator/ip.address.decorator'
|
||||
import { User } from '../../decorator/user.decorator'
|
||||
import { SubmissionProgressModel } from '../../dto/submission/submission.progress.model'
|
||||
import { SubmissionStartInput } from '../../dto/submission/submission.start.input'
|
||||
@ -22,11 +23,12 @@ export class SubmissionStartMutation {
|
||||
@User() user: UserEntity,
|
||||
@Args({ name: 'form', type: () => ID }) id: string,
|
||||
@Args({ name: 'submission', type: () => SubmissionStartInput }) input: SubmissionStartInput,
|
||||
@IpAddress() ipAddr: string,
|
||||
@Context('cache') cache: ContextCache,
|
||||
): Promise<SubmissionProgressModel> {
|
||||
const form = await this.formService.findById(id)
|
||||
|
||||
const submission = await this.startService.start(form, input, user)
|
||||
const submission = await this.startService.start(form, input, user, ipAddr)
|
||||
|
||||
cache.add(cache.getCacheKey(SubmissionEntity.name, submission.id), submission)
|
||||
|
||||
|
||||
27
src/service/form/form.field.service.ts
Normal file
27
src/service/form/form.field.service.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { InjectRepository } from '@nestjs/typeorm'
|
||||
import { Repository } from 'typeorm'
|
||||
import { FormEntity } from '../../entity/form.entity'
|
||||
import { FormFieldEntity } from '../../entity/form.field.entity'
|
||||
import { UserEntity } from '../../entity/user.entity'
|
||||
|
||||
@Injectable()
|
||||
export class FormFieldService {
|
||||
constructor(
|
||||
@InjectRepository(FormFieldEntity)
|
||||
private readonly formFieldRepository: Repository<FormFieldEntity>,
|
||||
) {
|
||||
}
|
||||
|
||||
async findById(id: number | string, existing?: FormFieldEntity): Promise<FormFieldEntity> {
|
||||
if (existing) return existing
|
||||
|
||||
const entity = await this.formFieldRepository.findOne(id);
|
||||
|
||||
if (!entity) {
|
||||
throw new Error('no form field found')
|
||||
}
|
||||
|
||||
return entity
|
||||
}
|
||||
}
|
||||
@ -41,7 +41,9 @@ export class FormService {
|
||||
return await qb.getManyAndCount()
|
||||
}
|
||||
|
||||
async findById(id: string): Promise<FormEntity> {
|
||||
async findById(id: number | string, existing?: FormEntity): Promise<FormEntity> {
|
||||
if (existing) return existing
|
||||
|
||||
const form = await this.formRepository.findOne(id);
|
||||
|
||||
if (!form) {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { FormCreateService } from './form.create.service'
|
||||
import { FormDeleteService } from './form.delete.service'
|
||||
import { FormFieldService } from './form.field.service'
|
||||
import { FormService } from './form.service'
|
||||
import { FormStatisticService } from './form.statistic.service'
|
||||
import { FormUpdateService } from './form.update.service'
|
||||
@ -7,6 +8,7 @@ import { FormUpdateService } from './form.update.service'
|
||||
export const formServices = [
|
||||
FormCreateService,
|
||||
FormDeleteService,
|
||||
FormFieldService,
|
||||
FormService,
|
||||
FormStatisticService,
|
||||
FormUpdateService,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { SubmissionHookService } from './submission.hook.service'
|
||||
import { SubmissionNotificationService } from './submission.notification.service'
|
||||
import { SubmissionService } from './submission.service'
|
||||
import { SubmissionSetFieldService } from './submission.set.field.service'
|
||||
import { SubmissionStartService } from './submission.start.service'
|
||||
@ -6,10 +7,11 @@ import { SubmissionStatisticService } from './submission.statistic.service'
|
||||
import { SubmissionTokenService } from './submission.token.service'
|
||||
|
||||
export const submissionServices = [
|
||||
SubmissionHookService,
|
||||
SubmissionNotificationService,
|
||||
SubmissionService,
|
||||
SubmissionSetFieldService,
|
||||
SubmissionStartService,
|
||||
SubmissionStatisticService,
|
||||
SubmissionTokenService,
|
||||
SubmissionHookService,
|
||||
]
|
||||
|
||||
@ -23,7 +23,7 @@ export class SubmissionHookService {
|
||||
await this.format(submission, hook.format)
|
||||
).toPromise()
|
||||
|
||||
console.log('sent', response.data)
|
||||
console.log('sent hook', response.data)
|
||||
} catch (e) {
|
||||
this.logger.error(`failed to post to "${hook.url}: ${e.message}`)
|
||||
this.logger.error(e.stack)
|
||||
|
||||
70
src/service/submission/submission.notification.service.ts
Normal file
70
src/service/submission/submission.notification.service.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import { MailerService } from '@nestjs-modules/mailer'
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import handlebars from 'handlebars'
|
||||
import htmlToText from 'html-to-text'
|
||||
import { PinoLogger } from 'nestjs-pino/dist'
|
||||
import { SubmissionEntity } from '../../entity/submission.entity'
|
||||
import mjml2html from 'mjml'
|
||||
|
||||
@Injectable()
|
||||
export class SubmissionNotificationService {
|
||||
constructor(
|
||||
private readonly nestMailer: MailerService,
|
||||
private readonly logger: PinoLogger,
|
||||
) {
|
||||
}
|
||||
|
||||
public async process(submission: SubmissionEntity): Promise<void> {
|
||||
await Promise.all(submission.form.notifications.map(async (notification) => {
|
||||
if (!notification.enabled) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const to = this.getEmail(submission.fields.find(field => field.fieldId === notification.toField.id )?.fieldValue, notification.toEmail)
|
||||
const from = this.getEmail(submission.fields.find(field => field.fieldId === notification.fromField.id )?.fieldValue, notification.fromEmail)
|
||||
|
||||
const html = mjml2html(
|
||||
handlebars.compile(
|
||||
notification.htmlTemplate
|
||||
)({
|
||||
// TODO add varialbes
|
||||
}),
|
||||
{
|
||||
minify: true
|
||||
}
|
||||
).html
|
||||
|
||||
await this.nestMailer.sendMail({
|
||||
to,
|
||||
replyTo: from,
|
||||
subject: notification.subject,
|
||||
html,
|
||||
text: htmlToText.fromString(html)
|
||||
})
|
||||
console.log('sent notification to', to)
|
||||
} catch (e) {
|
||||
this.logger.error(e.stack)
|
||||
throw e
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
private getEmail(raw: string, fallback: string): string {
|
||||
if (!raw) {
|
||||
return fallback
|
||||
}
|
||||
|
||||
try {
|
||||
const data = JSON.parse(raw)
|
||||
|
||||
if (data.value) {
|
||||
return data.value
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.error('could not decode field value', raw)
|
||||
}
|
||||
|
||||
return fallback
|
||||
}
|
||||
}
|
||||
@ -19,6 +19,8 @@ export class SubmissionService {
|
||||
async find(form: FormEntity, start: number, limit: number, sort: any = {}): Promise<[SubmissionEntity[], number]> {
|
||||
const qb = this.submissionRepository.createQueryBuilder('s')
|
||||
|
||||
qb.leftJoinAndSelect('s.fields', 'fields')
|
||||
|
||||
qb.where('s.form = :form', { form: form.id })
|
||||
|
||||
// TODO readd sort
|
||||
|
||||
@ -7,6 +7,7 @@ import { SubmissionSetFieldInput } from '../../dto/submission/submission.set.fie
|
||||
import { SubmissionEntity } from '../../entity/submission.entity'
|
||||
import { SubmissionFieldEntity } from '../../entity/submission.field.entity'
|
||||
import { SubmissionHookService } from './submission.hook.service'
|
||||
import { SubmissionNotificationService } from './submission.notification.service'
|
||||
|
||||
@Injectable()
|
||||
export class SubmissionSetFieldService {
|
||||
@ -16,6 +17,7 @@ export class SubmissionSetFieldService {
|
||||
@InjectRepository(SubmissionFieldEntity)
|
||||
private readonly submissionFieldRepository: Repository<SubmissionFieldEntity>,
|
||||
private readonly webHook: SubmissionHookService,
|
||||
private readonly notifications: SubmissionNotificationService,
|
||||
private readonly logger: PinoLogger,
|
||||
) {
|
||||
}
|
||||
@ -24,7 +26,7 @@ export class SubmissionSetFieldService {
|
||||
let field = submission.fields.find(field => field.field.id.toString() === input.field)
|
||||
|
||||
if (field) {
|
||||
field.fieldValue = input.data
|
||||
field.fieldValue = JSON.parse(input.data)
|
||||
|
||||
await this.submissionFieldRepository.save(field)
|
||||
} else {
|
||||
@ -33,7 +35,7 @@ export class SubmissionSetFieldService {
|
||||
field.submission = submission
|
||||
field.field = submission.form.fields.find(field => field.id.toString() === input.field)
|
||||
field.fieldType = field.field.type
|
||||
field.fieldValue = input.data
|
||||
field.fieldValue = JSON.parse(input.data)
|
||||
|
||||
field = await this.submissionFieldRepository.save(field)
|
||||
|
||||
@ -50,6 +52,9 @@ export class SubmissionSetFieldService {
|
||||
this.webHook.process(submission).catch(e => {
|
||||
this.logger.error(`failed to send webhooks: ${e.message}`)
|
||||
})
|
||||
this.notifications.process(submission).catch(e => {
|
||||
this.logger.error(`failed to send notifications: ${e.message}`)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,11 +20,15 @@ export class SubmissionStartService {
|
||||
form: FormEntity,
|
||||
input: SubmissionStartInput,
|
||||
user?: UserEntity,
|
||||
ipAddr?: string,
|
||||
): Promise<SubmissionEntity> {
|
||||
const submission = new SubmissionEntity()
|
||||
|
||||
submission.form = form
|
||||
submission.user = user
|
||||
submission.ipAddr = ipAddr || '?'
|
||||
submission.timeElapsed = 0
|
||||
submission.percentageComplete = 0
|
||||
|
||||
submission.device.language = input.device.language
|
||||
submission.device.name = input.device.name
|
||||
|
||||
19
yarn.lock
19
yarn.lock
@ -2280,6 +2280,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c"
|
||||
integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==
|
||||
|
||||
"@types/request-ip@^0.0.35":
|
||||
version "0.0.35"
|
||||
resolved "https://registry.yarnpkg.com/@types/request-ip/-/request-ip-0.0.35.tgz#c30e832533296151c221532c48c10591b7edd468"
|
||||
integrity sha512-FtI7lv1EDaZnWmaCU7ZTwfOpW76EioocaWyiSeWdfW1cDPZYzzij781A5O/UeHQUN9yjtjEcD3StTzZjKG0XEA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/semver@^6.0.0":
|
||||
version "6.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-6.2.2.tgz#5c27df09ca39e3c9beb4fae6b95f4d71426df0a9"
|
||||
@ -6847,6 +6854,11 @@ is-wsl@^2.1.1, is-wsl@^2.2.0:
|
||||
dependencies:
|
||||
is-docker "^2.0.0"
|
||||
|
||||
is_js@^0.9.0:
|
||||
version "0.9.0"
|
||||
resolved "https://registry.yarnpkg.com/is_js/-/is_js-0.9.0.tgz#0ab94540502ba7afa24c856aa985561669e9c52d"
|
||||
integrity sha1-CrlFQFArp6+iTIVqqYVWFmnpxS0=
|
||||
|
||||
isarray@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
|
||||
@ -10235,6 +10247,13 @@ repeat-string@^1.6.1:
|
||||
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
|
||||
integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
|
||||
|
||||
request-ip@^2.1.3:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/request-ip/-/request-ip-2.1.3.tgz#99ab2bafdeaf2002626e28083cb10597511d9e14"
|
||||
integrity sha512-J3qdE/IhVM3BXkwMIVO4yFrvhJlU3H7JH16+6yHucadT4fePnR8dyh+vEs6FIx0S2x5TCt2ptiPfHcn0sqhbYQ==
|
||||
dependencies:
|
||||
is_js "^0.9.0"
|
||||
|
||||
request-promise-core@1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user