Initial commit

This commit is contained in:
2021-12-18 02:34:52 +02:00
parent 3d2768a68b
commit 3a51a595a5
20 changed files with 25261 additions and 0 deletions

23
config/default.json Normal file
View File

@@ -0,0 +1,23 @@
{
"database" : {
"dialect" : "sqlite",
"host" : "localhost",
"database" : "databases",
"username" : "databases",
"password" : "database",
"storage" : "database.sqlite",
"define": {
"paranoid": true
},
"logging": false
},
"server" : {
"secret" : "don't forget to keep your secrets secret",
"port" : 7878,
"cookie": {
// "domain": "mantra.exonumia.africa"
},
"compiled-render": false,
"domain": "https://mantra.exonumia.africa"
}
}

14
config/test.json Normal file
View File

@@ -0,0 +1,14 @@
{
"database" : {
"dialect" : "sqlite",
"host" : "localhost",
"database" : "databases",
"username" : "databases",
"password" : "database",
"storage" : "database.sqlite",
"define": {
"paranoid": true
},
"logging": false
}
}

12
mantra.exonumia.africa.js Normal file
View File

@@ -0,0 +1,12 @@
const db = require('mantra-db-models');
const server = require('./server/server.js')();
db.init().then(() => {
console.log("Loaded DB.");
return server.run(db);
}).then((port) => {
console.log("Server is running on: ", port);
})
.catch((error) => {
console.error("Failed to run server: ", error);
})

3201
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

33
package.json Normal file
View File

@@ -0,0 +1,33 @@
{
"name": "mantra",
"version": "0.0.1",
"description": "Community platform for translating literature.",
"main": "mantra.exonumia.africa.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/exonumia-cc/mantra.exonumia.africa.git"
},
"author": "kngako",
"license": "ISC",
"bugs": {
"url": "https://github.com/exonumia-cc/mantra.exonumia.africa/issues"
},
"homepage": "https://github.com/exonumia-cc/mantra.exonumia.africa#readme",
"devDependencies": {
"sqlite3": "npm:@vscode/sqlite3@^5.0.7"
},
"dependencies": {
"body-parser": "^1.19.1",
"config": "^3.3.6",
"connect-session-sequelize": "^7.1.2",
"express": "^4.17.2",
"express-session": "^1.17.2",
"mantra-db-models": "file:../mantra-db-models",
"passport": "^0.5.2",
"passport-local": "^1.0.0",
"pug": "^3.0.2"
}
}

21
server/router/index.js Normal file
View File

@@ -0,0 +1,21 @@
/**
* This router handles things related to the web browser experience...
*/
const fetch = require("node-fetch");
module.exports = function (options) {
var express = options.express;
var db = options.db;
var router = express.Router();
router.route('/')
.get(function(request, response, next) {
response.display("home", {
user: request.user,
pageTitle: "Home - Mantra"
})
});
return router;
};

268
server/server.js Normal file
View File

