Add linting, formatting and pre-commit (#53)

* Add dev-dependancies

* Security update npm packages

* Force audit fix

* Add linting, formatting and precommit

* Format files
This commit is contained in:
E 2024-04-29 13:21:31 +01:00 committed by GitHub
parent 80813d48e3
commit 8449b30401
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 4064 additions and 2039 deletions

15
.editorconfig Normal file
View File

@ -0,0 +1,15 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = false
[pug.ts]
indent_size = 4

3
.eslintrc.json Normal file
View File

@ -0,0 +1,3 @@
{
"extends": ["prettier"]
}

1
.husky/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
_

4
.husky/pre-commit Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged

8
.prettierrc.json Normal file
View File

@ -0,0 +1,8 @@
{
"trailingComma": "es5",
"semi": true,
"tabWidth": 2,
"singleQuote": true,
"jsxSingleQuote": true,
"plugins": ["prettier-plugin-tailwindcss"]
}

70
app.js
View File

@ -4,7 +4,7 @@ var path = require('path');
var cookieParser = require('cookie-parser');
var mLogger = require('morgan');
var logger = require('./config/winston');
const helmet = require("helmet");
const helmet = require('helmet');
var indexRouter = require('./routes/index');
var aboutRouter = require('./routes/about');
@ -12,7 +12,8 @@ var contactRouter = require('./routes/contact');
var app = express();
if (process.env.IS_DOCKER != 'true') app.set('trust proxy', 'loopback,uniquelocal');
if (process.env.IS_DOCKER != 'true')
app.set('trust proxy', 'loopback,uniquelocal');
app.disable('x-powered-by');
// view engine setup
@ -20,26 +21,43 @@ app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
if (process.env.NODE_ENV === 'production') {
app.use(mLogger("common", { "stream": logger.stream }));
app.use(mLogger('common', { stream: logger.stream }));
} else {
app.use(mLogger('dev'));
app.use(mLogger('dev'));
}
app.use(helmet());
app.use(
helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'", "https://hcaptcha.com", "https://*.hcaptcha.com", "https://cdn.ravenjs.com/"],
imgSrc: ["'self'", "https://blog.pastel.codes", "https://static.ghost.org", "https://secure.gravatar.com"],
styleSrc: ["'self'", "'unsafe-inline'", "https://hcaptcha.com", "https://*.hcaptcha.com"],
fontSrc: ["'self'", "data:"],
frameSrc: ["https://hcaptcha.com", "https://*.hcaptcha.com"],
objectSrc: ["'none'"],
upgradeInsecureRequests: [],
},
})
);
helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: [
"'self'",
"'unsafe-inline'",
"'unsafe-eval'",
'https://hcaptcha.com',
'https://*.hcaptcha.com',
'https://cdn.ravenjs.com/',
],
imgSrc: [
"'self'",
'https://blog.pastel.codes',
'https://static.ghost.org',
'https://secure.gravatar.com',
],
styleSrc: [
"'self'",
"'unsafe-inline'",
'https://hcaptcha.com',
'https://*.hcaptcha.com',
],
fontSrc: ["'self'", 'data:'],
frameSrc: ['https://hcaptcha.com', 'https://*.hcaptcha.com'],
objectSrc: ["'none'"],
upgradeInsecureRequests: [],
},
})
);
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
@ -51,19 +69,19 @@ app.use('/about', aboutRouter);
app.use('/contact', contactRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
app.use(function (req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
app.use(function (err, req, res, _next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error', { title: 'Error', description: "Error" });
// render the error page
res.status(err.status || 500);
res.render('error', { title: 'Error', description: 'Error' });
});
module.exports = app;

64
bin/www
View File

@ -35,19 +35,19 @@ server.on('listening', onListening);
*/
function normalizePort(val) {
var port = parseInt(val, 10);
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
if (port >= 0) {
// port number
return port;
}
return false;
return false;
}
/**
@ -55,27 +55,25 @@ function normalizePort(val) {
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
@ -83,9 +81,7 @@ function onError(error) {
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
var addr = server.address();
var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
debug('Listening on ' + bind);
}

View File

@ -2,29 +2,29 @@ var winston = require('winston');
var appRoot = require('app-root-path');
var logger = new winston.createLogger({
transports: [
new winston.transports.File({
level: 'info',
filename: `${appRoot}/logs/app.log`,
handleExceptions: true,
json: true,
maxsize: 5242880, //5MB
maxFiles: 5,
colorize: false
}),
new winston.transports.Console({
level: 'debug',
handleExceptions: true,
json: false,
colorize: true
})
],
exitOnError: false
transports: [
new winston.transports.File({
level: 'info',
filename: `${appRoot}/logs/app.log`,
handleExceptions: true,
json: true,
maxsize: 5242880, //5MB
maxFiles: 5,
colorize: false,
}),
new winston.transports.Console({
level: 'debug',
handleExceptions: true,
json: false,
colorize: true,
}),
],
exitOnError: false,
});
logger.stream = {
write: function(message, encoding){
logger.info(message);
}
write: function (message, _encoding) {
logger.info(message);
},
};
module.exports = logger
module.exports = logger;

20
eslint.config.mjs Normal file
View File

@ -0,0 +1,20 @@
import globals from 'globals';
import pluginJs from '@eslint/js';
export default [
{ files: ['**/*.js'], languageOptions: { sourceType: 'commonjs' } },
{ languageOptions: { globals: { ...globals.browser, ...globals.node } } },
pluginJs.configs.recommended,
{
rules: {
'no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
},
],
},
},
];

