parent
384fe16671
commit
fb007d65de
14
api/_shared/formidablePromise.js
Normal file
14
api/_shared/formidablePromise.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import formidable from 'formidable'
|
||||||
|
|
||||||
|
export const formidablePromise = async (request, options) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const form = formidable(options)
|
||||||
|
|
||||||
|
form.parse(request, (err, fields, files) => {
|
||||||
|
if (err) {
|
||||||
|
return reject(err)
|
||||||
|
}
|
||||||
|
return resolve({ fields, files })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,29 +1,28 @@
|
||||||
const MG = require('mailgun.js')
|
import { formidablePromise } from './_shared/formidablePromise'
|
||||||
const fd = require('form-data')
|
const mailgun = require('mailgun-js')({
|
||||||
const mailgun = new MG(fd)
|
apiKey: process.env.MAILGUN_API_KEY,
|
||||||
|
domain: process.env.MAILGUN_DOMAIN
|
||||||
|
})
|
||||||
|
|
||||||
const mgOptions = {
|
export default async function handler(req, res) {
|
||||||
key: process.env.MAILGUN_API_KEY,
|
const { contact, subject, message } = await formidablePromise(req)
|
||||||
domain: process.env.MAILGUN_DOMAIN,
|
|
||||||
username: 'discoursio' // FIXME
|
|
||||||
}
|
|
||||||
|
|
||||||
const client = mailgun.client(mgOptions)
|
const text = `${contact}\n\n${message}`
|
||||||
|
|
||||||
const messageData = (subject, text) => {
|
const data = {
|
||||||
return {
|
|
||||||
from: 'Discours Feedback Robot <robot@discours.io>',
|
from: 'Discours Feedback Robot <robot@discours.io>',
|
||||||
to: 'welcome@discours.io',
|
to: 'welcome@discours.io',
|
||||||
subject,
|
subject,
|
||||||
text
|
text
|
||||||
}
|
}
|
||||||
}
|
|
||||||
export default async function handler(req, res) {
|
mailgun.messages().send(data, (error) => {
|
||||||
const { contact, subject, message } = req.query
|
if (error) {
|
||||||
try {
|
console.log('Error:', error)
|
||||||
const data = messageData(`${contact}: ${subject}`, message)
|
res.status(400).json(error)
|
||||||
client.messages.create(mgOptions.domain, data).then(console.log).catch(console.error)
|
} else {
|
||||||
} catch (error) {
|
console.log('Email sent successfully!')
|
||||||
res.status(400).json(error)
|
res.status(200)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import formidable from 'formidable'
|
||||||
import { Writable } from 'stream'
|
import { Writable } from 'stream'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import crypto from 'crypto'
|
import crypto from 'crypto'
|
||||||
|
import { formidablePromise } from './_shared/formidablePromise.js'
|
||||||
|
|
||||||
const { STORJ_ACCESS_KEY, STORJ_SECRET_KEY, STORJ_END_POINT, STORJ_BUCKET_NAME, CDN_DOMAIN } = process.env
|
const { STORJ_ACCESS_KEY, STORJ_SECRET_KEY, STORJ_END_POINT, STORJ_BUCKET_NAME, CDN_DOMAIN } = process.env
|
||||||
|
|
||||||
|
@ -15,19 +16,6 @@ const storjS3Client = new S3Client({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const formidablePromise = async (request, options) => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const form = formidable(options)
|
|
||||||
|
|
||||||
form.parse(request, (err, fields, files) => {
|
|
||||||
if (err) {
|
|
||||||
return reject(err)
|
|
||||||
}
|
|
||||||
return resolve({ fields, files })
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileConsumer = (acc) => {
|
const fileConsumer = (acc) => {
|
||||||
return new Writable({
|
return new Writable({
|
||||||
write: (chunk, _enc, next) => {
|
write: (chunk, _enc, next) => {
|
||||||
|
|
|
@ -48,6 +48,7 @@ export const TopicSelect = (props: TopicSelectProps) => {
|
||||||
|
|
||||||
const format = (item, type) => {
|
const format = (item, type) => {
|
||||||
if (type === 'option') {
|
if (type === 'option') {
|
||||||
|
// eslint-disable-next-line solid/components-return-once
|
||||||
return item.label
|
return item.label
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { createSignal, For } from 'solid-js'
|
import { createSignal, For, Show } from 'solid-js'
|
||||||
import type { Author } from '../../../graphql/types.gen'
|
import type { Author } from '../../../graphql/types.gen'
|
||||||
import { useAuthorsStore } from '../../../stores/zine/authors'
|
import { useAuthorsStore } from '../../../stores/zine/authors'
|
||||||
import { Icon } from '../../_shared/Icon'
|
import { Icon } from '../../_shared/Icon'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
import styles from './GrowingTextarea.module.scss'
|
import styles from './GrowingTextarea.module.scss'
|
||||||
import { createEffect, createSignal } from 'solid-js'
|
import { createSignal } from 'solid-js'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
class?: string
|
class?: string
|
||||||
|
|
|
@ -1,6 +1,41 @@
|
||||||
import { PageLayout } from '../components/_shared/PageLayout'
|
import { PageLayout } from '../components/_shared/PageLayout'
|
||||||
|
import { createSignal } from 'solid-js'
|
||||||
|
|
||||||
export const ConnectPage = () => {
|
export const ConnectPage = () => {
|
||||||
|
const [state, setState] = createSignal<'initial' | 'loading' | 'success' | 'error'>('initial')
|
||||||
|
|
||||||
|
const formRef: { current: HTMLFormElement } = { current: null }
|
||||||
|
const handleFormSubmit = async (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
setState('loading')
|
||||||
|
|
||||||
|
// eslint-disable-next-line unicorn/prefer-spread
|
||||||
|
const postData = Array.from(formRef.current.elements).reduce((acc, element) => {
|
||||||
|
const formField = element as unknown as { name: string; value: string }
|
||||||
|
if (formField.name) {
|
||||||
|
acc[formField.name] = formField.value
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc
|
||||||
|
}, {} as Record<string, string>)
|
||||||
|
|
||||||
|
const requestOptions = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(postData)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fetch('/api/feedback', requestOptions)
|
||||||
|
setState('success')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[handleFormSubmit]', error)
|
||||||
|
setState('error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout>
|
<PageLayout>
|
||||||
<article class="wide-container container--static-page">
|
<article class="wide-container container--static-page">
|
||||||
|
@ -15,26 +50,30 @@ export const ConnectPage = () => {
|
||||||
скорее! Если укажете свою почту, мы обязательно ответим.
|
скорее! Если укажете свою почту, мы обязательно ответим.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<form action="/api/feedback">
|
<form onSubmit={handleFormSubmit} ref={(el) => (formRef.current = el)}>
|
||||||
<div class="pretty-form__item">
|
<div class="pretty-form__item">
|
||||||
<select id="subject">
|
<select name="subject">
|
||||||
<option value="">Сотрудничество</option>
|
<option value="Сотрудничество" selected>
|
||||||
<option value="">Посоветовать тему</option>
|
Сотрудничество
|
||||||
<option value="">Сообщить об ошибке</option>
|
</option>
|
||||||
<option value="">Предложить проект</option>
|
<option value="Посоветовать тему">Посоветовать тему</option>
|
||||||
<option value="">Волонтерство</option>
|
<option value="Сообщить об ошибке">Сообщить об ошибке</option>
|
||||||
<option value="">Другое</option>
|
<option value="Предложить проект">Предложить проект</option>
|
||||||
|
<option value="Волонтерство">Волонтерство</option>
|
||||||
|
<option value="Другое">Другое</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="pretty-form__item">
|
<div class="pretty-form__item">
|
||||||
<input type="text" id="contact-email" placeholder="Email для обратной связи" />
|
<input type="email" name="contact" placeholder="Email для обратной связи" />
|
||||||
<label for="contact-email">Email для обратной связи</label>
|
<label for="contact-email">Email для обратной связи</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="pretty-form__item">
|
<div class="pretty-form__item">
|
||||||
<textarea id="message" placeholder="Текст сообщения" />
|
<textarea name="message" placeholder="Текст сообщения" />
|
||||||
<label for="message">Текст сообщения</label>
|
<label for="message">Текст сообщения</label>
|
||||||
</div>
|
</div>
|
||||||
<button class="button">Отправить письмо</button>
|
<button class="button" disabled={state() !== 'initial'} type="submit">
|
||||||
|
Отправить письмо
|
||||||
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user