@@ -0,0 +1,268 @@
const config = require('config');
const express = require('express');
const pug = require("pug");
const session = require("express-session");
const SequelizeSessionStore = require('connect-session-sequelize')(session.Store);
const bodyParser = require("body-parser");
const path = require("path");
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const fs = require('fs');
module.exports = function () {
var server = {};
server.run = (db) => {
console.log("Starting Server")
return new Promise((resolve, reject) => {
const app = express();
app.set("view engine", "pug");
app.set("views", path.resolve("server/views"));
// compile pug views below so that they are rendered quicker
var pugPages = {};
var modelDir = path.join(__dirname, './views');
fs.readdirSync(modelDir)
.filter(file => {
return file.endsWith(".pug");
})
.forEach(file => {
const name = file.split(".pug")[0];
pugPages[name] = pug.compileFile(path.join(modelDir, file))
});
// Set up middleware to make use of the compiled pugjs views
app.use((request, response, next) => {
// This is cheating...
response.display = (view, options) => {
options.domain = config.get("server.domain");
options.urlEndpoint = request.originalUrl;
// Set a default pageTitle...
options.pageTitle = options.pageTitle ? options.pageTitle : "Mantra";
options.errorMessage = (error) => {
// TODO: Make error human readable...
return error.message;
}
if(config.get("server.compiled-render")) {
if(pugPages[view]) {
response.send(pugPages[view](options))
} else {
response.send(pugPages["404"](options))
}
} else {
response.render(view, options);
}
}
next();
})
app.use('/static', express.static("server/static"));
// Session related stuff...
function extendDefaultFields(defaults, session) {
return {
data: defaults.data,
expires: session.cookie && session.cookie.expires ? session.cookie.expires : defaults.expires, // 157680000
userId: session.passport.user
};
}
var sessionStore = new SequelizeSessionStore({
db: db.sequelize,
table: 'session',
extendDefaultFields: extendDefaultFields
// TODO: Define expiry and clean up...
});
app.use(session({
store: sessionStore,
secret: config.get("server.secret"),
resave: false,
saveUninitialized: false,
cookie: config.get("server.cookie")
}));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
passport.use(new LocalStrategy(
function(username, password, done) {
username = username != null ? username.trim().toLowerCase() : "";
password = password != null ? password : "";
return db.User.findOne({
include: [
{
association: db.User.Passwords
},
{
association: db.User.UserEmails,
required: true,
where: {
emailAddress: {
[db.Sequelize.Op.like]: username
}
}
},
{
association: db.User.UserRoles,
// required: true,
include: [
{
association: db.UserRole.Role,
required: true,
where: {
type: {
[db.Sequelize.Op.or]: roles
}
}
}
]
}
]
}).then(
user => {
// Validate Password
if(user) {
if (!user.passwords[0].validPassword(password)) {
return done(null, false, {
username: username,
message: 'Invalid username or password.'
});
} else {
return done(null, user);
}
} else {
console.error("User Doesn't Exist: ", username);
return done(null, false, {
username: username,
message: 'Invalid username or password.'
});
}
},
err => {
console.error("Authentication Failed: " + err);
return done(null, false, { message: 'Incorrect username or password.' });
}
).catch(error => {
console.log("Passport Error: ", error);
return done(error);
})
}
));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
// TODO: Add memberships and things like that...
return db.User.findByPk(id, {
include: [
{
association: db.User.UserRoles,
// required: true,
include: [
{
association: db.UserRole.Role,
required: true,
where: {
type: {
[db.Sequelize.Op.or]: roles
}
}
}
]
}
]
})
.then(user => {
return done(null, user);
}).catch(error => {
return done(error);
})
});
app.use(passport.initialize());
app.use(passport.session());
sessionStore.sync();
// TODO: Create a load router module...
var router = require('./router/index.js')({
app: app,
express: express,
db: db,
passport: passport
});
app.use('/', router);
// 404 pages...
app.use((request, response, next) => {
if(!response.headersSent) {
response.status(404);
if (request.accepts('html')) {
response.display("404", {
user: request.user,
pageTitle: "Page Not Found - Mantra"
})
return;
}
if (request.accepts('application/json')) {
response.json({
message: "URL not accessible"
});
return;
}
response.type('txt').send('Resource Not found');
}
});
// 500 pages...
app.use((error, request, response, next) => {
console.error(`${response.getHeader("id")} - `, error);
if(error && !response.headersSent) {
response.status(500);
// TODO: Check if production and then show stack...
if (request.accepts('html')) {
response.display("error", {
user: request.user,
pageTitle: "Internal Error - Mantra",
error: error
})
return;
}
if (request.xhr) {
response.json({
message: "Internal Error, Please check your tires.",
error: error
});
return;
}
response.type('txt').send('Internal server error');
}
});
const port = process.env.PORT || config.get("server.port");
app.listen(port);
resolve(port);
});
}
return server;
};

9067
server/static/css/materialize.css vendored Normal file

File diff suppressed because it is too large Load Diff

13
server/static/css/materialize.min.css vendored Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

12374
server/static/js/materialize.js vendored Normal file

File diff suppressed because it is too large Load Diff

6
server/static/js/materialize.min.js vendored Normal file

File diff suppressed because one or more lines are too long

6
server/views/404.pug Normal file
View File

@@ -0,0 +1,6 @@
extend templates/layout.pug
block content
.container
.center
h1 404: Page Not Found

View File

@@ -0,0 +1,20 @@
body {
display: flex;
width: 100%;
min-height: 100vh;
flex-direction: column;
}
main {
flex: 1 0 auto;
}
header, main, footer {
padding-left: 300px;
}
@media only screen and (max-width : 992px) {
header, main, footer {
padding-left: 0;
}
}

47
server/views/css/site.css Normal file
View File

@@ -0,0 +1,47 @@
/* Color scheme from https://material.io/resources/color/#!/?view.left=0&view.right=0&primary.color=212121&secondary.color=263238 */
.primary-color {
background-color: #212121 !important;
}
.primary-color-light {
background-color: #484848 !important;
}
.primary-color-dark {
background-color: #000000 !important;
}
.secondary-color {
background-color: #263238 !important;
}
.secondary-color-light {
background-color: #4f5b62 !important;
}
.secondary-color-dark {
background-color: #000a12 !important;
}
.money-chip {
display: inline-block;
/* height: 33px;
font-size: 1;
font-weight: 500; */
line-height: 32px;
padding: 12px;
border-radius: 16px;
}
.auto-image {
width: auto !important;
}
/* This gives use dots in our markdown lists... */
.flow-text ul li {
list-style-type: disc !important;
}
.bitcoin-address {
word-wrap: break-word;
}

6
server/views/error.pug Normal file
View File

@@ -0,0 +1,6 @@
extend templates/layout.pug
block content
.container
.center
h1 Internal Server Error

6
server/views/home.pug Normal file
View File

@@ -0,0 +1,6 @@
extend templates/layout.pug
block content
.container
.center
h1 Home

View File

@@ -0,0 +1,18 @@
ul.pagination
- var disableLeft = page<=0 ? "disabled" : "";
- var hrefLeft = page<=0 ? "#" : "?page="+(Number(page)-1);
li(class=disableLeft)
//- TODO: Compute the left value...
a(href=hrefLeft)
i.material-icons chevron_left
- var n = 0;
while n < totalPages
- var derivedClass = n == page ? "active" : ""
li(class=derivedClass)
a(href="?page="+n)= (++n)
- var disableRight = page>=(Number(totalPages)-1) ? "disabled" : "";
li(class=disableRight)
//- TODO: Compute the right value..
- var hrefRight = page>=(Number(totalPages)-1) ? "#" : "?page="+(Number(page)+1);
a(href=hrefRight)
i.material-icons chevron_right

4
server/views/js/site.js Normal file
View File

@@ -0,0 +1,4 @@
document.addEventListener('DOMContentLoaded', function() {
//
M.Sidenav.init(document.querySelectorAll('.sidenav'));
});

View File

@@ -0,0 +1,122 @@
doctype html
html(lang="en" dir="ltr")
head
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
<link async href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
link(rel="icon", href="/static/img/mantra-icon.png", type="image/png")
block seo
title Mantra
//- Using Material Design Lite for the CSS stuff
//- https://getmdl.io
//- <script src="https://code.getmdl.io/1.3.0/material.min.js"></script>
//- <link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.indigo-pink.min.css">
link(rel="stylesheet", href="/static/css/materialize.min.css")
style(media="screen")
include ../css/site-layout.css
style(media="screen")
include ../css/site.css
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
block additionalStyle
body
ul.sidenav.sidenav-fixed#slide-out
li
.user-view.primary-color-light
.background
a#name
p.flow-text.white-text.name Mantra
a#bio
span.white-text Mantra Mantra Mantra
a#email
span.white-text.email mantra@exonumia.africa
if user
li
.divider
li
a(href="/account")
i.material-icons.black-text person
span account
li
a(href="/account/tokens")
i.material-icons.black-text vpn_key
span tokens
li
form.center(action="/account/logout" method="POST")
<button class="btn-flat" type="submit">logout</button>
//- li.red-text
a DELETE ACCOUNT
else
li
a(href="/account/authenticate") login/register
//- li
a(href="/login/hd-auth") login with hd-auth
header(role="banner")
block navigation
nav.primary-color
.nav-wrapper
a.brand-logo.center.hide-on-small-only(href="/") Mantra
ul.left
li
a.sidenav-trigger(href="#", data-target="slide-out")
i.material-icons menu
main(role="main")
.row
.col.s12
//- I just need the space at the top of the page to be good on mobile...
block content
.container
p.flow-text Center content
.row
.col.s12
//- I just need some space at the bottom...
block footer
footer.primary-color.page-footer(role="footer")
.container
.row
.col.s12.m6
p.flow-text Follow
ul
li
a.white-text(href="https://www.twitter.com/exonumia280") Twitter
li
a.white-text(href="https://www.facebook.com/exonumia.africa") Facebook
li
a.white-text(href="https://www.instagram.com/exonumia.africa") Instagram
li
a.white-text(href="https://www.linkedin.com/company/exonumia-africa") LinkedIn
.col.s12.m6
//- p.flow-text Links
//- ul
li
a.white-text(href="/faq") FAQ
li
a.white-text(href="/privacy") Privacy Policy
li
a.white-text(href="/compliance") Compliance
li
a.white-text(href="/terms") Terms
li
a.white-text(href="/contact") Contact
.footer-copyright.primary-color-dark
.container
span &copy; 2021
a(href="https://sigidli.com/")
strong Sigidli
script(src="/static/js/materialize.min.js")
script
include ../js/site.js
block additionalScripts
//- All other scripts will be added here as they are needed in the views