Implement contact form backend
This commit is contained in:
@@ -1,9 +1,19 @@
|
||||
import {ORMUser, Singleton, Unit} from '@extollo/lib'
|
||||
import {Config, Inject, ORMUser, Singleton, Unit} from '@extollo/lib'
|
||||
import {User} from './models/User.model'
|
||||
import {Gotify} from 'gotify'
|
||||
|
||||
@Singleton()
|
||||
export class AppUnit extends Unit {
|
||||
@Inject()
|
||||
protected readonly config!: Config
|
||||
|
||||
async up(): Promise<void> {
|
||||
this.container().registerStaticOverride(ORMUser, User)
|
||||
|
||||
const gotify = new Gotify({
|
||||
server: this.config.safe('gotify.server').string(),
|
||||
})
|
||||
|
||||
this.container().registerSingletonInstance(Gotify, gotify)
|
||||
}
|
||||
}
|
||||
|
||||
6
src/app/configs/gotify.config.ts
Normal file
6
src/app/configs/gotify.config.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { env } from '@extollo/lib'
|
||||
|
||||
export default {
|
||||
server: env('GOTIFY_SERVER'),
|
||||
app: env('GOTIFY_TOKEN'),
|
||||
}
|
||||
@@ -1,6 +1,22 @@
|
||||
import {Controller, view, Injectable, SecurityContext, Inject, Collection, Config, Routing, file, Application, plaintext} from '@extollo/lib'
|
||||
import {
|
||||
Controller,
|
||||
view,
|
||||
Injectable,
|
||||
SecurityContext,
|
||||
Inject,
|
||||
Collection,
|
||||
Config,
|
||||
Routing,
|
||||
file,
|
||||
Application,
|
||||
make,
|
||||
Valid,
|
||||
} from '@extollo/lib'
|
||||
import {WorkItem} from '../../models/WorkItem.model'
|
||||
import {FeedPost} from '../../models/FeedPost.model'
|
||||
import {ContactForm} from '../../types/ContactForm.type'
|
||||
import {ContactSubmission} from '../../models/ContactSubmission.model'
|
||||
import {Gotify} from 'gotify'
|
||||
|
||||
@Injectable()
|
||||
export class Home extends Controller {
|
||||
@@ -13,6 +29,9 @@ export class Home extends Controller {
|
||||
@Inject()
|
||||
protected readonly routing!: Routing
|
||||
|
||||
@Inject()
|
||||
protected readonly gotify!: Gotify
|
||||
|
||||
public async welcome(feedPosts: Collection<FeedPost>) {
|
||||
const workItems = await this.getWorkItems()
|
||||
|
||||
@@ -88,4 +107,29 @@ export class Home extends Controller {
|
||||
.appPath('resources', 'assets', 'humans.txt')
|
||||
.read()
|
||||
}
|
||||
|
||||
async contact(data: Valid<ContactForm>) {
|
||||
const submission = make<ContactSubmission>(ContactSubmission)
|
||||
submission.name = data.name
|
||||
submission.email = data.email
|
||||
submission.message = data.message
|
||||
await submission.save()
|
||||
|
||||
this.gotify.send({
|
||||
app: this.config.get('gotify.app'),
|
||||
title: `Contact form submission from ${data.name}`,
|
||||
message: [
|
||||
`From: ${data.name}`,
|
||||
`E-mail: ${data.email}`,
|
||||
'Message:',
|
||||
data.message,
|
||||
].join('\n'),
|
||||
})
|
||||
|
||||
return view('message', {
|
||||
title: 'Message Sent',
|
||||
message: 'Your message has been sent. Thanks! I\'ll be in touch soon.',
|
||||
buttonAction: this.routing.getNamedPath('home').toRemote,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import {ParameterMiddleware, Injectable, Either, ResponseObject, Validator, Valid, right} from '@extollo/lib'
|
||||
import {ContactForm} from '../../../types/ContactForm.type'
|
||||
|
||||
/**
|
||||
* ContactForm Middleware
|
||||
* --------------------------------------------
|
||||
* Parse the contact form data and validate it. Provide the fields as middleware.
|
||||
*/
|
||||
@Injectable()
|
||||
export class ValidContactForm extends ParameterMiddleware<Valid<ContactForm>> {
|
||||
async handle(): Promise<Either<ResponseObject, Valid<ContactForm>>> {
|
||||
const validator = new Validator<ContactForm>()
|
||||
return right(validator.parse(this.request.input()))
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import {GoLinks} from '../controllers/GoLinks.controller'
|
||||
import {Feed} from '../controllers/Feed.controller'
|
||||
import {LoadSnippet} from '../middlewares/parameters/LoadSnippet.middleware'
|
||||
import {LoadFeedPosts} from '../middlewares/parameters/LoadFeedPosts.middleware'
|
||||
import {ValidContactForm} from '../middlewares/parameters/ValidContactForm.middleware'
|
||||
|
||||
Route
|
||||
.group('/', () => {
|
||||
@@ -14,6 +15,11 @@ Route
|
||||
.calls<Home>(Home, home => home.welcome)
|
||||
.alias('home')
|
||||
|
||||
Route.post('/contact')
|
||||
.parameterMiddleware(ValidContactForm)
|
||||
.calls<Home>(Home, home => home.contact)
|
||||
.alias('contact')
|
||||
|
||||
Route.get('/humans.txt')
|
||||
.calls<Home>(Home, home => home.humans)
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import {Injectable, Migration, Inject, DatabaseService, FieldType, raw} from '@extollo/lib'
|
||||
|
||||
/**
|
||||
* CreateContactSubmissionsTableMigration
|
||||
* ----------------------------------
|
||||
* Put some description here.
|
||||
*/
|
||||
@Injectable()
|
||||
export default class CreateContactSubmissionsTableMigration extends Migration {
|
||||
@Inject()
|
||||
protected readonly db!: DatabaseService
|
||||
|
||||
/**
|
||||
* Apply the migration.
|
||||
*/
|
||||
async up(): Promise<void> {
|
||||
const schema = this.db.get().schema()
|
||||
const table = await schema.table('contact_submissions')
|
||||
|
||||
table.primaryKey('contact_submission_id').required()
|
||||
|
||||
table.column('email')
|
||||
.type(FieldType.varchar)
|
||||
.required()
|
||||
|
||||
table.column('name')
|
||||
.type(FieldType.varchar)
|
||||
.required()
|
||||
|
||||
table.column('message')
|
||||
.type(FieldType.text)
|
||||
|
||||
table.column('sent_at')
|
||||
.type(FieldType.timestamp)
|
||||
.default(raw('NOW()'))
|
||||
|
||||
await schema.commit(table)
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo the migration.
|
||||
*/
|
||||
async down(): Promise<void> {
|
||||
const schema = this.db.get().schema()
|
||||
const table = await schema.table('contact_submissions')
|
||||
|
||||
table.dropIfExists()
|
||||
|
||||
await schema.commit(table)
|
||||
}
|
||||
}
|
||||
27
src/app/models/ContactSubmission.model.ts
Normal file
27
src/app/models/ContactSubmission.model.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import {Field, FieldType, Injectable, Model} from '@extollo/lib'
|
||||
|
||||
/**
|
||||
* ContactSubmission Model
|
||||
* -----------------------------------
|
||||
* A message submitted via the contact form on my website.
|
||||
*/
|
||||
@Injectable()
|
||||
export class ContactSubmission extends Model<ContactSubmission> {
|
||||
protected static table = 'contact_submissions'
|
||||
protected static key = 'contact_submission_id'
|
||||
|
||||
@Field(FieldType.serial, 'contact_submission_id')
|
||||
protected id?: number
|
||||
|
||||
@Field(FieldType.varchar)
|
||||
public email!: string
|
||||
|
||||
@Field(FieldType.varchar)
|
||||
public name!: string
|
||||
|
||||
@Field(FieldType.text)
|
||||
public message!: string
|
||||
|
||||
@Field(FieldType.timestamp, 'sent_at')
|
||||
public sentAt = new Date()
|
||||
}
|
||||
@@ -40,13 +40,11 @@ block content
|
||||
p I'd love to hear from you if you have questions or inquiries related to me or my projects. You can get in touch by text, e-mail, or using this form. I also occasionally share thoughts on my <a href="/blog">blog</a>.
|
||||
p <b>E-mail:</b> <a href="mailto:shout@garrettmills.dev">shout@garrettmills.dev</a>
|
||||
.form
|
||||
form#contact-form
|
||||
form#contact-form(method='post' action=named('contact'))
|
||||
.form-group
|
||||
input#contactEmail.form-control(type='email' name='email' placeholder='E-Mail Address' required)
|
||||
.form-group
|
||||
input#contactFirst.form-control(name='first' placeholder='First Name' required)
|
||||
.form-group
|
||||
input#contactLast.form-control(name='last' placeholder='Last Name' required)
|
||||
input#contactFirst.form-control(name='name' placeholder='Name' required)
|
||||
.form-group
|
||||
textarea.form-control#contactMessage(name='message' placeholder='Message' required rows=6)
|
||||
.form-group
|
||||
|
||||
12
src/app/types/ContactForm.type.ts
Normal file
12
src/app/types/ContactForm.type.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
/** A contact form submission. */
|
||||
export interface ContactForm {
|
||||
/** @email */
|
||||
email: string
|
||||
|
||||
/** The submitter's name */
|
||||
name: string
|
||||
|
||||
/** The body of the contact form submission. */
|
||||
message: string
|
||||
}
|
||||
Reference in New Issue
Block a user