5272
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -12,14 +12,19 @@
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "npx nodemon ./bin/www -e js,pug,sass",
"dev": "npx nodemon ./bin/www -e js,pug,sass",
"start": "npx ./bin/www -e js,pug,sass",
"watch-tailwind": "npx npm-watch",
"build-tailwind": "npx postcss src/tailwind.css -o public/stylesheets/style.css"
"build-tailwind": "npx postcss src/tailwind.css -o public/stylesheets/style.css",
"lint": "prettier --check . && eslint .",
"format": "prettier --write --ignore-path .gitignore .",
"prepare": "husky install"
},
"dependencies": {
"@sendgrid/mail": "^7.4.4",
"@sendgrid/mail": "^8.1.3",
"app-root-path": "^3.0.0",
"axios": "^0.21.3",
"autoprefixer": "^10.3.4",
"axios": "^1.6.8",
"cookie-parser": "^1.4.5",
"express": "^4.17.1",
"express-rate-limit": "^5.2.6",
@ -28,12 +33,25 @@
"http-errors": "^1.8.0",
"morgan": "^1.10.0",
"nodemailer": "^6.6.1",
"pug": "^3.0.2",
"tailwind-hamburgers": "^1.1.1",
"winston": "^3.3.3",
"autoprefixer": "^10.3.4",
"npm-watch": "^0.11.0",
"postcss-cli": "^8.3.1",
"tailwindcss": "^2.2.15"
"pug": "^3.0.2",
"tailwind-hamburgers": "^1.1.1",
"tailwindcss": "^2.2.15",
"winston": "^3.3.3"
},
"devDependencies": {
"@eslint/js": "^9.1.1",
"eslint": "^9.1.1",
"eslint-config-prettier": "^9.1.0",
"globals": "^15.1.0",
"husky": "^9.0.11",
"lint-staged": "^15.2.2",
"prettier": "^3.2.5",
"prettier-plugin-tailwindcss": "^0.5.14"
},
"lint-staged": {
"*.js": "eslint --cache --fix",
"*.{js,css,md,pug}": "prettier --write"
}
}

View File

@ -1,6 +1,3 @@
module.exports = {
plugins: [
require('tailwindcss'),
require('autoprefixer')
]
}
plugins: [require('tailwindcss'), require('autoprefixer')],
};

View File

