Compare commits
10 commits
c3376522eb
...
2c0671e870
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c0671e870 | ||
|
|
35cac6560b | ||
|
|
aadeac6695 | ||
|
|
fbaa65d10a | ||
|
|
2224841564 | ||
|
|
aac4ed731b | ||
|
|
acd510b1d6 | ||
|
|
c33b5ac2a8 | ||
|
|
8e631c436a | ||
|
|
3d9d14705a |
7 changed files with 152 additions and 25 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,3 +2,4 @@ node_modules
|
||||||
package.json
|
package.json
|
||||||
package-lock.json
|
package-lock.json
|
||||||
assets/js/validate.min.js
|
assets/js/validate.min.js
|
||||||
|
assets/public
|
||||||
|
|
|
||||||
18
Dockerfile
Normal file
18
Dockerfile
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
FROM node:18-alpine
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
# Use --omit=dev instead of deprecated --production flag
|
||||||
|
RUN npm install --omit=dev
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
EXPOSE 7000
|
||||||
|
|
||||||
|
RUN addgroup -S contactform && adduser -S contactform -G contactform
|
||||||
|
RUN chown -R contactform:contactform /app
|
||||||
|
USER contactform
|
||||||
|
|
||||||
|
CMD ["node", "ghost-contact-svc.js"]
|
||||||
22
README.md
22
README.md
|
|
@ -1,5 +1,10 @@
|
||||||
# ghost-contact-form
|
# ghost-contact-form
|
||||||
Contact Forms in Ghost — Without External Services
|
Add a beautiful, fully functional contact form to your Ghost blog or website without paying for external services.
|
||||||
|
|
||||||
|
*Not running on Ghost?* This solution also works with Gatsby, Hugo, Hexo, Nuxt and any other static site generator.
|
||||||
|
|
||||||
|
## Tutorial
|
||||||
|
- [Contact Forms in Ghost](https://atmolabs.org/contact-forms-in-ghost/)
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
- A supported version of Node.js
|
- A supported version of Node.js
|
||||||
|
|
@ -26,14 +31,23 @@ EMAIL_FROM = noreply@your-blog.com
|
||||||
EMAIL_TO = your@email.com
|
EMAIL_TO = your@email.com
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Additionally, you can add a port variable `PORT = `, if you want to change the default port `7000` to something else.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```
|
```
|
||||||
$ node ghost-contact-svc.js
|
$ node ghost-contact-svc.js
|
||||||
```
|
```
|
||||||
|
You should see the message `Listening on http://localhost:7000`.
|
||||||
|
|
||||||
## Test Locally
|
## Test Locally
|
||||||
|
|
||||||
```
|
[http://localhost:7000/v1/demo](http://localhost:7000/v1/demo)
|
||||||
$ http://lcoalhost:7000/v1/demo
|
|
||||||
```
|
## Deploy (More detailed info in the tutorial)
|
||||||
|
|
||||||
|
- copy the work folder `ghost-contact-form` to your server
|
||||||
|
- use `ghost-contact.service` as a starting point for running `ghost-contact-svc.js` as a systemd daemon
|
||||||
|
- configure your web-server to map endpoint `http://localhost:7000/` to a public endpoint, e.g. `https://api.your-blog.com/`
|
||||||
|
- Use the files in folder `ghost-admin` to configure your Ghost front-end
|
||||||
|
- Other front-end configuration files upon request!
|
||||||
|
|
|
||||||
53
README.md.bak
Normal file
53
README.md.bak
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
# ghost-contact-form
|
||||||
|
Add a beautiful, fully functional contact form to your Ghost blog or website without paying for external services.
|
||||||
|
|
||||||
|
*Not running on Ghost?* This solution also works with Gatsby, Hugo, Hexo, Nuxt and any other static site generator.
|
||||||
|
|
||||||
|
## Tutorial
|
||||||
|
- [Contact Forms in Ghost](https://atmolabs.org/contact-forms-in-ghost/)
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
- A supported version of Node.js
|
||||||
|
- npm to manage packages
|
||||||
|
|
||||||
|
## Quickstart Install
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git clone https://github.com/styxlab/ghost-contact-form.git
|
||||||
|
$ cd ghost-contact-form
|
||||||
|
$ sh install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configure
|
||||||
|
|
||||||
|
Adapt the `.env` file to your needs. The following variables must be defined.
|
||||||
|
|
||||||
|
```
|
||||||
|
SMTP_HOST = mail.server.com
|
||||||
|
SMTP_USER = user@server.com
|
||||||
|
SMTP_PASS = strong password
|
||||||
|
ALLOW_ORIGIN = https://your-blog.com
|
||||||
|
EMAIL_FROM = noreply@your-blog.com
|
||||||
|
EMAIL_TO = your@email.com
|
||||||
|
```
|
||||||
|
|
||||||
|
Additionally, you can add a port vairable `PORT = `, if you want to change thedefault port `7000` to something else.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
$ node ghost-contact-svc.js
|
||||||
|
```
|
||||||
|
You should see the message `Listening on http://localhost:7000`.
|
||||||
|
|
||||||
|
## Test Locally
|
||||||
|
|
||||||
|
[http://localhost:7000/v1/demo](http://localhost:7000/v1/demo)
|
||||||
|
|
||||||
|
## Deploy (More detailed info in the tutorial)
|
||||||
|
|
||||||
|
- copy the work folder `ghost-contact-form` to your server
|
||||||
|
- use `ghost-contact.service` as a starting point for running `ghost-contact-svc.js` as a systemd deamon
|
||||||
|
- configure your webserver to map endpoint `http://localhost:7000/` to a public endpoint, e.g. `https://api.your-blog.com/`
|
||||||
|
- Use the files in folder `ghost-admin` to configure your Ghost front-end
|
||||||
|
- Other front-end config files upon request!
|
||||||
|
|
@ -34,8 +34,7 @@ body {
|
||||||
.header {
|
.header {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 50%;
|
margin-left: 100px;
|
||||||
margin-left: -480px;
|
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-bottom: 1px solid #ddd;
|
border-bottom: 1px solid #ddd;
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
|
|
|
||||||
48
docker-compose.yml
Normal file
48
docker-compose.yml
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
services:
|
||||||
|
contact-form:
|
||||||
|
build: .
|
||||||
|
container_name: contact-form
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
# Only expose to localhost - nginx will proxy
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:7000:7000"
|
||||||
|
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
|
||||||
|
# Security hardening
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
read_only: true
|
||||||
|
tmpfs:
|
||||||
|
- /tmp
|
||||||
|
|
||||||
|
networks:
|
||||||
|
contact-form-net:
|
||||||
|
mailcow-network:
|
||||||
|
ipv4_address: 172.22.1.243
|
||||||
|
|
||||||
|
# Resource limits
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: '0.25'
|
||||||
|
memory: 128M
|
||||||
|
reservations:
|
||||||
|
memory: 64M
|
||||||
|
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--quiet", "--tries=1",
|
||||||
|
"--spider", "http://localhost:7000/v1/demo"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 10s
|
||||||
|
|
||||||
|
networks:
|
||||||
|
contact-form-net:
|
||||||
|
driver: bridge
|
||||||
|
mailcow-network:
|
||||||
|
external: true
|
||||||
|
name: mailcowdockerized_mailcow-network
|
||||||
|
|
@ -8,7 +8,7 @@ var smtpTrans = require('nodemailer-smtp-transport');
|
||||||
var validator = require("email-validator");
|
var validator = require("email-validator");
|
||||||
var sanitize = require('sanitize-html');
|
var sanitize = require('sanitize-html');
|
||||||
|
|
||||||
var smtp = { "auth": {}, "port": 465, "secure": true, "tls": {"rejectUnauthorized": false}, "debug": false};
|
var smtp = { "auth": {}, "port": 587, "secure": false, "tls": {"rejectUnauthorized": false}, "debug": false};
|
||||||
smtp.host = process.env.SMTP_HOST;
|
smtp.host = process.env.SMTP_HOST;
|
||||||
smtp.auth.user = process.env.SMTP_USER;
|
smtp.auth.user = process.env.SMTP_USER;
|
||||||
smtp.auth.pass = process.env.SMTP_PASS;
|
smtp.auth.pass = process.env.SMTP_PASS;
|
||||||
|
|
@ -18,9 +18,9 @@ var app = express();
|
||||||
app.disable('x-powered-by');
|
app.disable('x-powered-by');
|
||||||
app.use(bodyParser.urlencoded({limit: '1mb', extended: false}));
|
app.use(bodyParser.urlencoded({limit: '1mb', extended: false}));
|
||||||
app.use(bodyParser.json({limit: '1mb'}));
|
app.use(bodyParser.json({limit: '1mb'}));
|
||||||
app.use(cors({origin: process.env.ALLOW_ORIGIN,
|
// CORS handled by nginx
|
||||||
allowedHeaders: ['Content-Type', 'application/json; charset=utf-8', 'text/html; charset=utf-8']}));
|
// app.use(cors({origin: process.env.ALLOW_ORIGIN,
|
||||||
|
// allowedHeaders: ['Content-Type', 'application/json; charset=utf-8', 'text/html; charset=utf-8']}));
|
||||||
app.use('/v1/assets', express.static(__dirname + '/assets'));
|
app.use('/v1/assets', express.static(__dirname + '/assets'));
|
||||||
|
|
||||||
app.post('/v1/contact', function(req, res) {
|
app.post('/v1/contact', function(req, res) {
|
||||||
|
|
@ -37,22 +37,16 @@ app.listen(process.env.PORT || 7000, function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
function sendEmail(data, res){
|
function sendEmail(data, res){
|
||||||
var email = { "from": process.env.EMAIL_FROM, "to": process.env.EMAIL_TO};
|
var email = {
|
||||||
email.subject = 'MY BLOG - ' + (data.subject && data.subject.toUpperCase());
|
"from": process.env.EMAIL_FROM,
|
||||||
|
"to": process.env.EMAIL_TO,
|
||||||
|
"replyTo": `${data.name} <${data.email}>`,
|
||||||
|
"subject": `tobiaseigen.org contact form message from ${data.name}`,
|
||||||
|
"text": data.message
|
||||||
|
};
|
||||||
|
|
||||||
const output = `
|
|
||||||
<p>Hello,<p>
|
|
||||||
<p>You got a new contact request.</p>
|
|
||||||
<h3>Contact Details</h3>
|
|
||||||
<ul><li>Name: ${data.name}</li><li>Email: ${data.email}</li></ul>
|
|
||||||
<h3>Message:</h3>
|
|
||||||
<p>${data.message}</p>
|
|
||||||
`
|
|
||||||
email.html = sanitize(output, {
|
|
||||||
allowedTags: sanitize.defaults.allowedTags.concat([ 'img' ])
|
|
||||||
});
|
|
||||||
transporter.sendMail(email, function(error, info){
|
transporter.sendMail(email, function(error, info){
|
||||||
if(error) return res.json({"sendEmail": "failed"});
|
if(error) { console.log("SMTP ERROR:", error); return res.json({"sendEmail": "failed"}); }
|
||||||
res.json({"sendEmail": "ok"});
|
res.json({"sendEmail": "ok"});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue