Initial commit
This commit is contained in:
commit
1f4e759225
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "gulp"]
|
||||
path = gulp
|
||||
url = https://github.com/thebitmill/gulp
|
||||
74
bin/initDb.js
Executable file
74
bin/initDb.js
Executable file
@ -0,0 +1,74 @@
|
||||
#!/bin/env node
|
||||
|
||||
'use strict';
|
||||
|
||||
const pg = require('../server/db');
|
||||
|
||||
// pg.schema.dropTableIfExists('transactions')
|
||||
pg.raw('DROP TABLE IF EXISTS companies CASCADE')
|
||||
.then(() => pg.raw('DROP TABLE IF EXISTS years CASCADE'))
|
||||
.then(() => pg.raw('DROP TABLE IF EXISTS accounts CASCADE'))
|
||||
.then(() => pg.raw('DROP TABLE IF EXISTS account_types CASCADE'))
|
||||
.then(() => pg.raw('DROP TABLE IF EXISTS tickets CASCADE'))
|
||||
.then(() => pg.raw('DROP TABLE IF EXISTS ticket_types CASCADE'))
|
||||
.then(() => pg.raw('DROP TABLE IF EXISTS transactions CASCADE'))
|
||||
.then(() => pg.raw('DROP TABLE IF EXISTS initial_balance CASCADE'))
|
||||
|
||||
.then(() => pg.schema.createTable('companies', (table) => {
|
||||
table.increments();
|
||||
table.text('name').notNullable().unique();
|
||||
table.text('organization_number').notNullable().unique();
|
||||
}))
|
||||
.then(() => pg.schema.createTable('years', (table) => {
|
||||
table.increments();
|
||||
table.integer('company_id').references('companies.id').notNullable().onUpdate('cascade');
|
||||
table.integer('previous_year_id').references('id');
|
||||
table.date('start_date').notNullable();
|
||||
table.date('end_date').notNullable();
|
||||
table.text('name');
|
||||
}))
|
||||
.then(() => pg.schema.createTable('account_types', (table) => {
|
||||
table.increments();
|
||||
table.text('name').notNullable().unique();
|
||||
}))
|
||||
.then(() => pg.schema.createTable('accounts', (table) => {
|
||||
table.increments();
|
||||
table.text('name').notNullable();
|
||||
table.integer('number').notNullable();
|
||||
table.integer('sru');
|
||||
table.integer('account_type_id').notNullable().references('account_types.id').onUpdate('cascade');
|
||||
}))
|
||||
.then(() => pg.schema.createTable('initial_balance', (table) => {
|
||||
table.increments();
|
||||
table.integer('year_id').notNullable().references('years.id').onUpdate('cascade');
|
||||
table.integer('account_id').notNullable().references('accounts.id').onUpdate('cascade');
|
||||
table.decimal('amount').notNullable();
|
||||
}))
|
||||
.then(() => pg.schema.createTable('ticket_types', (table) => {
|
||||
table.increments();
|
||||
table.integer('number').notNullable();
|
||||
table.text('name').notNullable().unique();
|
||||
}))
|
||||
.then(() => pg.schema.createTableIfNotExists('tickets', (table) => {
|
||||
table.increments();
|
||||
table.integer('type');
|
||||
table.integer('ticket_type_id').references('ticket_types.id').onUpdate('cascade');
|
||||
// table.integer('ticket_type_id').notNullable().references('ticket_types.id').onUpdate('cascade');
|
||||
table.integer('number').notNullable();
|
||||
table.date('date').notNullable();
|
||||
table.text('title');
|
||||
table.date('date_created').notNullable();
|
||||
table.timestamp('uploaded_at').defaultsTo(pg.fn.now());
|
||||
}))
|
||||
.then(() => pg.schema.createTableIfNotExists('transactions', (table) => {
|
||||
table.increments();
|
||||
table.integer('account_id').notNullable().references('accounts.id').onUpdate('cascade');
|
||||
table.decimal('amount', 14, 2).notNullable();
|
||||
table.integer('ticket_id').notNullable().references('tickets.id').onUpdate('cascade');
|
||||
}))
|
||||
.then(() => pg('account_types').insert([{ name: 'Tillgång' }, { name: 'Skuld' }, { name: 'Intäkt' }, { name: 'Kostnad' }]))
|
||||
.then(() => pg.destroy())
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
pg.destroy();
|
||||
});
|
||||
1
gulp
Submodule
1
gulp
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 1daad4b4629c3ddef1f60710a0f7f8a67af9d714
|
||||
1
gulpfile.js
Symbolic link
1
gulpfile.js
Symbolic link
@ -0,0 +1 @@
|
||||
gulp/gulpfile.js
|
||||
30
package.json
Normal file
30
package.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "vizwiz",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"main": "server/server.js",
|
||||
"scripts": {
|
||||
"gulp": "gulp",
|
||||
"gulp:production": "NODE_ENV=production gulp",
|
||||
"gulp:development": "NODE_ENV=development gulp",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@gitlab.bitmill.co:lohfu/vizwiz.git"
|
||||
},
|
||||
"author": "Linus Miller <lohfu@lohfu.io> (https://lohfu.io/)",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"body-parser": "^1.16.1",
|
||||
"busboy": "^0.2.14",
|
||||
"chalk": "^1.1.3",
|
||||
"express": "^4.14.1",
|
||||
"jsx-node": "^0.2.2",
|
||||
"knex": "^0.12.7",
|
||||
"midwest": "github:thebitmill/midwest",
|
||||
"morgan": "^1.8.1",
|
||||
"multer": "^1.3.0",
|
||||
"split": "^1.0.0"
|
||||
}
|
||||
}
|
||||
95
server/api/parse.js
Normal file
95
server/api/parse.js
Normal file
@ -0,0 +1,95 @@
|
||||
'use strict';
|
||||
|
||||
function parseAccount(str) {
|
||||
// str will look like: 'KONTO 1010 "Balanserade utgifter"'
|
||||
const [, number, ...name] = str.split(/\s+/);
|
||||
|
||||
return {
|
||||
number: parseInt(number, 10),
|
||||
name: name.join(' '),
|
||||
};
|
||||
}
|
||||
|
||||
const accountType = {
|
||||
T: 1,
|
||||
S: 2,
|
||||
I: 3,
|
||||
K: 4,
|
||||
};
|
||||
|
||||
function parseAccountType(str) {
|
||||
const [, number, type] = str.split(/\s+/);
|
||||
|
||||
return {
|
||||
number: parseInt(number, 10),
|
||||
type: accountType[type],
|
||||
};
|
||||
}
|
||||
|
||||
function parseDate(str) {
|
||||
const [, year, month, day] = /(\d\d\d\d)(\d\d)(\d\d)/.exec(str);
|
||||
|
||||
return new Date(`${year}-${month}-${day}T00:00:00.000Z`);
|
||||
}
|
||||
|
||||
function parseTransaction(str) {
|
||||
// str looks like: ' #TRANS 6310 {} 1541.00 20150824 '
|
||||
const [,, account,, amount, date] = str.split(/\s+/);
|
||||
|
||||
return {
|
||||
account: parseInt(account, 10),
|
||||
amount: parseFloat(amount),
|
||||
date: parseDate(date),
|
||||
};
|
||||
}
|
||||
|
||||
function parseTicket(str) {
|
||||
const arr = str.split('\r\n');
|
||||
// arr[0] will look like: 'VER 5101 147 20150917 "Binero" 20160224'
|
||||
const [, type, number, date, ...rest] = arr[0].split(/\s+/);
|
||||
const dateCreated = rest.pop();
|
||||
const title = rest.join(' ');
|
||||
|
||||
console.log(type);
|
||||
const transactions = arr.slice(2, -2).map(parseTransaction);
|
||||
|
||||
return {
|
||||
type: parseInt(type, 10),
|
||||
number: parseInt(number, 10),
|
||||
date: parseDate(date),
|
||||
dateCreated: parseDate(date),
|
||||
title,
|
||||
transactions,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// console.log(string.split('\n#').length)
|
||||
|
||||
module.exports = function parse(string) {
|
||||
const accounts = [];
|
||||
const tickets = [];
|
||||
|
||||
string.split(/\n#/g).forEach((str, i) => {
|
||||
if (str.startsWith('KONTO')) {
|
||||
accounts.push(parseAccount(str));
|
||||
} else if (str.startsWith('KTYP')) {
|
||||
const { number, type } = parseAccountType(str);
|
||||
|
||||
// const string = fs.readFileSync('./bitmill2015.se', { encoding: 'UTF8' });
|
||||
const account = accounts.find((account) => account.number === number);
|
||||
|
||||
account.type = type;
|
||||
} else if (str.startsWith('VER')) {
|
||||
tickets.push(parseTicket(str));
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
accounts,
|
||||
tickets,
|
||||
};
|
||||
};
|
||||
// console.log(tickets);
|
||||
// process.exit(0);
|
||||
|
||||
137
server/api/parseLine.js
Normal file
137
server/api/parseLine.js
Normal file
@ -0,0 +1,137 @@
|
||||
'use strict';
|
||||
|
||||
function parseAccount(str) {
|
||||
// str will look like: 'KONTO 1010 "Balanserade utgifter"'
|
||||
const [, number, ...name] = str.split(/\s+/);
|
||||
|
||||
return {
|
||||
number: parseInt(number, 10),
|
||||
name: name.join(' ').slice(1, -1),
|
||||
};
|
||||
}
|
||||
|
||||
const accountType = {
|
||||
T: 1,
|
||||
S: 2,
|
||||
I: 3,
|
||||
K: 4,
|
||||
};
|
||||
|
||||
function parseCompanyName(str) {
|
||||
const [, name] = str.split(/\s+/);
|
||||
|
||||
return name.slice(1, -1);
|
||||
}
|
||||
|
||||
// str should be `ORGNR "556930-1673"`
|
||||
function parseOrganizationNumber(str) {
|
||||
const [, number] = str.split(/\s+/);
|
||||
return number.slice(1, -1);
|
||||
}
|
||||
|
||||
function parseYearDates(str) {
|
||||
const [, current, start, end] = str.split(/\s+/);
|
||||
|
||||
return {
|
||||
start: parseDate(start),
|
||||
end: parseDate(end),
|
||||
current: current === '0',
|
||||
};
|
||||
}
|
||||
|
||||
function parseAccountType(str) {
|
||||
const [, number, type] = str.split(/\s+/);
|
||||
|
||||
return {
|
||||
number: parseInt(number, 10),
|
||||
type: accountType[type],
|
||||
};
|
||||
}
|
||||
|
||||
function parseDate(str) {
|
||||
const [, year, month, day] = /(\d\d\d\d)(\d\d)(\d\d)/.exec(str);
|
||||
|
||||
return new Date(`${year}-${month}-${day}T00:00:00.000Z`);
|
||||
}
|
||||
|
||||
function parseSRU(str) {
|
||||
const [, number, sru] = str.split(/\s+/);
|
||||
|
||||
return {
|
||||
number: parseInt(number, 10),
|
||||
sru: parseInt(sru, 10),
|
||||
};
|
||||
}
|
||||
|
||||
function parseTransaction(str) {
|
||||
// str looks like: ' #TRANS 6310 {} 1541.00 20150824 '
|
||||
const [, accountNumber,, amount] = str.split(/\s+/);
|
||||
|
||||
return {
|
||||
accountNumber: parseInt(accountNumber, 10),
|
||||
amount: parseFloat(amount),
|
||||
};
|
||||
}
|
||||
|
||||
function parseTicket(str) {
|
||||
const [, type, number, date, ...rest] = str.split(/\s+/);
|
||||
const dateCreated = rest.pop();
|
||||
const title = rest.join(' ');
|
||||
|
||||
return {
|
||||
type: parseInt(type, 10),
|
||||
number: parseInt(number, 10),
|
||||
date: parseDate(date),
|
||||
date_created: parseDate(dateCreated),
|
||||
title: title.slice(1, -1),
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
parseAccountType,
|
||||
parseAccount,
|
||||
parseCompanyName,
|
||||
parseOrganizationNumber,
|
||||
parseSRU,
|
||||
parseTransaction,
|
||||
parseTicket,
|
||||
parseYearDates,
|
||||
};
|
||||
|
||||
// console.log(string.split('\n#').length)
|
||||
|
||||
// module.exports = function parseLine(line) {
|
||||
// line = line.slice(1);
|
||||
|
||||
// if (line.startsWith('KONTO')) {
|
||||
// const account = parseAccount(line);
|
||||
// } else if (line.startWith('KTYP') {
|
||||
// const accountType = parseAccountType(line);
|
||||
// }
|
||||
|
||||
// const accounts = [];
|
||||
// const tickets = [];
|
||||
|
||||
// string.split(/\n#/g).forEach((str, i) => {
|
||||
// if (str.startsWith('KONTO')) {
|
||||
// accounts.push(parseAccount(str));
|
||||
// } else if (str.startsWith('KTYP')) {
|
||||
// const { number, type } = parseAccountType(str);
|
||||
|
||||
// // const string = fs.readFileSync('./bitmill2015.se', { encoding: 'UTF8' });
|
||||
// const account = accounts.find((account) => account.number === number);
|
||||
|
||||
// account.type = type;
|
||||
// } else if (str.startsWith('VER')) {
|
||||
// tickets.push(parseTicket(str));
|
||||
// }
|
||||
// });
|
||||
|
||||
// return {
|
||||
// accounts,
|
||||
// tickets,
|
||||
// };
|
||||
//};
|
||||
// console.log(tickets);
|
||||
// process.exit(0);
|
||||
|
||||
7
server/config/dir.js
Normal file
7
server/config/dir.js
Normal file
@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const p = require('path');
|
||||
|
||||
module.exports = {
|
||||
static: p.join(PWD, 'public'),
|
||||
};
|
||||
41
server/config/error-handler.js
Normal file
41
server/config/error-handler.js
Normal file
@ -0,0 +1,41 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
|
||||
// const errorTemplate = require('../templates/error.jsx');
|
||||
const errorTemplate = () => '<h1>Error</h1>';
|
||||
|
||||
const defaults = {
|
||||
post: (req, res, next) => {
|
||||
res.template = errorTemplate;
|
||||
|
||||
next();
|
||||
},
|
||||
|
||||
mystify: {
|
||||
properties: ['errors', 'message', 'name', 'status', 'statusText'],
|
||||
},
|
||||
|
||||
log: {
|
||||
// if database = true there has to be a mongoose model name ErrorModel
|
||||
ignore: [],
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = _.merge(defaults, {
|
||||
development: {
|
||||
log: {
|
||||
console: true,
|
||||
},
|
||||
},
|
||||
testing: {
|
||||
log: {
|
||||
console: false,
|
||||
},
|
||||
},
|
||||
production: {
|
||||
log: {
|
||||
console: false,
|
||||
},
|
||||
},
|
||||
}[ENV]);
|
||||
4
server/config/globals.js
Normal file
4
server/config/globals.js
Normal file
@ -0,0 +1,4 @@
|
||||
'use strict';
|
||||
|
||||
// global.LOGIN_USER = 'linus.miller@thecodebureau.com'
|
||||
// global.LOGIN_USER = 'zarac@zarac.se'
|
||||
78
server/config/membership.js
Normal file
78
server/config/membership.js
Normal file
@ -0,0 +1,78 @@
|
||||
'use strict';
|
||||
|
||||
const site = require('./site');
|
||||
|
||||
module.exports = {
|
||||
invite: {
|
||||
from: `${site.title} Robot <${site.emails.robot}>`,
|
||||
subject: `You have been invited to ${site.title}`,
|
||||
},
|
||||
|
||||
timeouts: {
|
||||
// 1 day
|
||||
changePassword: 24 * 60 * 60 * 1000,
|
||||
// verify email
|
||||
verifyEmail: 7 * 24 * 60 * 60 * 1000,
|
||||
},
|
||||
|
||||
paths: {
|
||||
register: '/register',
|
||||
login: '/login',
|
||||
forgotPassword: '/forgot-password',
|
||||
updatePassword: '/change-password',
|
||||
},
|
||||
|
||||
redirects: {
|
||||
login: '/admin',
|
||||
logout: '/',
|
||||
register: '/admin',
|
||||
},
|
||||
|
||||
remember: {
|
||||
// if expires is defined, it will be used. otherwise maxage
|
||||
expires: new Date('2038-01-19T03:14:07.000Z'),
|
||||
// expires: Date.now() - 1,
|
||||
maxAge: 30 * 24 * 60 * 60 * 1000,
|
||||
},
|
||||
|
||||
messages: {
|
||||
login: {
|
||||
notLocal: 'Account requires external login.',
|
||||
wrongPassword: 'Wrong password.',
|
||||
noLocalUser: 'No user registered with that email.',
|
||||
noExternalUser: 'The account is not connected to this website.',
|
||||
externalLoginFailed: 'External login failed.',
|
||||
unverified: 'This account has not been verified.',
|
||||
banned: 'User is banned.',
|
||||
blocked: 'User is blocked due to too many login attempts.',
|
||||
},
|
||||
|
||||
register: {
|
||||
missingProperties: 'Oh no missing stuff',
|
||||
notAuthorized: 'The email is not authorized to create an account.',
|
||||
duplicateEmail: 'The email has already been registered.',
|
||||
},
|
||||
},
|
||||
|
||||
passport: {
|
||||
local: {
|
||||
usernameField: 'email',
|
||||
},
|
||||
|
||||
scope: ['email'],
|
||||
|
||||
//providers: {
|
||||
// facebook: {
|
||||
// clientID: 'change-this-fool',
|
||||
// clientSecret: 'change-this-fool',
|
||||
// callbackURL: p.join(config.site.domain, '/auth/facebook/callback'),
|
||||
// passReqToCallback: true
|
||||
// },
|
||||
|
||||
},
|
||||
|
||||
// needs to be even
|
||||
tokenLength: 64,
|
||||
// needs to be even
|
||||
saltLength: 16,
|
||||
};
|
||||
10
server/config/port.js
Normal file
10
server/config/port.js
Normal file
@ -0,0 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
const basePort = 3070;
|
||||
|
||||
module.exports = {
|
||||
development: basePort,
|
||||
testing: basePort + 1,
|
||||
staging: basePort + 2,
|
||||
production: basePort + 3,
|
||||
}[ENV];
|
||||
24
server/config/postgres.js
Normal file
24
server/config/postgres.js
Normal file
@ -0,0 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
const defaults = {
|
||||
user: 'vizwiz', //env var: PGUSER
|
||||
database: 'vizwiz', //env var: PGDATABASE
|
||||
password: 'secret', //env var: PGPASSWORD
|
||||
host: 'localhost', // Server hosting the postgres database
|
||||
port: 5432, //env var: PGPORT
|
||||
max: 10, // max number of clients in the pool
|
||||
idleTimeoutMillis: 30000, // how long a client is allowed to remain idle before being closed
|
||||
};
|
||||
// {
|
||||
// user: 'newseri_supreme', // env var: PGUSER
|
||||
// database: 'newseri', // env var: PGDATABASE
|
||||
// password: 'oh-look-it-is-raining-news', // env var: PGPASSWORD
|
||||
// // host: '192.168.1.11', // Server hosting the postgres database
|
||||
// host: 'hq.bitmill.co', // Server hosting the postgres database
|
||||
// // port: 5432, // env var: PGPORT
|
||||
// port: 6543, // env var: PGPORT
|
||||
// max: 10, // max number of clients in the pool
|
||||
// idleTimeoutMillis: 30000, // how long a client is allowed to remain idle before being closed
|
||||
// };
|
||||
|
||||
module.exports = defaults;
|
||||
38
server/config/session.js
Normal file
38
server/config/session.js
Normal file
@ -0,0 +1,38 @@
|
||||
'use strict';
|
||||
|
||||
const chalk = require('chalk');
|
||||
const session = require('express-session');
|
||||
|
||||
let redisStore;
|
||||
|
||||
const config = {
|
||||
secret: 'ohohohogogogoONMYTOETOEthisisacompletelyrandomgeneratedstring.thisisonlyacoincidence.',
|
||||
resave: false,
|
||||
saveUninitialized: true,
|
||||
};
|
||||
|
||||
const redisConfig = {
|
||||
host: 'localhost',
|
||||
port: 6379,
|
||||
};
|
||||
|
||||
if (ENV === 'production') {
|
||||
const RedisStore = require('connect-redis')(require('express-session'));
|
||||
|
||||
redisStore = new RedisStore(redisConfig);
|
||||
|
||||
redisStore.on('connect', () => {
|
||||
console.info(`[${chalk.cyan('INIT')}] Redis connected succcessfully`);
|
||||
});
|
||||
|
||||
redisStore.on('disconnect', () => {
|
||||
throw new Error('Unable to connect to redis. Has it been started?');
|
||||
});
|
||||
|
||||
config.store = redisStore;
|
||||
} else {
|
||||
config.store = new session.MemoryStore();
|
||||
}
|
||||
|
||||
module.exports = config;
|
||||
|
||||
34
server/config/shim.js
Normal file
34
server/config/shim.js
Normal file
@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.9/es5-shim.min.js': [
|
||||
'chrome <= 12',
|
||||
'firefox <= 20',
|
||||
'ie <= 9',
|
||||
'opera <= 12',
|
||||
'safari <= 5',
|
||||
],
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.35.1/es6-shim.min.js': [
|
||||
// 'https://cdnjs.cloudflare.com/ajax/libs/core-js/2.4.1/core.min.js': [
|
||||
// 'https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.16.0/polyfill.js': [
|
||||
'chrome <= 51',
|
||||
'edge <= 13',
|
||||
'firefox <= 44',
|
||||
'ie <= 11',
|
||||
'safari <= 9',
|
||||
],
|
||||
'https://unpkg.com/es7-shim@latest/dist/es7-shim.min.js': [
|
||||
'chrome <= 50',
|
||||
'edge <= 14',
|
||||
'firefox <= 46',
|
||||
'ie <= 11',
|
||||
'safari <= 10',
|
||||
],
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/fetch/1.0.0/fetch.min.js': [
|
||||
'chrome <= 41',
|
||||
'edge <= 13',
|
||||
'firefox <= 38',
|
||||
'ie <= 11',
|
||||
'safari <= 9',
|
||||
],
|
||||
};
|
||||
51
server/config/site.js
Normal file
51
server/config/site.js
Normal file
@ -0,0 +1,51 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
|
||||
const domain = 'bitmill.co';
|
||||
|
||||
const defaults = {
|
||||
domain,
|
||||
title: 'Vizwiz',
|
||||
name: 'vizwiz',
|
||||
protocol: 'http',
|
||||
get host() {
|
||||
return this.port ? `${this.hostname}:${this.port}` : this.hostname;
|
||||
},
|
||||
get url() {
|
||||
return `${this.protocol}://${this.host}/`;
|
||||
},
|
||||
emails: {
|
||||
robot: 'no-reply@thecodebureau.com',
|
||||
info: 'info@thecodebureau.com',
|
||||
webmaster: 'webmaster@thecodebureau.com',
|
||||
order: 'info@thecodebureau.com',
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = _.merge(defaults, {
|
||||
development: {
|
||||
hostname: 'localhost',
|
||||
port: process.env.EXTERNAL_PORT || process.env.PORT || require('./port'),
|
||||
},
|
||||
|
||||
testing: {
|
||||
hostname: 'localhost',
|
||||
port: process.env.PORT || require('./port'),
|
||||
},
|
||||
|
||||
staging: {
|
||||
hostname: `vizwiz.staging.${domain}`,
|
||||
},
|
||||
|
||||
production: {
|
||||
hostname: `vizwiz.${domain}`,
|
||||
protocol: 'https',
|
||||
emails: {
|
||||
robot: `no-reply@${domain}`,
|
||||
info: `info@${domain}`,
|
||||
webmaster: `webmaster@${domain}`,
|
||||
order: `order@${domain}`,
|
||||
},
|
||||
},
|
||||
}[ENV]);
|
||||
15
server/config/smtp.js
Normal file
15
server/config/smtp.js
Normal file
@ -0,0 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
|
||||
const defaults = {
|
||||
auth: {
|
||||
user: 'SMTP_Injection',
|
||||
// dev key
|
||||
pass: '2eec390c5b3f5d593c9f152179bf51e90b073784',
|
||||
},
|
||||
host: 'smtp.sparkpostmail.com',
|
||||
port: 587,
|
||||
};
|
||||
|
||||
module.exports = _.merge(defaults, {}[ENV]);
|
||||
16
server/db.js
Normal file
16
server/db.js
Normal file
@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
// const factory = require('midwest/util/db');
|
||||
const conf = require('./config/postgres');
|
||||
|
||||
// module.exports = factory(conf);
|
||||
//
|
||||
const knex = require('knex');
|
||||
|
||||
const pg = knex({
|
||||
client: 'pg',
|
||||
connection: conf,
|
||||
});
|
||||
|
||||
module.exports = pg;
|
||||
|
||||
30
server/middleware.js
Normal file
30
server/middleware.js
Normal file
@ -0,0 +1,30 @@
|
||||
'use strict';
|
||||
|
||||
const db = require('./db');
|
||||
|
||||
module.exports = {
|
||||
result(req, res, next) {
|
||||
db.select().sum('transactions.amount as sum').from('transactions')
|
||||
.innerJoin('accounts', 'transactions.account_id', 'accounts.id')
|
||||
.innerJoin('tickets', 'transactions.ticket_id', 'tickets.id')
|
||||
.whereBetween('tickets.date', ['2015-03-01', '2015-03-31]'])
|
||||
.andWhereBetween('accounts.account_type_id', [3, 4])
|
||||
.then((res) => {
|
||||
console.log(res);
|
||||
// console.log(res);
|
||||
// const result = res.reduce((result, t) => {
|
||||
// console.log(t.amount);
|
||||
// console.log(parseFloat(t.amount, 2));
|
||||
// result += parseFloat(t.amount, 2);
|
||||
|
||||
// return result;
|
||||
|
||||
// }, 0);
|
||||
// console.log(result);
|
||||
});
|
||||
// db.schema.raw(`SELECT * FROM transactions INNER JOIN accounts ON accounts.id = transactions.account_id
|
||||
// WHERE accounts.account_type_id = 3;`, (result) => {
|
||||
// console.log(result);
|
||||
// })
|
||||
}
|
||||
};
|
||||
26
server/render.js
Normal file
26
server/render.js
Normal file
@ -0,0 +1,26 @@
|
||||
const { h } = require('jsx-node');
|
||||
|
||||
module.exports = function (Component, Master) {
|
||||
const locals = Object.assign({ query: this.req.query }, this.app.locals, this.locals);
|
||||
|
||||
if (typeof Master === 'function') {
|
||||
if (typeof Component === 'function') {
|
||||
return this.send(
|
||||
h(Master, locals,
|
||||
h(Component, locals)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Component = Master;
|
||||
}
|
||||
|
||||
if (typeof Component !== 'function') {
|
||||
throw new Error('Not a Component');
|
||||
} else if (Component.prototype && Component.prototype.render) {
|
||||
const i = new Component(locals);
|
||||
this.send(i.render(i.props, i.state));
|
||||
} else {
|
||||
this.send(Component(locals));
|
||||
}
|
||||
};
|
||||
251
server/routers/api.js
Normal file
251
server/routers/api.js
Normal file
@ -0,0 +1,251 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const router = new (require('express').Router)();
|
||||
const Busboy = require('busboy');
|
||||
const split = require('split');
|
||||
|
||||
const db = require('../db');
|
||||
|
||||
const parse = require('../api/parse');
|
||||
|
||||
const {
|
||||
parseAccount,
|
||||
parseAccountType,
|
||||
parseCompanyName,
|
||||
parseSRU,
|
||||
parseTransaction,
|
||||
parseTicket,
|
||||
parseYearDates,
|
||||
parseOrganizationNumber,
|
||||
} = require('../api/parseLine');
|
||||
|
||||
const queries = {
|
||||
saveAccount(account) {
|
||||
db('accounts').insert(h)
|
||||
}
|
||||
}
|
||||
|
||||
const handlers = require('../handlers');
|
||||
|
||||
router.get('/parse', (req, res) => res.send('<h1>Parse</h1>'));
|
||||
router.post('/sie', (req, res) => {
|
||||
const busboy = new Busboy({ headers: req.headers });
|
||||
|
||||
busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
|
||||
let accounts = [];
|
||||
let ticketId = null;
|
||||
let companyDone = false;
|
||||
let yearDone = false;
|
||||
let accountsDone = false;
|
||||
let accountIds = [];
|
||||
let details = {};
|
||||
const transactions = [];
|
||||
let currentYearDates = [];
|
||||
let currentYear;
|
||||
let previousYearDates = [];
|
||||
let company;
|
||||
|
||||
// let saving = false;
|
||||
const splitStream = file.pipe(split())
|
||||
.on('data', async (line) => {
|
||||
line = line.trim();
|
||||
|
||||
if (line[0] !== '#') return;
|
||||
|
||||
const lineType = line.slice(1, line.indexOf(' '));
|
||||
console.log(lineType);
|
||||
|
||||
if (!companyDone) {
|
||||
switch (lineType) {
|
||||
case 'ORGNR': {
|
||||
const organizationNumber = parseOrganizationNumber(line);
|
||||
|
||||
splitStream.pause();
|
||||
|
||||
company = await handlers.getCompanyByOrganizationNumber(organizationNumber) || { organization_number: organizationNumber };
|
||||
|
||||
splitStream.resume();
|
||||
|
||||
return;
|
||||
}
|
||||
case 'FNAMN': {
|
||||
const companyName = parseCompanyName(line);
|
||||
|
||||
if (!company.name || company.name !== companyName) {
|
||||
company.name = companyName;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
case 'RAR': {
|
||||
if (!company.id) {
|
||||
splitStream.pause();
|
||||
|
||||
company = await handlers.createCompany(company);
|
||||
|
||||
companyDone = true;
|
||||
|
||||
splitStream.resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (companyDone && !yearDone) {
|
||||
switch (lineType) {
|
||||
case 'RAR': {
|
||||
const dates = parseYearDates(line);
|
||||
|
||||
if (dates.current) {
|
||||
currentYearDates = _.omit(dates, 'current');
|
||||
} else {
|
||||
previousYearDates = _.omit(dates, 'current');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
case 'KONTO': {
|
||||
splitStream.pause();
|
||||
|
||||
const previousYear = await handlers.getYearByCompanyIdAndDates(company.id, previousYearDates);
|
||||
|
||||
// currentYear = await handlers.getYearByCompanyIdAndDates(company.id, currentYearDates) || Object.assign({ company_id: company.id }, _.mapKeys(currentYearDates, (value, key) => `${key}_date`);
|
||||
currentYear = Object.assign({ company_id: company.id }, _.mapKeys(currentYearDates, (value, key) => `${key}_date`));
|
||||
|
||||
if (previousYear) currentYear.previous_year_id = previousYear.id;
|
||||
|
||||
// TODO remove all entries for year
|
||||
currentYear = await handlers.createYear(currentYear);
|
||||
|
||||
yearDone = true;
|
||||
|
||||
splitStream.resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (companyDone && yearDone && !accountsDone) {
|
||||
console.log(lineType);
|
||||
console.log(line);
|
||||
switch (lineType) {
|
||||
case 'KONTO':
|
||||
const account = parseAccount(line);
|
||||
if (!account || !account.number) console.log(account);
|
||||
accounts.push(account);
|
||||
|
||||
return;
|
||||
case 'KTYP': {
|
||||
const { number, type } = parseAccountType(line);
|
||||
|
||||
const account = accounts.find((account) => account.number === number) || { number };
|
||||
|
||||
if (account) account.account_type_id = type;
|
||||
|
||||
return;
|
||||
}
|
||||
case 'SRU': {
|
||||
const { number, sru } = parseSRU(line);
|
||||
|
||||
const account = accounts.find((account) => account.number === number) || { number };
|
||||
|
||||
// if (account)
|
||||
account.sru = sru;
|
||||
|
||||
return;
|
||||
}
|
||||
case 'VER':
|
||||
console.log('shhould be saviung accounts');
|
||||
splitStream.pause();
|
||||
// console.log(accounts);
|
||||
accounts.forEach((account) => {
|
||||
if (!account.account_type_id) console.log(account);
|
||||
});
|
||||
|
||||
await db('accounts').returning(['id', 'number']).insert(accounts).then((res) => {
|
||||
accounts = res;
|
||||
}).catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
accountsDone = true;
|
||||
|
||||
splitStream.resume();
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
|
||||
if (companyDone && yearDone && accountsDone) {
|
||||
switch (lineType) {
|
||||
case 'VER': {
|
||||
splitStream.pause();
|
||||
|
||||
const ticket = parseTicket(line);
|
||||
|
||||
await db('tickets').returning('id').insert(_.omit(ticket, 'transactions')).then((res) => {
|
||||
ticketId = res[0];
|
||||
}).catch((err) => console.log(err));
|
||||
|
||||
splitStream.resume();
|
||||
|
||||
return;
|
||||
}
|
||||
case 'TRANS': {
|
||||
const transaction = parseTransaction(line);
|
||||
|
||||
transaction.ticket_id = ticketId;
|
||||
|
||||
const account = accounts.find((account) => account.number === transaction.accountNumber);
|
||||
|
||||
delete transaction.accountNumber;
|
||||
|
||||
transaction.account_id = account.id;
|
||||
|
||||
transactions.push(transaction);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.on('end', () => {
|
||||
db('transactions').insert(transactions)
|
||||
.catch((err) => console.log(err));
|
||||
});
|
||||
});
|
||||
|
||||
busboy.on('field', function(fieldname, val, fieldnameTruncated, valTruncated, encoding, mimetype) {
|
||||
console.log('Field [' + fieldname + ']: value: ' + inspect(val));
|
||||
});
|
||||
|
||||
busboy.on('finish', function() {
|
||||
console.log('Done parsing form!');
|
||||
// res.writeHead(303, { Connection: 'close', Location: '/' });
|
||||
// res.end();
|
||||
|
||||
res.sendStatus(204);
|
||||
});
|
||||
|
||||
req.pipe(busboy);
|
||||
|
||||
// tickets.forEach(({ type, number, date, dateCreated, title, transactions }) => {
|
||||
// db.query(`INSERT INTO tickets (number, type, title, date, date_created) VALUES ('${number}', '${type}', '${title}', '2016-11-01', '2016-11-01') RETURNING id`, (err, result) => {
|
||||
// if (err) throw err;
|
||||
|
||||
// console.log(result.rows[0]);
|
||||
// const ticketId = result.rows[0].id;
|
||||
|
||||
// transactions.forEach(({ account, amount }) => {
|
||||
// db.query(`INSERT INTO transactions (account, amount, ticket) VALUES ('${account}', '${amount}', '${ticketId}')`, (err, result) => {
|
||||
// console.log(result);
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
// })
|
||||
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
7
server/routers/index.js
Normal file
7
server/routers/index.js
Normal file
@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const router = new (require('express').Router)();
|
||||
|
||||
router.get('/', (req, res) => res.send('<h1>Hello</h1>'));
|
||||
|
||||
module.exports = router;
|
||||
132
server/server.js
Normal file
132
server/server.js
Normal file
@ -0,0 +1,132 @@
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
* The main file that sets up the Express instance and node
|
||||
*
|
||||
* @module server/server
|
||||
* @type {Express instance}
|
||||
*/
|
||||
|
||||
// set up some globals (these are also set in Epiphany if not already set)
|
||||
global.ENV = process.env.NODE_ENV || 'development';
|
||||
global.PWD = process.env.NODE_PWD || process.cwd();
|
||||
|
||||
// modules > native
|
||||
const p = require('path');
|
||||
|
||||
// output filename in console log and colour console.dir
|
||||
if (ENV === 'development') {
|
||||
require('midwest/util/console');
|
||||
// needed so symlinked modules get access to main projects node_modules/
|
||||
require('app-module-path').addPath(p.join(PWD, 'node_modules'));
|
||||
}
|
||||
|
||||
// make node understand `*.jsx` files
|
||||
require('jsx-node').install();
|
||||
|
||||
// modules > 3rd party
|
||||
const chalk = require('chalk');
|
||||
const express = require('express');
|
||||
// const passport = require('passport');
|
||||
const requireDir = require('require-dir');
|
||||
|
||||
// modules > express middlewares
|
||||
const bodyParser = require('body-parser');
|
||||
// const session = require('express-session');
|
||||
// const cookieParser = require('cookie-parser');
|
||||
|
||||
// modules > midwest
|
||||
const colorizeStack = require('midwest/util/colorize-stack');
|
||||
|
||||
const config = requireDir('./config', { camelcase: true });
|
||||
|
||||
// make error output stack pretty
|
||||
process.on('uncaughtException', (err) => {
|
||||
console.error(chalk.red('UNCAUGHT EXCEPTION'));
|
||||
if (err.stack) {
|
||||
console.error(colorizeStack(err.stack));
|
||||
} else {
|
||||
console.error(err);
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
const prewares = [
|
||||
express.static(config.dir.static, ENV === 'production' ? { maxAge: '1 year' } : null),
|
||||
bodyParser.json(),
|
||||
bodyParser.urlencoded({ extended: true }),
|
||||
// cookieParser(),
|
||||
// session(config.session),
|
||||
// passport.initialize(),
|
||||
// passport.session(),
|
||||
];
|
||||
|
||||
if (ENV === 'development') {
|
||||
// only log requests to console in development mode
|
||||
prewares.unshift(require('morgan')('dev'));
|
||||
// automatically login global.LOGIN_USER
|
||||
// prewares.push(require('midwest-module-membership/passport/automatic-login'));
|
||||
}
|
||||
|
||||
const postwares = [
|
||||
require('midwest/middleware/ensure-found'),
|
||||
// transform and log error
|
||||
require('midwest/factories/error-handler')(config.errorHandler),
|
||||
// respond
|
||||
require('midwest/middleware/responder'),
|
||||
];
|
||||
|
||||
const server = express();
|
||||
|
||||
// get IP & whatnot from nginx proxy
|
||||
server.set('trust proxy', true);
|
||||
|
||||
Object.assign(server.locals, {
|
||||
site: require('./config/site'),
|
||||
});
|
||||
|
||||
// override default response render method for
|
||||
server.response.render = require('./render.js');
|
||||
|
||||
try {
|
||||
server.locals.js = require(p.join(PWD, 'public/js.json'));
|
||||
} catch (e) {}
|
||||
|
||||
try {
|
||||
server.locals.css = require(p.join(PWD, 'public/css.json'));
|
||||
} catch (e) {}
|
||||
|
||||
// load all prewares
|
||||
server.use(...prewares);
|
||||
|
||||
// routes
|
||||
server.use(require('./routers/index'));
|
||||
server.use('/api', require('./routers/api'));
|
||||
|
||||
// routes > authentication
|
||||
// server.use('/auth', require('midwest-module-membership/passport/router'));
|
||||
|
||||
// routes > api > membership
|
||||
// server.use('/api/roles', require('midwest-module-membership/services/roles/router'));
|
||||
// server.use('/api/permissions', require('midwest-module-membership/services/permissions/router'));
|
||||
// server.use('/api/invites', require('midwest-module-membership/services/invites/router'));
|
||||
// server.use('/api/users', require('midwest-module-membership/services/users/router'));
|
||||
|
||||
// load all postwares
|
||||
server.use(...postwares);
|
||||
|
||||
// mpromise (built in mongoose promise library) is deprecated,
|
||||
// tell mongoose to use native Promises instead
|
||||
|
||||
// Only start Express server when it is the main module (ie not required by test)
|
||||
if (require.main === module) {
|
||||
server.http = server.listen(config.port, () => {
|
||||
console.info(`[${chalk.cyan('INIT')}] HTTP Server listening on port ${chalk.magenta(config.port)} (${chalk.yellow(ENV)})`);
|
||||
});
|
||||
}
|
||||
|
||||
const mw = require('./middleware');
|
||||
|
||||
mw.result();
|
||||
|
||||
module.exports = server;
|
||||
Loading…
Reference in New Issue
Block a user