@ -3,293 +3,289 @@ $black: #002234
$green: #CDE7B0
@font-face
font-family: 'Titling Gothic FB'
src: url("../fonts/TITLINGGOTHICFB-WIDE.OTF") format('opentype')
font-family: 'Titling Gothic FB'
src: url("../fonts/TITLINGGOTHICFB-WIDE.OTF") format('opentype')
@font-face
font-family: 'Gilroy'
src: url("../fonts/Gilroy-ExtraBold.otf") format('opentype')
font-family: 'Gilroy'
src: url("../fonts/Gilroy-ExtraBold.otf") format('opentype')
@font-face
font-family: 'Apercu Mono'
src: url("../fonts/ApercuMono.ttf") format('truetype')
font-family: 'Apercu Mono'
src: url("../fonts/ApercuMono.ttf") format('truetype')
html, body
width: 100%
height: 100%
width: 100%
height: 100%
body
background-color: $black
color: $pink
background-color: $black
color: $pink
h1
font-family: "Gilroy", sans-serif
font-family: "Gilroy", sans-serif
h2
font-weight: bold
font-family: "Apercu Mono", monospace
font-weight: bold
font-family: "Apercu Mono", monospace
p, label, input, textarea
font-family: "Apercu Mono", monospace
font-family: "Apercu Mono", monospace
label
font-size: 3vh
font-size: 3vh
a:hover
color: $green
color: $green
article
min-height: 100%
display: grid
grid-template-rows: auto 1fr auto
grid-template-columns: 100%
min-height: 100%
display: grid
grid-template-rows: auto 1fr auto
grid-template-columns: 100%
header
nav
a
span
font-size: 2rem
font-family: 'Titling Gothic FB', sans-serif
color: $pink
nav
a
span
font-size: 2rem
font-family: 'Titling Gothic FB', sans-serif
color: $pink
#navbarSupportedContent23
ul
background-color: $pink
li
transition: 0.5s
background-color: $pink
text-align: center
a
span
transition: 0.2s
color: $black
font-family: "Apercu Mono", monospace
ul
background-color: $pink
li
transition: 0.5s
background-color: $pink
text-align: center
a
span
transition: 0.2s
color: $black
font-family: "Apercu Mono", monospace
li:hover
background-color: $black
a
span
color: $pink
background-color: $black
a
span
color: $pink
.active
background-color: $black
a
span
color: $green
background-color: $black
a
span
color: $green
.active:hover
background-color: $pink
a
span
color: $black
background-color: $pink
a
span
color: $black
main
padding: 0.5rem
h1
padding-bottom: 1rem
font-size: 4.5rem
span
text-decoration: underline $pink
text-decoration-style: wavy
padding: 0.5rem
h1
padding-bottom: 1rem
font-size: 4.5rem
span
text-decoration: underline $pink
text-decoration-style: wavy
p
font-size: 2rem
margin-bottom: 5px
font-size: 2rem
margin-bottom: 5px
footer
text-align: center
font-size: 1rem
padding: 0.5rem
a
color: $pink
text-decoration: underline $pink
text-align: center
font-size: 1rem
padding: 0.5rem
a
color: $pink
text-decoration: underline $pink
.ef
max-height: 85vh
min-height: 85vh !important
max-height: 85vh
min-height: 85vh !important
.error
text-align: center
text-align: center
.pink-block
background-color: $pink
min-height: 100vh
background-color: $pink
min-height: 100vh
.center-v
min-height: 100% /* Fallback for browsers do NOT support vh unit */
min-height: 100vh /* These two lines are counted as one :-) */
min-height: 100% /* Fallback for browsers do NOT support vh unit */
min-height: 100vh /* These two lines are counted as one :-) */
display: flex
align-items: center
display: flex
align-items: center
.center-v-h
display: flex
justify-content: center
align-items: center
min-height: 100vh
display: flex
justify-content: center
align-items: center
min-height: 100vh
.logos-container
width: 3.5rem
width: 3.5rem
.logo
width: auto
height: 3.5rem
fill: $black
margin-bottom: 2.5rem
width: auto
height: 3.5rem
fill: $black
margin-bottom: 2.5rem
.logo-container
cursor: pointer
cursor: pointer
.about-pos
p
font-size: 3vh
margin-bottom: 3vh
p
font-size: 3vh
margin-bottom: 3vh
.pr
img
width: 100%
border: 2vh solid $pink
img
width: 100%
border: 2vh solid $pink
margin-bottom: 3vh
margin-top: 1vh
.pr-text
a
color: $pink
h1
a
color: $pink
h1
font-size: 4vh
margin-bottom: 0
padding-bottom: 0
font-size: 4vh
margin-bottom: 0
padding-bottom: 0
a:hover
color: $green
color: $green
p
font-size: 3vh
font-size: 3vh
.start
margin-top: 3vh
margin-top: 3vh
#contact-message
margin-top: 2vh
p
color: $green
span
text-decoration: underline $green
margin-top: 2vh
p
color: $green
span
text-decoration: underline $green
.h-captcha
margin-bottom: 0.5vh
margin-bottom: 0.5vh
.button-c
display: inline-block
font-weight: 400
color: #212529
text-align: center
vertical-align: middle
cursor: pointer
user-select: none
border: 1px solid $black
padding: 0.375rem 0.75rem
font-size: 1rem
line-height: 1.5
border-radius: 0.25rem
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out
font-family: "Apercu Mono"
display: inline-block
font-weight: 400
color: #212529
text-align: center
vertical-align: middle
cursor: pointer
user-select: none
border: 1px solid $black
padding: 0.375rem 0.75rem
font-size: 1rem
line-height: 1.5
border-radius: 0.25rem
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out
font-family: "Apercu Mono"
.button-c
background-color: $pink
background-color: $pink
.button-c:hover
background-color: $black
color: $pink
border-color: $pink
background-color: $black
color: $pink
border-color: $pink
// Nav stuff
.ham
width: 30px
height: 20px
position: relative
margin: 0px
-webkit-transform: rotate(0deg)
-moz-transform: rotate(0deg)
-o-transform: rotate(0deg)
transform: rotate(0deg)
-webkit-transition: .5s ease-in-out
-moz-transition: .5s ease-in-out
-o-transition: .5s ease-in-out
transition: .5s ease-in-out
cursor: pointer
width: 30px
height: 20px
position: relative
margin: 0px
-webkit-transform: rotate(0deg)
-moz-transform: rotate(0deg)
-o-transform: rotate(0deg)
transform: rotate(0deg)
-webkit-transition: .5s ease-in-out
-moz-transition: .5s ease-in-out
-o-transition: .5s ease-in-out
transition: .5s ease-in-out
cursor: pointer
.ham span
display: block
position: absolute
height: 3px
width: 100%
border-radius: 9px
opacity: 1
left: 0
-webkit-transform: rotate(0deg)
-moz-transform: rotate(0deg)
-o-transform: rotate(0deg)
transform: rotate(0deg)
-webkit-transition: .25s ease-in-out
-moz-transition: .25s ease-in-out
-o-transition: .25s ease-in-out
transition: .25s ease-in-out
background: $pink
display: block
position: absolute
height: 3px
width: 100%
border-radius: 9px
opacity: 1
left: 0
-webkit-transform: rotate(0deg)
-moz-transform: rotate(0deg)
-o-transform: rotate(0deg)
transform: rotate(0deg)
-webkit-transition: .25s ease-in-out
-moz-transition: .25s ease-in-out
-o-transition: .25s ease-in-out
transition: .25s ease-in-out
background: $pink
.ham span:nth-child(1)
top: 0
top: 0
.ham span:nth-child(2), .ham span:nth-child(3)
top: 10px
top: 10px
.ham span:nth-child(4)
top: 20px
top: 20px
.ham.open span:nth-child(1)
top: 11px
width: 0
left: 50%
top: 11px
width: 0
left: 50%
.ham.open span:nth-child(2)
-webkit-transform: rotate(45deg)
-moz-transform: rotate(45deg)
-o-transform: rotate(45deg)
transform: rotate(45deg)
-webkit-transform: rotate(45deg)
-moz-transform: rotate(45deg)
-o-transform: rotate(45deg)
transform: rotate(45deg)
.ham.open span:nth-child(3)
-webkit-transform: rotate(-45deg)
-moz-transform: rotate(-45deg)
-o-transform: rotate(-45deg)
transform: rotate(-45deg)
-webkit-transform: rotate(-45deg)
-moz-transform: rotate(-45deg)
-o-transform: rotate(-45deg)
transform: rotate(-45deg)
.ham.open span:nth-child(4)
top: 11px
width: 0
left: 50%
top: 11px
width: 0
left: 50%
@media only screen and (max-height: 421px)
.pr-text
margin-bottom: 5vh !important
.pr-text
margin-bottom: 5vh !important
@media only screen and (max-width: 575px)
.logo
height: 5rem
.logo
height: 5rem
.logos-container
width: 5rem
width: 5rem
.aaa
min-height: 40vh !important
min-height: 40vh !important
.aaaa
margin-top: 5vh !important
margin-top: 5vh !important
.ef
max-height: none !important
min-height: 0 !important
max-height: none !important
min-height: 0 !important
.ff
min-height: 82.8vh !important
min-height: 82.8vh !important
@media only screen and (max-height: 815px)
.ef
max-height: none !important
min-height: 0 !important
.ef
max-height: none !important
min-height: 0 !important

