Compare commits
12 Commits
c16657f996
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| e4a24a6d55 | |||
| 3a80dc0dc3 | |||
| b7568e2ba2 | |||
| f17d267336 | |||
| 92768c5661 | |||
| b44868361d | |||
| 419c8a579c | |||
| 9bb90607b6 | |||
| 65a55300a1 | |||
| 5c42fb7f41 | |||
| c1c0b4e723 | |||
| 83cad4b0ae |
20
.gitea/workflows/deploy.yaml
Normal file
20
.gitea/workflows/deploy.yaml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
name: Deploy on Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy-locally:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Copy to Host Volume
|
||||||
|
run: |
|
||||||
|
# Ensure the destination exists
|
||||||
|
mkdir -p /mnt/shared/pathtoglory.querst/public
|
||||||
|
# Copy the specific folder contents to the mounted volume
|
||||||
|
cp -r ./app/public /mnt/shared/pathtoglory.quest/public
|
||||||
|
echo "Public files copied to host successfully."
|
||||||
52
.gitignore
vendored
52
.gitignore
vendored
@@ -1,64 +1,14 @@
|
|||||||
# Logs
|
# Logs
|
||||||
logs
|
|
||||||
*.log
|
*.log
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
*.pid.lock
|
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
||||||
lib-cov
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
|
||||||
coverage
|
|
||||||
|
|
||||||
# nyc test coverage
|
|
||||||
.nyc_output
|
|
||||||
|
|
||||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
|
||||||
.grunt
|
|
||||||
|
|
||||||
# Bower dependency directory (https://bower.io/)
|
|
||||||
bower_components
|
|
||||||
|
|
||||||
# node-waf configuration
|
|
||||||
.lock-wscript
|
|
||||||
|
|
||||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
|
||||||
build/Release
|
|
||||||
|
|
||||||
# Dependency directories
|
# Dependency directories
|
||||||
node_modules/
|
node_modules/
|
||||||
jspm_packages/
|
|
||||||
|
|
||||||
# Typescript v1 declaration files
|
|
||||||
typings/
|
|
||||||
|
|
||||||
# Optional npm cache directory
|
# Optional npm cache directory
|
||||||
.npm
|
.npm
|
||||||
|
|
||||||
# Optional eslint cache
|
|
||||||
.eslintcache
|
|
||||||
|
|
||||||
# Optional REPL history
|
|
||||||
.node_repl_history
|
|
||||||
|
|
||||||
# Output of 'npm pack'
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Yarn Integrity file
|
|
||||||
.yarn-integrity
|
|
||||||
|
|
||||||
# dotenv environment variables file
|
# dotenv environment variables file
|
||||||
.env
|
app/.env
|
||||||
|
|
||||||
# next.js build output
|
|
||||||
.next
|
|
||||||
|
|
||||||
#postgres stuff
|
#postgres stuff
|
||||||
postgresql
|
postgresql
|
||||||
|
|||||||
@@ -4,30 +4,59 @@
|
|||||||
* Module dependencies.
|
* Module dependencies.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import app from './app.js';
|
import app from './server/app.js';
|
||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
debug('pathtoglory:server');
|
debug('pathtoglory:server');
|
||||||
import https from 'https';
|
import http from 'http';
|
||||||
import path from 'node:path';
|
import rssGenerator from './server/rss/index.js';
|
||||||
import fs from 'node:fs';
|
import PostController from './server/controllers/Post.controller.js';
|
||||||
|
import OptionController from './server/controllers/Option.controller.js';
|
||||||
|
|
||||||
const sslOptions = {
|
// Check needed options
|
||||||
key: fs.readFileSync(path.join(import.meta.dirname, 'localhost-key.pem')),
|
Promise.all([
|
||||||
cert: fs.readFileSync(path.join(import.meta.dirname, 'localhost.pem'))
|
OptionController.get('blog_name'),
|
||||||
};
|
OptionController.get('blog_description'),
|
||||||
|
`https://${process.env.RP_ID}/`,
|
||||||
|
PostController.getAll()
|
||||||
|
])
|
||||||
|
.then(([name, description, link, posts]) => {
|
||||||
|
rssGenerator.setChannel(
|
||||||
|
name || process.env.RP_NAME,
|
||||||
|
description || '',
|
||||||
|
link,
|
||||||
|
posts[0] ? new Date(posts[0].updated_at) : new Date()
|
||||||
|
);
|
||||||
|
|
||||||
|
posts
|
||||||
|
.map(post => {
|
||||||
|
return {
|
||||||
|
title: post.title,
|
||||||
|
description: post.content.slice(0, 255),
|
||||||
|
link: `${link}/posts/${post.id}`,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.forEach(post => {
|
||||||
|
rssGenerator.addItem(post);
|
||||||
|
});
|
||||||
|
|
||||||
|
rssGenerator.generateFile();
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get port from environment and store in Express.
|
* Get port from environment and store in Express.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var port = normalizePort(process.env.PORT || '1234');
|
var port = normalizePort(process.env.PORT);
|
||||||
app.set('port', port);
|
app.set('port', port);
|
||||||
|
app.set('trust proxy', true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create HTTP server.
|
* Create HTTP server.
|
||||||
*/
|
*/
|
||||||
|
var server = http.createServer(app);
|
||||||
var server = https.createServer(sslOptions, app);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listen on provided port, on all network interfaces.
|
* Listen on provided port, on all network interfaces.
|
||||||
0
package-lock.json → app/package-lock.json
generated
0
package-lock.json → app/package-lock.json
generated
@@ -5,7 +5,7 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node --env-file=.env ./index.js",
|
"start": "node --env-file=.env ./index.js",
|
||||||
"db:migrate": "node db/migrate.js"
|
"db:migrate": "node --env-file=.env ./server/db/migrate.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@simplewebauthn/server": "^13.2.3",
|
"@simplewebauthn/server": "^13.2.3",
|
||||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -15,3 +15,7 @@ footer {
|
|||||||
border-top: 1px solid var(--primary);
|
border-top: 1px solid var(--primary);
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
li h2 {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
@@ -4,34 +4,36 @@ import path from 'path';
|
|||||||
import cookieParser from 'cookie-parser';
|
import cookieParser from 'cookie-parser';
|
||||||
import logger from 'morgan';
|
import logger from 'morgan';
|
||||||
|
|
||||||
|
import authenticateToken from './middlewares/authentication.js';
|
||||||
import indexRouter from './routes/index.js';
|
import indexRouter from './routes/index.js';
|
||||||
import usersRouter from './routes/users.js';
|
import usersRouter from './routes/users.js';
|
||||||
import postsRouter from './routes/posts.js';
|
import postsRouter from './routes/posts.js';
|
||||||
|
import conclaveRouter from './routes/conclave.js';
|
||||||
|
|
||||||
const __dirname = import.meta.dirname;
|
|
||||||
var app = express();
|
var app = express();
|
||||||
|
|
||||||
// view engine setup
|
// view engine setup
|
||||||
app.set('views', path.join(__dirname, 'views'));
|
app.set('views', path.join(import.meta.dirname, 'views'));
|
||||||
app.set('view engine', 'pug');
|
app.set('view engine', 'pug');
|
||||||
|
|
||||||
app.use(logger('dev'));
|
app.use(logger('dev'));
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use(express.urlencoded({ extended: false }));
|
app.use(express.urlencoded({ extended: true }));
|
||||||
app.use(cookieParser());
|
app.use(cookieParser());
|
||||||
app.use(express.static(path.join(__dirname, 'public')));
|
// app.use(express.static(path.join(import.meta.dirname, 'public')));
|
||||||
|
|
||||||
app.use('/', indexRouter);
|
app.use('/', indexRouter);
|
||||||
app.use('/users', usersRouter);
|
app.use('/users', usersRouter);
|
||||||
app.use('/posts', postsRouter);
|
app.use('/posts', postsRouter);
|
||||||
|
app.use('/conclave', authenticateToken, conclaveRouter);
|
||||||
|
|
||||||
// catch 404 and forward to error handler
|
// catch 404 and forward to error handler
|
||||||
app.use(function(req, res, next) {
|
app.use(function(_req, _res, next) {
|
||||||
next(createError(404));
|
next(createError(404));
|
||||||
});
|
});
|
||||||
|
|
||||||
// error handler
|
// error handler
|
||||||
app.use(function(err, req, res, next) {
|
app.use(function(err, req, res) {
|
||||||
// set locals, only providing error in development
|
// set locals, only providing error in development
|
||||||
res.locals.message = err.message;
|
res.locals.message = err.message;
|
||||||
res.locals.error = req.app.get('env') === 'development' ? err : {};
|
res.locals.error = req.app.get('env') === 'development' ? err : {};
|
||||||
60
app/server/controllers/Option.controller.js
Normal file
60
app/server/controllers/Option.controller.js
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import dbClient from "../db/index.js";
|
||||||
|
|
||||||
|
export default class OptionController {
|
||||||
|
static async getAll() {
|
||||||
|
let queryText = 'SELECT * FROM blog_options';
|
||||||
|
try {
|
||||||
|
const res = await dbClient.query(queryText);
|
||||||
|
return res.rows;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async get(name) {
|
||||||
|
const res = await dbClient.query({
|
||||||
|
text: 'SELECT value FROM blog_options WHERE name=$1',
|
||||||
|
values: [name]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.rows[0]) {
|
||||||
|
return res.rows[0].value;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[OptionController] cannot find option ', name);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async create(name, value) {
|
||||||
|
const res = await dbClient.query({
|
||||||
|
text: 'INSERT INTO blog_options(name, value) VALUES($1, $2)',
|
||||||
|
values: [name, value]
|
||||||
|
});
|
||||||
|
console.log('[OptionController] create:', res.rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(name, value) {
|
||||||
|
try {
|
||||||
|
const res = await dbClient.query({
|
||||||
|
text: 'UPDATE blog_options SET value = $2 WHERE name = $1',
|
||||||
|
values: [name, value]
|
||||||
|
});
|
||||||
|
return res.rows;
|
||||||
|
} catch(err) {
|
||||||
|
console.error('[OptionController] update error: ', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async delete(name) {
|
||||||
|
try {
|
||||||
|
const res = dbClient.query({
|
||||||
|
text: 'DELETE FROM blog_options WHERE name=$1',
|
||||||
|
values: [name]
|
||||||
|
});
|
||||||
|
return res.rows;
|
||||||
|
} catch(err) {
|
||||||
|
console.error('[OptionController] delete error: ', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,22 @@
|
|||||||
import client from "../db/index.js";
|
import client from "../db/index.js";
|
||||||
|
|
||||||
export default class PostController {
|
export default class PostController {
|
||||||
static async getAll() {
|
static async getAll(options) {
|
||||||
const res = await client.query("SELECT * FROM posts");
|
let queryText = "SELECT * FROM posts";
|
||||||
|
|
||||||
|
if (options && options.order === "asc") {
|
||||||
|
queryText += ' ORDER BY created_at ASC';
|
||||||
|
} else {
|
||||||
|
queryText += ' ORDER BY created_at DESC';
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await client.query(queryText);
|
||||||
return res.rows;
|
return res.rows;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async get(id) {
|
static async get(id) {
|
||||||
@@ -4,7 +4,7 @@ const db = new Pool({
|
|||||||
host: process.env.DB_HOST,
|
host: process.env.DB_HOST,
|
||||||
user: process.env.DB_USER,
|
user: process.env.DB_USER,
|
||||||
password: process.env.DB_PASSWORD,
|
password: process.env.DB_PASSWORD,
|
||||||
port: process.env.PORT,
|
port: process.env.DB_PORT,
|
||||||
database: process.env.DB_DATABASE,
|
database: process.env.DB_DATABASE,
|
||||||
max: 20,
|
max: 20,
|
||||||
idleTimeoutMillis: 30_000,
|
idleTimeoutMillis: 30_000,
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
CREATE XTENSION IF NOT EXISTS "uuid-ossp";
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||||
|
|
||||||
CREATE TABLE posts (
|
CREATE TABLE posts (
|
||||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
CREATE TABLE blog_options (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
name VARCHAR(255) NOT NULL UNIQUE,
|
||||||
|
value TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX blog_options_name_index ON blog_options (name);
|
||||||
|
|
||||||
|
-- Remove useless index
|
||||||
|
DROP INDEX index_passkeys;
|
||||||
@@ -5,6 +5,7 @@ const authenticateToken = function(req, res, next) {
|
|||||||
|
|
||||||
if(!token) {
|
if(!token) {
|
||||||
res.redirect("/");
|
res.redirect("/");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = token.split('.');
|
const data = token.split('.');
|
||||||
63
app/server/routes/conclave.js
Normal file
63
app/server/routes/conclave.js
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import express from 'express';
|
||||||
|
import PostController from '../controllers/Post.controller.js';
|
||||||
|
import OptionController from '../controllers/Option.controller.js';
|
||||||
|
|
||||||
|
var router = express.Router();
|
||||||
|
|
||||||
|
function handleOptionOperation (option) {
|
||||||
|
switch(option.action) {
|
||||||
|
case 'create':
|
||||||
|
OptionController.create(option.name, option.value);
|
||||||
|
break;
|
||||||
|
case 'update':
|
||||||
|
OptionController.update(option.name, option.value);
|
||||||
|
break;
|
||||||
|
case 'delete':
|
||||||
|
OptionController.delete(option.name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
router.get('/', function (_, res) {
|
||||||
|
res.render('conclave/admin_panel');
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/new_post', async function(_, res) {
|
||||||
|
res.render('conclave');
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/posts/new', async function(req, res) {
|
||||||
|
await PostController.create(req.body.title, req.body.content);
|
||||||
|
res.redirect('/conclave');
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/options', async function(_, res) {
|
||||||
|
const options = await OptionController.getAll();
|
||||||
|
res.render('conclave/options', { options });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/options/edit', async function (_, res) {
|
||||||
|
const options = await OptionController.getAll();
|
||||||
|
res.render('conclave/options_edit', { options });
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: currently we send the whole options even the one that arent changed. we need to update the front-end to only send the updated ones. I think we will need javascript.
|
||||||
|
router.post('/options/edit', async function (req, res) {
|
||||||
|
if (Array.isArray(req.body.name)) {
|
||||||
|
const options = req.body.name.map((name, index) => {
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
action: req.body.action[index],
|
||||||
|
value: req.body.value[index]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
options.forEach(handleOptionOperation);
|
||||||
|
} else {
|
||||||
|
handleOptionOperation(req.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.redirect('/conclave/options');
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
12
app/server/routes/index.js
Normal file
12
app/server/routes/index.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import express from 'express';
|
||||||
|
import PostController from '../controllers/Post.controller.js';
|
||||||
|
|
||||||
|
var router = express.Router();
|
||||||
|
|
||||||
|
/* GET home page. */
|
||||||
|
router.get('/', async function(_, res) {
|
||||||
|
const posts = await PostController.getAll();
|
||||||
|
res.render('index', { posts: posts });
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
13
app/server/rss/index.js
Normal file
13
app/server/rss/index.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { RssGenerator } from "./rssGenerator.js";
|
||||||
|
|
||||||
|
let generator = null;
|
||||||
|
|
||||||
|
export default (() => {
|
||||||
|
if (!generator) {
|
||||||
|
generator = Object.freeze(new RssGenerator({
|
||||||
|
filePath: process.env.RSS_FILE_PATH,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return generator;
|
||||||
|
})();
|
||||||
123
app/server/rss/rssGenerator.js
Normal file
123
app/server/rss/rssGenerator.js
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import fs from 'node:fs';
|
||||||
|
|
||||||
|
export class RssGenerator {
|
||||||
|
maxItem = 10;
|
||||||
|
items = [];
|
||||||
|
channel = {};
|
||||||
|
|
||||||
|
constructor(options) {
|
||||||
|
this.options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: move to pass an option parameter and iterate over the key/values
|
||||||
|
setChannel (title, description, link, pubDate) {
|
||||||
|
this.channel.title = title;
|
||||||
|
this.channel.description = description;
|
||||||
|
this.channel.link = link;
|
||||||
|
this.channel.pubDate = pubDate;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
addItem (item) {
|
||||||
|
if (!this.isItemValid(item)) {
|
||||||
|
console.error('[RssGenerator] addItem: item invalid.', item);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.items.push(item);
|
||||||
|
|
||||||
|
if (this.items.lenght > this.maxItem) {
|
||||||
|
this.items.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
isItemValid(item) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
generateFile() {
|
||||||
|
let data = '';
|
||||||
|
|
||||||
|
data += '<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel>';
|
||||||
|
data += this.#generateChannelInfos();
|
||||||
|
data += this.#generateItems();
|
||||||
|
data += '</channel></rss>';
|
||||||
|
|
||||||
|
this.#saveFile(data);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#generateChannelInfos() {
|
||||||
|
let infos = '';
|
||||||
|
|
||||||
|
infos += `<title>${this.channel.title}</title>\n`;
|
||||||
|
infos += `<description>${this.channel.description}</description>\n`;
|
||||||
|
infos += `<link>${this.channel.link}</link>\n`;
|
||||||
|
infos += `<pubDate>${RssGenerator.formatDate(this.channel.pubDate)}</pubDate>\n`;
|
||||||
|
infos += `<atom:link href="${this.channel.link}/rss.xml" rel="self" type="application/rss+xml" />\n`;
|
||||||
|
|
||||||
|
return infos;
|
||||||
|
}
|
||||||
|
|
||||||
|
#generateItems() {
|
||||||
|
let data = '';
|
||||||
|
|
||||||
|
for (const item of this.items) {
|
||||||
|
data += '<item>\n';
|
||||||
|
data += `<title>${item.title}</title>\n`;
|
||||||
|
data += `<link>${item.link}</link>\n`;
|
||||||
|
data += `<guid>${item.link}</guid>\n`;
|
||||||
|
data += `<description>${item.description}</description>\n`;
|
||||||
|
data += '</item>\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static formatDate(date) {
|
||||||
|
return new Intl.DateTimeFormat("en-GB", {
|
||||||
|
hour12: false,
|
||||||
|
weekday: "short",
|
||||||
|
year: "numeric",
|
||||||
|
month: "short",
|
||||||
|
day: "2-digit",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
second: "2-digit",
|
||||||
|
timeZone: "Europe/London",
|
||||||
|
}).formatToParts(date)
|
||||||
|
.filter(el => el.type !== "literal")
|
||||||
|
.reduce((acc, cur) => {
|
||||||
|
switch (cur.type) {
|
||||||
|
case "weekday":
|
||||||
|
acc += `${cur.value}, `;
|
||||||
|
break;
|
||||||
|
case "hour":
|
||||||
|
case "minute":
|
||||||
|
acc += `${cur.value}:`;
|
||||||
|
break;
|
||||||
|
case "timeZoneName":
|
||||||
|
acc += cur.value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
acc += `${cur.value} `;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, '')
|
||||||
|
.concat(" GMT");
|
||||||
|
}
|
||||||
|
|
||||||
|
#saveFile(data) {
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(this.options.filePath, data, { encoding: 'utf8' });
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[RssGenerator] saveFile - ERROR');
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
8
app/server/rss/template.xml
Normal file
8
app/server/rss/template.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<rss version="2.0">
|
||||||
|
<channel>
|
||||||
|
<title>$TITLE</title>
|
||||||
|
<description>$DESCRIPTION</description>
|
||||||
|
<link>$LINK</link>
|
||||||
|
<channel>
|
||||||
|
</rss>
|
||||||
@@ -2,7 +2,7 @@ extends layout
|
|||||||
|
|
||||||
block content
|
block content
|
||||||
h1 Conclave
|
h1 Conclave
|
||||||
form(method="POST" action="/conclave/new")
|
form(method="POST" action="/conclave/posts/new")
|
||||||
input(type="text" name="title" placeholder="Title")
|
input(type="text" name="title" placeholder="Title")
|
||||||
textarea(name="content")
|
textarea(name="content")
|
||||||
button(type="submit") Publish
|
button(type="submit") Publish
|
||||||
6
app/server/views/conclave/admin_panel.pug
Normal file
6
app/server/views/conclave/admin_panel.pug
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
extends ../layout
|
||||||
|
|
||||||
|
block content
|
||||||
|
nav
|
||||||
|
a(href="/conclave/new_post") New post
|
||||||
|
a(href="/conclave/options") Options
|
||||||
11
app/server/views/conclave/options.pug
Normal file
11
app/server/views/conclave/options.pug
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
extends ../layout
|
||||||
|
|
||||||
|
block content
|
||||||
|
a(href='/conclave/options/edit') Edit
|
||||||
|
case options.length
|
||||||
|
when 0
|
||||||
|
p There is no options at the moment
|
||||||
|
default
|
||||||
|
ul
|
||||||
|
each option in options
|
||||||
|
li #{option.name}: #{option.value}
|
||||||
16
app/server/views/conclave/options_edit.pug
Normal file
16
app/server/views/conclave/options_edit.pug
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
extends ../layout
|
||||||
|
|
||||||
|
block content
|
||||||
|
form(method="POST" action="/conclave/options/edit")
|
||||||
|
each option in options
|
||||||
|
fieldset(id=option.name)
|
||||||
|
input(value=option.name name="name" readonly type="text")
|
||||||
|
input(value="update" name="action" readonly type="hidden")
|
||||||
|
input(value=option.value name="value" type="text")
|
||||||
|
|
||||||
|
fieldset(id="new-one")
|
||||||
|
input(value="" name="name" type="text")
|
||||||
|
input(value="create" name="action" readonly type="hidden")
|
||||||
|
input(value="" name="value" type="text")
|
||||||
|
|
||||||
|
input(type="submit" value="Save")
|
||||||
@@ -2,8 +2,9 @@ doctype html
|
|||||||
html
|
html
|
||||||
head
|
head
|
||||||
title= title
|
title= title
|
||||||
link(rel='stylesheet', href='/stylesheets/style.css')
|
link(type='text/css' rel='stylesheet', href='/stylesheets/style.css')
|
||||||
block head
|
block head
|
||||||
|
link(rel="icon", type="image/x-icon", href="/favicon.ico")
|
||||||
body
|
body
|
||||||
header
|
header
|
||||||
h1
|
h1
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
extends ../layout
|
extends ../layout
|
||||||
|
|
||||||
block head
|
block head
|
||||||
script(defer src="https://unpkg.com/@simplewebauthn/browser/dist/bundle/index.umd.min.js")
|
script(defer type='text/javascript' src="https://unpkg.com/@simplewebauthn/browser/dist/bundle/index.umd.min.js")
|
||||||
script(defer src="/javascripts/users.js")
|
script(defer type='text/javascript' src="/javascripts/users.js")
|
||||||
|
|
||||||
block content
|
block content
|
||||||
h2 Register new user
|
h2 Register new user
|
||||||
70
compose.yaml
Normal file
70
compose.yaml
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: node:krypton-alpine
|
||||||
|
restart: no
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
|
working_dir: /home/node/app
|
||||||
|
volumes:
|
||||||
|
- ./app:/home/node/app
|
||||||
|
- rss-data:/home/node/rss
|
||||||
|
environment:
|
||||||
|
DB_USER: myuser
|
||||||
|
DB_PASSWORD: example
|
||||||
|
DB_DATABASE: pathtoglory
|
||||||
|
DB_HOST: db
|
||||||
|
RSS_FILE_PATH: /home/node/rss
|
||||||
|
develop:
|
||||||
|
watch:
|
||||||
|
- path: ./app/server
|
||||||
|
action: sync+restart
|
||||||
|
target: /home/node/app/server
|
||||||
|
command: node --env-file=.env ./index.js
|
||||||
|
networks:
|
||||||
|
- app-network
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgres
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
POSTGRES_PASSWORD: example
|
||||||
|
POSTGRES_USER: myuser
|
||||||
|
POSTGRES_DB: pathtoglory
|
||||||
|
volumes:
|
||||||
|
- ./postgres:/var/lib/postgresql
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
|
||||||
|
interval: 1s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
|
networks:
|
||||||
|
- app-network
|
||||||
|
|
||||||
|
nginx:
|
||||||
|
image: nginx
|
||||||
|
ports:
|
||||||
|
- "8080:80"
|
||||||
|
- "443:443"
|
||||||
|
volumes:
|
||||||
|
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
|
- ./nginx/ssl:/etc/nginx/ssl:ro
|
||||||
|
- ./app/public/:/usr/share/nginx/html:ro
|
||||||
|
- rss-data:/usr/share/nginx/rss:ro
|
||||||
|
develop:
|
||||||
|
watch:
|
||||||
|
- path: ./app/public
|
||||||
|
action: sync+restart
|
||||||
|
target: /usr/share/nginx/html
|
||||||
|
depends_on:
|
||||||
|
- app
|
||||||
|
networks:
|
||||||
|
- app-network
|
||||||
|
|
||||||
|
networks:
|
||||||
|
app-network:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
rss-data:
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
export default class Post {
|
|
||||||
id;
|
|
||||||
title;
|
|
||||||
content;
|
|
||||||
created_at;
|
|
||||||
updated_at;
|
|
||||||
|
|
||||||
constructor(data) {
|
|
||||||
this.title = data.title;
|
|
||||||
this.content = data.content;
|
|
||||||
|
|
||||||
this.id = data.id || undefined;
|
|
||||||
this.created_at = data.created_at || undefined;
|
|
||||||
this.updated_at = data.updated_at || undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
-----BEGIN PRIVATE KEY-----
|
|
||||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCiYoJCZdHcVH/w
|
|
||||||
GFQZUhY59jxiJ6GlKbo2cu7ozp34WYyIKun5cPXyWxWUtY0yuf6jUd8+JtL/T0uo
|
|
||||||
6qBMdnznE8uP0Ol7XmiozZ156celbbhO4VFUDu0wesXGktAPQllmIF7tB3zUhb2R
|
|
||||||
6PUao8lzn/Um6kLD7FFriTqOJL4X2d9cpAVpvTmjl5YqNRwiPQPOhPH1yyOv6iin
|
|
||||||
aRjklk00krWlp1Ezd4gitTqzMdGc72cUIVvC3JWLFUUHG4Xjkl7va4FPWCz6P7Di
|
|
||||||
kq3gadnOE+sWuQNNyVEjnU4hwTUZ8AYEVM23Siq2+7IkKu/oiX+zeBCfZhLNgilf
|
|
||||||
eTMxxUyhAgMBAAECggEAReo1/VKLhdrX7s76vqAqM7CCFRzNKyiJJVJc7N2xBDHC
|
|
||||||
IQqhDKYHLt7qrslwTsvoB/eDL+ZVaFmC0OqcM++8HV3XgkdHj7d5RlypFcmDDQXt
|
|
||||||
mgDHHHMEyp/BsZqafEdr6F29oT5dD7+5fC4aAetNHDxdt/Ca6HJCKBPAo1zMf8W+
|
|
||||||
HXepvb3eTTIDG+msclY9b6gCPxuJU3rM+JzkohwClQJJe40lBu4qyMeiGYynjHY2
|
|
||||||
/98M21dja02RFBP66S+S9UaPQ5/h1LRMz2vI+njX3qgAWjdTsazoa0jzZsqbIl/u
|
|
||||||
6NnGlScIGDAQaBpkb2YhhCJxR7Q3f9PGd2leGo1zsQKBgQDITbw05KaKU1hZ0R+0
|
|
||||||
196rurSUDbsFmamwFouzRWSwTCMwJlGcoMQ0mChfwwWKAXiNJRlMf0biywIGZisD
|
|
||||||
CApjgpRNuOdIqy3rU4iDVxwj45XNNgobfF1ZKAHum5q9KjIchUZsI8htC7KgH0S6
|
|
||||||
zMsPbxk56ueKGXF6kyZpoW4Z1QKBgQDPiZYkPyk153J72O+V+kwOTOdGoBMETXdY
|
|
||||||
wPf8gd/j5oNwuQV7DWxMyq4ku4uc9fJn9rAxymHlbFqWce8h2jYVPOfak0Om6N2z
|
|
||||||
IDjlLKWzvjzpiUNZ8QsODfm4rFYni3sEhC0K3IkDzEDtSr3UakL66Uqjsqg4cn7S
|
|
||||||
Ik4u0r8hnQKBgQCq+l/7LmpSjQ5PrMjJz7LNGBRohMft4dsM6lHZdxSZwIQQ58Sm
|
|
||||||
VDznQDLGe2xQ/yxuHwrXV5WkpfFWkQOKFOT5SE9bgMg8KZKK28Udh9AHeo82mjhK
|
|
||||||
egAcyJ/Nk5mke05HNiSEzo6ZNnEFaWt7oLB8vjLkU3XNViadoNobNKcM+QKBgAku
|
|
||||||
KeESli0XPt4xm2+D8edUCYr7O7wd/SCE8LNPv2qiYMAUvyRRVLAU6x0e2q8nxgBJ
|
|
||||||
TkP1kt0GLP+orI5Py8Kmvg7SItT4Sg5JZ5rjnbTUvncKJluNKRMHFTvRC8KWDewG
|
|
||||||
OMPZO4pad6jHfJwv0ySsOywAlCZjEi8Ta2fw1JmVAoGASSUtaNsjsTJ5ikRXPZTQ
|
|
||||||
MS0Vb+leH5+M9umbV4sxqsY4oz6l3Dv7fEdigJmrrBTKlH5uRKFxqE91FcOYbyH4
|
|
||||||
C9HyzJgiujvfWoFqF5H0EoIpsHvtydGMr+JkHbwDSZfeRelFnUEFLhTQSu/fyHeW
|
|
||||||
FX/eT/HtOo4D1NKZ5UDG1O8=
|
|
||||||
-----END PRIVATE KEY-----
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIID/jCCAmagAwIBAgIRAIckhgeUs09k09IblD8x28EwDQYJKoZIhvcNAQELBQAw
|
|
||||||
VTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMRUwEwYDVQQLDAxhcnRo
|
|
||||||
dXJAYmVhY2gxHDAaBgNVBAMME21rY2VydCBhcnRodXJAYmVhY2gwHhcNMjYwMzA0
|
|
||||||
MTY0OTQwWhcNMjgwNjA0MTU0OTQwWjBAMScwJQYDVQQKEx5ta2NlcnQgZGV2ZWxv
|
|
||||||
cG1lbnQgY2VydGlmaWNhdGUxFTATBgNVBAsMDGFydGh1ckBiZWFjaDCCASIwDQYJ
|
|
||||||
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKJigkJl0dxUf/AYVBlSFjn2PGInoaUp
|
|
||||||
ujZy7ujOnfhZjIgq6flw9fJbFZS1jTK5/qNR3z4m0v9PS6jqoEx2fOcTy4/Q6Xte
|
|
||||||
aKjNnXnpx6VtuE7hUVQO7TB6xcaS0A9CWWYgXu0HfNSFvZHo9RqjyXOf9SbqQsPs
|
|
||||||
UWuJOo4kvhfZ31ykBWm9OaOXlio1HCI9A86E8fXLI6/qKKdpGOSWTTSStaWnUTN3
|
|
||||||
iCK1OrMx0ZzvZxQhW8LclYsVRQcbheOSXu9rgU9YLPo/sOKSreBp2c4T6xa5A03J
|
|
||||||
USOdTiHBNRnwBgRUzbdKKrb7siQq7+iJf7N4EJ9mEs2CKV95MzHFTKECAwEAAaNe
|
|
||||||
MFwwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMB8GA1UdIwQY
|
|
||||||
MBaAFNLXA4V6orI5Z7VVA/5i5ga0AjShMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDAN
|
|
||||||
BgkqhkiG9w0BAQsFAAOCAYEAqJ/+hvJ/xFQ7GBzPteLNcfBtfokSJ5siynMokU3b
|
|
||||||
mOoKl+jR9r+4/X5aaSe5sOXAaE0kB3dliYxF9J4FMa5y4E5yluYAdt6vFYOI2u4/
|
|
||||||
zOuo1VStMhkStXc8h41jd2jfApoiZaicQYv8LwdJ34oHNq+phbUmxxggFLSd2tsD
|
|
||||||
oaVKUjMe15QvI8HsB+czmpPJUMO8u9ajHUxJZOKyYvTOjdefGMpjEhUpBwOCcFxd
|
|
||||||
TTybDD/+FmV3h/0m1U0CVrK+TN+hGje97DNK+bV7OxYL/RhoCFm99TOdKJxmV78X
|
|
||||||
AEIVb6360GVo7ySWxsFRXHu3vzBBoDPwWIieEksqCyfA7jhkaCKRVWZLmwUl4lrg
|
|
||||||
wGBYJCzibjFZihlgslGAavxBk1cL2Eu5KANkp+ybmP4assbzoXrTZGfY3Va10q3r
|
|
||||||
5u8oovVVTcVlthVGjpP90qSBogwdlDjtDYFq8PpTyIcqJQTneJ/F4KDf9gawOSqF
|
|
||||||
1KEt58J/uzK159CrXzJVIXJq
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
64
nginx/nginx.conf
Normal file
64
nginx/nginx.conf
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
# Upstream definitions for load balancing
|
||||||
|
upstream app_servers {
|
||||||
|
server app:3000;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Redirect HTTP to HTTPS
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name localhost;
|
||||||
|
return 301 https://$server_name$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
# HTTPS server
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
http3 on;
|
||||||
|
server_name localhost;
|
||||||
|
|
||||||
|
# SSL certificates
|
||||||
|
ssl_certificate /etc/nginx/ssl/nginx.crt;
|
||||||
|
ssl_certificate_key /etc/nginx/ssl/nginx.key;
|
||||||
|
|
||||||
|
# SSL settings
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers ECDHE-ECDSA-AES129-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
|
||||||
|
ssl_prefer_server_ciphers off;
|
||||||
|
ssl_session_cache shared:SSL:11m;
|
||||||
|
ssl_session_timeout 2d;
|
||||||
|
|
||||||
|
# Security headers
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000" always;
|
||||||
|
add_header X-Content-Type-Options nosniff;
|
||||||
|
add_header X-Frame-Options DENY;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
sendfile on;
|
||||||
|
sendfile_max_chunk 2m;
|
||||||
|
|
||||||
|
try_files $uri @app;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /rss.xml {
|
||||||
|
root /usr/share/nginx/rss;
|
||||||
|
}
|
||||||
|
|
||||||
|
location @app {
|
||||||
|
proxy_pass http://app_servers;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
nginx/ssl/nginx.crt
Normal file
21
nginx/ssl/nginx.crt
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDfTCCAmWgAwIBAgIUWsvMHfQcp9IAbwDFqxSmMnhMi0gwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwTjELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5
|
||||||
|
MQwwCgYDVQQKDANPcmcxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0yNjAzMTMxMjMx
|
||||||
|
NTlaFw0yNzAzMTMxMjMxNTlaME4xCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVTdGF0
|
||||||
|
ZTENMAsGA1UEBwwEQ2l0eTEMMAoGA1UECgwDT3JnMRIwEAYDVQQDDAlsb2NhbGhv
|
||||||
|
c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCs/E7zl7jje4rKxjBa
|
||||||
|
HA908CFWepFU7Q0i7JzhbIigbEdEX/7SqFb2Gl9ijaoccu5fcvuBeStzF9z4ie+i
|
||||||
|
rxaILtDz76HNxXCRPLKbUe8yKKQwBE2FedCA6FUApp9tLrZv050GHCXa2/rS5SwJ
|
||||||
|
5ZXirxWSLclNmI5JtPqTqFgMGZsLyUfhD4FNv/QbOgB7IDNwjLIC3Z+jv8kAdaUO
|
||||||
|
eG+LlZUxH9Tz0UrTmmTzLNp2Ky7O8nEQaRgDpnpTvMgyb7IF8x/u9mQnVIP+5hEJ
|
||||||
|
JF/PHfPqbF3lmwURypaBcAky3MBqvMwGYLrKGHdEn447vNJlxZ1my6tGXjNhqa3o
|
||||||
|
OuSXAgMBAAGjUzBRMB0GA1UdDgQWBBQfynYNBWR+i3PaJA+jNVrJUbJmhTAfBgNV
|
||||||
|
HSMEGDAWgBQfynYNBWR+i3PaJA+jNVrJUbJmhTAPBgNVHRMBAf8EBTADAQH/MA0G
|
||||||
|
CSqGSIb3DQEBCwUAA4IBAQBMfm5g92DDca2CfXTeNw5+vOC0xqomX6IA7cJiCA8m
|
||||||
|
IKOcisQZBQCduS77/0WfeQAwARfszdj4XgPo8poZwPWuy4WoPpCcDT82wwLm/oIg
|
||||||
|
J1z/JcATSTOzDUs2+cVOHcfKx13vvZustumUwMsP2Gy7lqU+SgXH+48HarmhWvxd
|
||||||
|
5W0tg+W2QIr/YCEwahC1/11zKtcZPfHlQhk2ZfbttpXMcHXJqzQaThd53KPUT8FD
|
||||||
|
dj5UpFenMsd+b5xjkl+K98EsetpuxCm92VcuXt1UBaClokkE1/ccK+8din1O4fgw
|
||||||
|
AG8aKlE2FfTNuhcjulHd6PkIcxuecCj33WRDHlxOtDoO
|
||||||
|
-----END CERTIFICATE-----
|
||||||
28
nginx/ssl/nginx.key
Normal file
28
nginx/ssl/nginx.key
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCs/E7zl7jje4rK
|
||||||
|
xjBaHA908CFWepFU7Q0i7JzhbIigbEdEX/7SqFb2Gl9ijaoccu5fcvuBeStzF9z4
|
||||||
|
ie+irxaILtDz76HNxXCRPLKbUe8yKKQwBE2FedCA6FUApp9tLrZv050GHCXa2/rS
|
||||||
|
5SwJ5ZXirxWSLclNmI5JtPqTqFgMGZsLyUfhD4FNv/QbOgB7IDNwjLIC3Z+jv8kA
|
||||||
|
daUOeG+LlZUxH9Tz0UrTmmTzLNp2Ky7O8nEQaRgDpnpTvMgyb7IF8x/u9mQnVIP+
|
||||||
|
5hEJJF/PHfPqbF3lmwURypaBcAky3MBqvMwGYLrKGHdEn447vNJlxZ1my6tGXjNh
|
||||||
|
qa3oOuSXAgMBAAECggEANnKkFyAmXdPA99ox1esHInOCysmBfQ2wGNII/xs+VeA+
|
||||||
|
/lKIzp2V4ZijdU3JwXKbfQegCjytZGiacNBZ0QbzPKIfuuguMap3p/9G6IR9mXgZ
|
||||||
|
VdPE4sJY0npOZwoLHD6bxc8Bma66a4zaAg+IMQVGjDc3vu8ScY4XGzQPuvI95HCW
|
||||||
|
Dp8fMnPH/CTCORv2rT6qheHUoSLGqH5/zNxhJesKGA9XfA98ZZlQPLXm5swg++20
|
||||||
|
jDkk5OSBiNiPCWwINvEBq7jq9xw5/BPnEdSI4HPcYkeSAkxEkFnatfhv0mBP674V
|
||||||
|
aDgTT8b7RYwezTcJtVCsOAhaouvjTqi9vgHao76a4QKBgQDgq9KHRN5VoYO6fa0t
|
||||||
|
OBNzKJvwNJWOoK6Ytp93ci6OpdDEcmF+YBD2and0I2sXGHy4tO8me3E2snL+Fn2c
|
||||||
|
PJjxnWTktAwX2Lfob37gOFjfyPFDyHZUvj8x81u72IiZf4RkYzTBPKcljG4WwnXp
|
||||||
|
CFOXa4VhxOSxyipU0dvUH5aEDQKBgQDFG3G9qWjJx/m53YKzefxsCHHkd90K3ZLZ
|
||||||
|
E6BK3s440sm8TOEU1rI1dYUvf/Idz8X8Qg1nf1theWJyiNypw5Y12cFUGZ2wIcMj
|
||||||
|
zc6cB0q4Y9kjYGGsEZX3W/L6GPFWCQz9YnSwGGC8pkRo1ORPUSqQZsJ1X8xjiXTL
|
||||||
|
kI3BfUVuMwKBgQCq/jraFvE8rRPL4RDnpJExCUw9slfNWL/pnLybtTx39v2ANim7
|
||||||
|
fDQMx5UowDgmOM3b6mFFoZfV8FUSy11IhtpEsezzH5FAqQG3lvwyfNAvagkt9KkS
|
||||||
|
8ES6qKQ9wJyebu39VX/2PkpcipfTHKeO93ZffUbj1W3VXq9/JMWxmWT+FQKBgCq6
|
||||||
|
g1KpKeUsK6+mHFJJ7HWC8yWi8lELrWzPe10fpbbdwwvF4rlAvYsnOcMNSnA5/WJs
|
||||||
|
8zjve+DKjpPjpWpL0wFDfkx77Rokh4f4coSSdBk60/R+TU4e1dK0pRygUZjkB2Pa
|
||||||
|
dw9BiZ77sDLFi+TB5Kc/D4KgTM97Gc2TPZenT2jPAoGBAJYrcDoj0WmszjmtB66I
|
||||||
|
m4RHO55ZNovh1lqjaNisLzJZ2FhRJY/1+rv7vCauw4UVNpsoO8s8owkW8muo+afC
|
||||||
|
m3Yyzb80mlnL0xFTG6ckjY8TjXXL1zxHR48xPhtvQaFzlM8GgMxa1XvCRosTquM7
|
||||||
|
zEs6JfMU65Z5QivvotGBXeKl
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import express from 'express';
|
|
||||||
var router = express.Router();
|
|
||||||
import PostController from '../controllers/Post.controller.js';
|
|
||||||
import authenticateToken from '../middlewares/authentication.js';
|
|
||||||
|
|
||||||
/* GET home page. */
|
|
||||||
router.get('/', async function(req, res, next) {
|
|
||||||
const posts = await PostController.getAll();
|
|
||||||
res.render('index', { title: 'Path to glory', posts: posts });
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/conclave', authenticateToken, async function(req, res, next) {
|
|
||||||
res.render('conclave');
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post('/conclave/new', authenticateToken, async function(req, res, next) {
|
|
||||||
console.log(req.body);
|
|
||||||
await PostController.create(req.body.title, req.body.content);
|
|
||||||
res.redirect('/conclave');
|
|
||||||
});
|
|
||||||
export default router;
|
|
||||||
Reference in New Issue
Block a user