View File

@ -3,25 +3,35 @@ const axios = require('axios');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
const GHOST_KEY = process.env.GHOST_KEY
const base_url = `https://blog.pastel.codes/ghost/api/v3/content/posts/?key=${GHOST_KEY}`
router.get('/', function (req, res, _next) {
const GHOST_KEY = process.env.GHOST_KEY;
const base_url = `https://blog.pastel.codes/ghost/api/v3/content/posts/?key=${GHOST_KEY}`;
axios.all([
axios.get(`${base_url}&limit=3`),
axios.get(`${base_url}&limit=3&filter=tag:project`),
axios
.all([
axios.get(`${base_url}&limit=3`),
axios.get(`${base_url}&limit=3&filter=tag:project`),
])
.then(axios.spread((response1, response2) => {
var base = { title: 'About', description: 'Who??? What??? AAAAaaa, about me.'};
var blog = JSON.parse(JSON.stringify(response1.data).split('"posts":').join('"blog":'));
var projects = JSON.parse(JSON.stringify(response2.data).split('"posts":').join('"project":'));
var out = Object.assign(base, blog, projects);
.then(
axios.spread((response1, response2) => {
var base = {
title: 'About',
description: 'Who??? What??? AAAAaaa, about me.',
};
var blog = JSON.parse(
JSON.stringify(response1.data).split('"posts":').join('"blog":')
);
var projects = JSON.parse(
JSON.stringify(response2.data).split('"posts":').join('"project":')
);
var out = Object.assign(base, blog, projects);
res.render('about', out);
}))
.catch(error => {
console.log(error);
});
res.render('about', out);
})
)
.catch((error) => {
console.log(error);
});
});
module.exports = router;

View File

@ -1,73 +1,80 @@
var express = require('express');
var rate_limit = require("express-rate-limit")
const {verify} = require('hcaptcha');
const nodemailer = require('nodemailer')
var rate_limit = require('express-rate-limit');
const { verify } = require('hcaptcha');
var router = express.Router();
const sgMail = require('@sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
const contact_rate_limit = rate_limit({
windowMs: 10 * 60 * 1000, // 10 minutes
max: 5, // limit each IP to 10 requests per windowMs
message: "Too many contact requests, try again later.",
handler: function (req, res /*, next*/) {
res.render('error', {
title: "Error",
message: "Too many contact requests, try again later.",
error: {status: null}
})
},
windowMs: 10 * 60 * 1000, // 10 minutes
max: 5, // limit each IP to 10 requests per windowMs
message: 'Too many contact requests, try again later.',
handler: function (req, res /*, next*/) {
res.render('error', {
title: 'Error',
message: 'Too many contact requests, try again later.',
error: { status: null },
});
},
});
// POST route from contact form
router.post('/', contact_rate_limit, (req, res) => {
const TO_MAIL_USER = process.env.TO_MAIL_USER
const FROM_MAIL_USER = process.env.FROM_MAIL_USER
const HCAPTCHA_KEY = process.env.HCAPTCHA_KEY
const REPLY_TO_MAIL = process.env.REPLY_TO_MAIL
const token = req.body["g-recaptcha-response"];
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
const TO_MAIL_USER = process.env.TO_MAIL_USER;
const FROM_MAIL_USER = process.env.FROM_MAIL_USER;
const HCAPTCHA_KEY = process.env.HCAPTCHA_KEY;
const REPLY_TO_MAIL = process.env.REPLY_TO_MAIL;
const token = req.body['g-recaptcha-response'];
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
verify(HCAPTCHA_KEY, token)
.then((data) => {
if (data.success === true) {
const msg = {
to: TO_MAIL_USER,
from: FROM_MAIL_USER,
subject: 'New message from contact form at pastel.codes',
text: `${req.body.firstname} ${req.body.lastname} (${req.body.email})\nsays: ${req.body.message}\n\nip: ${ip}`
};
verify(HCAPTCHA_KEY, token)
.then((data) => {
if (data.success === true) {
const msg = {
to: TO_MAIL_USER,
from: FROM_MAIL_USER,
subject: 'New message from contact form at pastel.codes',
text: `${req.body.firstname} ${req.body.lastname} (${req.body.email})\nsays: ${req.body.message}\n\nip: ${ip}`,
};
sgMail
.send(msg)
.then(() => {
res.render('contact', {
title: 'Contact',
message: "I will get back to you soon!",
success: "Make sure the email is from ",
email: REPLY_TO_MAIL
})
})
.catch(error => {
console.log(error)
res.render('error', {title: 'Contact', message: "Email did not send"})
});
} else {
// rerender with same info in the text box and show error message
res.render('contact', {title: 'Contact', message: "Captcha failed, try again"});
}
})
.catch(error => {
sgMail
.send(msg)
.then(() => {
res.render('contact', {
title: 'Contact',
message: 'I will get back to you soon!',
success: 'Make sure the email is from ',
email: REPLY_TO_MAIL,
});
})
.catch((error) => {
console.log(error);
res.render('contact', {title: 'Contact', message: "Something wrong happened, try again later"});
res.render('error', {
title: 'Contact',
message: 'Email did not send',
});
});
} else {
// rerender with same info in the text box and show error message
res.render('contact', {
title: 'Contact',
message: 'Captcha failed, try again',
});
})
}
})
.catch((error) => {
console.log(error);
res.render('contact', {
title: 'Contact',
message: 'Something wrong happened, try again later',
});
});
});
/* GET home page. */
router.get('/', function (req, res, next) {
res.render('contact', {title: 'Contact', description: "Contact me!"});
router.get('/', function (req, res, _next) {
res.render('contact', { title: 'Contact', description: 'Contact me!' });
});
module.exports = router;

View File

@ -2,8 +2,8 @@ var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Home', description: "Hello, I'm E" });
router.get('/', function (req, res, _next) {
res.render('index', { title: 'Home', description: "Hello, I'm E" });
});
module.exports = router;

View File

@ -2,23 +2,23 @@
@tailwind components;
@font-face {
font-family: 'Titling Gothic FB';
src: url("../fonts/TITLINGGOTHICFB-WIDE.OTF") format('opentype')
font-family: 'Titling Gothic FB';
src: url('../fonts/TITLINGGOTHICFB-WIDE.OTF') format('opentype');
}
@font-face {
font-family: 'Gilroy';
src: url("../fonts/Gilroy-ExtraBold.otf") format('opentype')
font-family: 'Gilroy';
src: url('../fonts/Gilroy-ExtraBold.otf') format('opentype');
}
@font-face {
font-family: 'Apercu Mono';
src: url("../fonts/ApercuMono.ttf") format('truetype');
font-family: 'Apercu Mono';
src: url('../fonts/ApercuMono.ttf') format('truetype');
}
.wavy {
text-decoration: underline;
text-decoration-style: wavy;
text-decoration: underline;
text-decoration-style: wavy;
}
@tailwind utilities;

View File

@ -1,13 +1,13 @@
module.exports = {
mode: "jit",
mode: 'jit',
purge: ['views/*.pug'],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {
fontFamily: {
'extra': ['"Titling Gothic FB"'],
'sans': ['Gilroy'],
'mono': ['"Apercu Mono"'],
extra: ['"Titling Gothic FB"'],
sans: ['Gilroy'],
mono: ['"Apercu Mono"'],
},
colors: {
transparent: 'transparent',
@ -20,14 +20,12 @@ module.exports = {
},
green: {
DEFAULT: '#CDE7B0',
}
}
},
},
},
},
variants: {
extend: {},
},
plugins: [
require('tailwind-hamburgers'),
],
}
plugins: [require('tailwind-hamburgers')],
};