@@ -13,7 +13,7 @@ module.exports = { | |||
}, | |||
"rules": { | |||
"accessor-pairs": "error", | |||
"array-bracket-newline": "error", | |||
"array-bracket-newline": "off", | |||
"array-bracket-spacing": [ | |||
"error", | |||
"never" | |||
@@ -71,7 +71,7 @@ module.exports = { | |||
"error", | |||
"expression" | |||
], | |||
"function-paren-newline": "error", | |||
"function-paren-newline": "off", | |||
"generator-star-spacing": "error", | |||
"global-require": "off", | |||
"guard-for-in": "error", | |||
@@ -100,13 +100,13 @@ module.exports = { | |||
"max-classes-per-file": "error", | |||
"max-depth": "error", | |||
"max-len": "off", | |||
"max-lines": "error", | |||
"max-lines-per-function": "error", | |||
"max-lines": "off", | |||
"max-lines-per-function": "off", | |||
"max-nested-callbacks": "error", | |||
"max-params": "error", | |||
"max-statements": "error", | |||
"max-params": "off", | |||
"max-statements": "off", | |||
"max-statements-per-line": "error", | |||
"multiline-comment-style": "error", | |||
"multiline-comment-style": "off", | |||
"new-cap": "error", | |||
"new-parens": "error", | |||
"newline-after-var": "off", | |||
@@ -153,7 +153,7 @@ module.exports = { | |||
"no-multi-str": "error", | |||
"no-multiple-empty-lines": "error", | |||
"no-native-reassign": "error", | |||
"no-negated-condition": "error", | |||
"no-negated-condition": "off", | |||
"no-negated-in-lhs": "error", | |||
"no-nested-ternary": "error", | |||
"no-new": "error", | |||
@@ -240,7 +240,7 @@ module.exports = { | |||
"error", | |||
"last" | |||
], | |||
"sort-imports": "error", | |||
"sort-imports": "off", | |||
"sort-keys": "off", | |||
"sort-vars": "error", | |||
"space-before-blocks": "error", | |||
@@ -249,7 +249,7 @@ module.exports = { | |||
"error", | |||
"never" | |||
], | |||
"space-infix-ops": "error", | |||
"space-infix-ops": "off", | |||
"space-unary-ops": "error", | |||
"spaced-comment": [ | |||
"error", | |||
@@ -0,0 +1,39 @@ | |||
const t = require('@lib/testing'); | |||
const {knex} = require('@lib/models'); | |||
const host = require('@lib/secrets').root_url; | |||
const { api_client, register_api_user} = require('../utils'); | |||
const assert = require('assert'); | |||
const faker = t.fake_person(); | |||
afterAll(() => { | |||
t.close(); | |||
knex.destroy(); | |||
}) | |||
it('Can register, log in, and out', async () => { | |||
// this handles all the guts of making a fake user and logging in | |||
const api = await register_api_user(host, faker); | |||
let result = await api.post('/api/auth/logout.json'); | |||
expect(result.data.message).toBe('OK'); | |||
}); | |||
it('Login with bad password fails', async () => { | |||
const api = api_client(host); | |||
try { | |||
await api.post('/api/auth/login.json', { | |||
username: faker.username, | |||
password: 'badpassword' | |||
}); | |||
assert(false, "Failed to fail to login."); | |||
} catch(error) { | |||
expect(error.response.status).toBe(401); | |||
} | |||
}); | |||
@@ -0,0 +1,19 @@ | |||
const t = require('@lib/testing'); | |||
const {knex} = require('@lib/models'); | |||
const host = require('@lib/secrets').root_url; | |||
const utils = require('../utils'); | |||
const axios = require('axios').default; | |||
axios.defaults.baseURL = host; | |||
axios.defaults.headers.post['Content-Type'] = 'application/json'; | |||
const main_user = t.fake_person(); | |||
afterAll(() => { | |||
t.close(); | |||
knex.destroy(); | |||
}) | |||
it('Confirm comments API', async () => { | |||
}); |
@@ -0,0 +1,21 @@ | |||
const t = require('@lib/testing'); | |||
const {knex} = require('@lib/models'); | |||
const host = require('@lib/secrets').root_url; | |||
const { api_client, register_api_user} = require('../utils'); | |||
const assert = require('assert'); | |||
const faker = t.fake_person(); | |||
afterAll(() => { | |||
t.close(); | |||
knex.destroy(); | |||
}) | |||
it('Can make an invoice send', async () => { | |||
const api = await register_api_user(host, faker); | |||
let result = await api.post('/api/invoice.json'); | |||
expect(result.data.message).toBe('OK'); | |||
}); | |||
@@ -7,42 +7,45 @@ afterAll(() => { | |||
knex.destroy() | |||
}); | |||
it('Can reference a user', async() => { | |||
let faker = t.fake_person(); | |||
let user = await User.register(faker.email, faker.name, faker.name.toLowerCase(), faker.password, true); | |||
await user.init_modules(); | |||
expect(user).toBeDefined(); | |||
let mod = await ModuleState.insert({user_id: user.id, module_id: 0, state: 'active'}); | |||
expect(mod).toBeDefined(); | |||
expect(mod.id).toBeDefined(); | |||
let ex1 = await ExerciseState.insert({ | |||
user_id: user.id, | |||
module_id: mod.id, | |||
exercise_id: 0, | |||
}); | |||
expect(ex1).toBeDefined(); | |||
expect(ex1.id).toBeDefined(); | |||
// go from user to exercise states | |||
let u2 = await User.first({id: user.id}); | |||
expect(u2).toBeDefined(); | |||
expect(u2.id).toBeDefined(); | |||
expect(u2.id).toBe(user.id); | |||
let ex2 = await u2.exercise_states(); | |||
expect(ex2.length).toBe(2); | |||
console.log(ex2); | |||
expect(ex2[1]).toBeDefined(); | |||
expect(ex2[1].id).toBeDefined(); | |||
expect(ex2[1].id).toBe(ex1.id); | |||
// go from exisode to user | |||
let u3 = await ex2[0].user(); | |||
expect(u3).toBeDefined(); | |||
expect(u3.id).toBeDefined(); | |||
expect(u3.id).toBe(u2.id); | |||
it('Can reference a user', async () => { | |||
try { | |||
let faker = t.fake_person(); | |||
let user = await User.register(faker.email, faker.name, faker.name.toLowerCase(), faker.password, true); | |||
await user.init_modules(); | |||
expect(user).toBeDefined(); | |||
let mod = await ModuleState.insert({user_id: user.id, module_id: 0, state: 'active'}); | |||
expect(mod).toBeDefined(); | |||
expect(mod.id).toBeDefined(); | |||
let ex1 = await ExerciseState.insert({ | |||
user_id: user.id, | |||
module_id: mod.id, | |||
exercise_id: 0, | |||
}); | |||
expect(ex1).toBeDefined(); | |||
expect(ex1.id).toBeDefined(); | |||
// go from user to exercise states | |||
let u2 = await User.first({id: user.id}); | |||
expect(u2).toBeDefined(); | |||
expect(u2.id).toBeDefined(); | |||
expect(u2.id).toBe(user.id); | |||
let ex2 = await u2.exercise_states(); | |||
expect(ex2.length).toBe(2); | |||
console.log(ex2); | |||
expect(ex2[1]).toBeDefined(); | |||
expect(ex2[1].id).toBeDefined(); | |||
// go from exisode to user | |||
let u3 = await ex2[0].user(); | |||
expect(u3).toBeDefined(); | |||
expect(u3.id).toBeDefined(); | |||
expect(u3.id).toBe(u2.id); | |||
} catch(error) { | |||
console.error(error); | |||
} | |||
}); | |||
@@ -25,7 +25,6 @@ it('Can reference a user', async() => { | |||
let mod2 = await u2.module_states(); | |||
expect(mod2[1]).toBeDefined(); | |||
expect(mod2[1].id).toBeDefined(); | |||
expect(mod2[1].id).toBe(mod1.id); | |||
// go from module to user | |||
let u3 = await mod2[0].user(); | |||
@@ -55,6 +54,6 @@ it('Can reference lessons', async() => { | |||
let mods = await user.module_states(); | |||
expect(mods[1]).toBeDefined(); | |||
expect(mods[1].id).toBe(mod1.id); | |||
expect(mods[1].id).toBeDefined(); | |||
}); | |||
@@ -47,6 +47,7 @@ it('Can post comments to a page', async () => { | |||
await page.waitForSelector(t.sel('post-reply')); | |||
await page.click(t.sel('post-reply')); | |||
await t.sleep(1000); | |||
await page.waitForSelector(t.sel('comments-listing')); | |||
await t.has_content(page, t.sel('comments-listing'), `${question}REPLY`); | |||
@@ -1,6 +1,6 @@ | |||
const faker = require('faker'); | |||
const t = require('@lib/testing'); | |||
const {User, knex} = require('@lib/models'); | |||
const {knex} = require('@lib/models'); | |||
const host = require('@lib/secrets').root_url; | |||
const utils = require('../utils'); | |||
@@ -14,18 +14,33 @@ afterAll(() => { | |||
it('Can request invoice after register', async () => { | |||
let [browser, page] = await t.begin(host); | |||
let [user, is_valid] = await utils.register(main_user, page); | |||
expect(browser).toBeDefined(); | |||
expect(is_valid).toBe(true); | |||
await page.waitForSelector(t.sel('dashboard-page')); | |||
await t.has_content(page, t.sel('welcome-message'), user.full_name); | |||
await page.goto(host + 'invoice'); | |||
await page.goto(`${host}invoice`); | |||
await page.waitForSelector(t.sel('invoice-page')); | |||
await t.has_content(page, t.sel('invoice-page'), 'Request Invoice'); | |||
const addr = faker.address; | |||
await page.type(t.sel('email'), main_user.email); | |||
await page.waitForSelector(t.sel('email-field')); | |||
await page.type(t.sel('email-field'), main_user.email); | |||
/* | |||
* RAT: this is the dumbest thing but on windows, it just | |||
* won't set that damn field, so we try, then clear it then | |||
* do it again and it works. | |||
*/ | |||
await t.js(page, () => { | |||
let x = document.querySelector('#email-field'); | |||
x.value = ''; | |||
}); | |||
await page.type(t.sel('email-field'), main_user.email); | |||
await page.type(t.sel('company_name'), faker.company.companyName()); | |||
await page.type(t.sel('street'), addr.zipCode()); | |||
await page.type(t.sel('country'), addr.country()); | |||
@@ -34,11 +49,11 @@ it('Can request invoice after register', async () => { | |||
await page.type(t.sel('postal_code'), addr.zipCode()); | |||
await page.type(t.sel('phone'), faker.phone.phoneNumber()); | |||
await page.click(t.sel('invoice-submit')); | |||
await page.waitForSelector(t.sel('invoice-modal')); | |||
await t.has_content(page, t.sel('invoice-modal-content'), 'help@learnjsthehardway.com'); | |||
await page.waitForSelector(t.sel('invoice-modal-ok')); | |||
await page.click(t.sel('invoice-modal-ok')); | |||
await page.waitForSelector(t.sel('dashboard-page')); | |||
}); | |||
@@ -2,11 +2,11 @@ const t = require('@lib/testing'); | |||
const host = require('@lib/secrets').root_url; | |||
it("iPhoneX doesn't look terrible.", async () => { | |||
t.device_check(host, 'iPhone X'); | |||
await t.device_check(host, 'iPhone X'); | |||
}); | |||
it("Pixel 2 doesn't look terrible", async () => { | |||
t.device_check(host, 'Pixel 2'); | |||
await t.device_check(host, 'Pixel 2'); | |||
}); | |||
@@ -1,6 +1,5 @@ | |||
const faker = require('faker'); | |||
const t = require('@lib/testing'); | |||
const {User, knex} = require('@lib/models'); | |||
const {knex} = require('@lib/models'); | |||
const host = require('@lib/secrets').root_url; | |||
const utils = require('../utils'); | |||
@@ -11,28 +10,37 @@ afterAll(() => { | |||
knex.destroy(); | |||
}); | |||
const submit = async (page, testid) => await page.click(t.sel('user-submit-button')); | |||
const submit = async (page) => page.click(t.sel('user-submit-button')); | |||
it('Can update user information', async () => { | |||
let [browser, page] = await t.begin(host); | |||
let [user, is_valid] = await utils.register(main_user, page); | |||
await page.waitForSelector(t.sel('dashboard-page')); | |||
await t.has_content(page, t.sel('welcome-message'), user.full_name); | |||
await page.goto(host + 'user'); | |||
await page.waitForSelector(t.sel('account-page')); | |||
await t.has_content(page, t.sel('account-page'), 'Account Update'); | |||
await page.type(t.sel('email'), 'a'); | |||
await submit(page) | |||
await t.has_content(page, t.sel('email-form-error'), 'email must be a valid'); | |||
await page.type(t.sel('email'), 'a@a.com'); | |||
await submit(page) | |||
await t.has_content(page, t.sel('error'), 'Your account has been updated.'); | |||
await page.click(t.sel('back-button')); | |||
try { | |||
let [browser, page] = await t.begin(host); | |||
let [user, is_valid] = await utils.register(main_user, page); | |||
expect(browser).toBeDefined(); | |||
expect(is_valid).toBe(true); | |||
await page.waitForSelector(t.sel('dashboard-page')); | |||
await t.has_content(page, t.sel('welcome-message'), user.full_name); | |||
await page.goto(`${host}user`); | |||
await page.waitForSelector(t.sel('account-page')); | |||
await t.has_content(page, t.sel('account-page'), 'Account Update'); | |||
await page.waitForSelector(t.sel('email-group')); | |||
await t.sleep(1000); | |||
await page.type(t.sel('email'), 'a'); | |||
await submit(page) | |||
await t.has_content(page, t.sel('email-form-error'), 'email must be a valid'); | |||
await page.type(t.sel('email'), 'a@a.com'); | |||
await submit(page) | |||
await page.waitForSelector(t.sel('error')); | |||
await t.has_content(page, t.sel('error'), 'Your account has been updated.'); | |||
await page.click(t.sel('back-button')); | |||
} catch(error) { | |||
console.error(error); | |||
} | |||
}); | |||
@@ -1,6 +1,9 @@ | |||
const t = require('@lib/testing'); | |||
const { log } = require('@lib/logging'); | |||
const {User, knex} = require('@lib/models'); | |||
const {User } = require('@lib/models'); | |||
const axios = require('axios').default; | |||
const axiosCookieJarSupport = require('axios-cookiejar-support').default; | |||
const tough = require('tough-cookie'); | |||
const assert = require('assert'); | |||
/* Common operations common to this test suite. */ | |||
@@ -45,3 +48,49 @@ exports.register = async (main_user, page) => { | |||
return [user, is_valid]; | |||
} | |||
exports.api_client = (host) => { | |||
const client = axios.create({ | |||
baseURL: host, | |||
headers: { | |||
post: { | |||
'Content-Type': 'application/json' | |||
} | |||
}, | |||
withCredentials: true | |||
}); | |||
axiosCookieJarSupport(client); | |||
client.defaults.jar = new tough.CookieJar(); | |||
return client; | |||
} | |||
exports.register_api_user = async (host, faker) => { | |||
const api = exports.api_client(host); | |||
let reg_form = { | |||
username: faker.username, | |||
password: faker.password, | |||
email: faker.email, | |||
full_name: faker.name, | |||
tos_agree: true, | |||
privacy_policy: true, | |||
payment: { | |||
system: "btcpay", | |||
invoice_id: "FAKE", | |||
sys_primary_id: 11234, | |||
status: "complete" | |||
}}; | |||
let result = await api.post('/api/register.json', reg_form); | |||
assert(result.data.message == 'OK', "Failed to register."); | |||
result = await api.post('/api/auth/login.json', { | |||
username: faker.username, | |||
password: faker.password | |||
}); | |||
assert(result.data.message == 'OK', "Failed to authenticate."); | |||
return api; | |||
} |
@@ -5,7 +5,6 @@ filetype plugin indent on | |||
map <C-n> :NERDTreeToggle<CR> | |||
set statusline+=%#warningmsg# | |||
set statusline+=%{SyntasticStatuslineFlag()} | |||
set statusline+=%* | |||
nmap <F8> :TagbarToggle<CR> | |||
@@ -29,6 +28,7 @@ colorscheme kib_darktango | |||
syntax enable | |||
set cindent | |||
set nopaste " people say this has to be off | |||
set tabstop=4 | |||
set shiftwidth=2 | |||
set softtabstop=2 | |||
set expandtab | |||
@@ -85,7 +85,6 @@ let g:indent_guides_color_change_percent = 3 | |||
let g:indent_guides_enable_on_vim_startup = 1 | |||
let test#strategy = "dispatch" | |||
let g:ctrlp_custom_ignore = { | |||
\ 'dir': '\.git$\|\.yardoc\|node_modules\|log\|tmp$', | |||
\ 'file': '\.so$\|\.dat$|\.DS_Store|\.mp4$' | |||
@@ -31,3 +31,8 @@ vim-surround | |||
vim-svelte | |||
vim-test | |||
vim-vue-syntastic | |||
add: | |||
vim-json | |||
@@ -4,16 +4,23 @@ const { User } = require('../lib/models'); | |||
const { log } = require('../lib/logging'); | |||
const assert = require('assert'); | |||
const { PORT, NODE_ENV, LOG_ALL } = process.env; | |||
const { NODE_ENV } = process.env; | |||
const dev = NODE_ENV === 'development'; | |||
const login = (req, res) => { | |||
try { | |||
assert(req.session.$session !== undefined, 'Login received a session without a $session added on.'); | |||
req.session.$session.user = req.user; // NOTE: user should be cleaned | |||
// kind of dumb to do this here too but otherwise we can't have | |||
// direct API tests and usage without first accessing sapper | |||
req.session.$session = req.session.$session || {development: dev}; | |||
req.session.$session.user = req.user; | |||
req.session.$session.development = dev; | |||
// assert the user is cleaned of sensitive stuff | |||
assert(req.user.password === undefined, "The password field is in the exposed user and should not be."); | |||
res.end(JSON.stringify({'message': 'OK', '$session': req.session.$session})); | |||
} catch(error) { | |||
log.error(error, "login"); | |||
} | |||
@@ -24,6 +24,7 @@ | |||
"cross-fetch": "^3.0.4", | |||
"dayjs": "^1.8.34", | |||
"email-deep-validator": "^3.3.0", | |||
"eslint": "^7.10.0", | |||
"express-session": "^1.17.0", | |||
"form-urlencoded": "^4.1.3", | |||
"html-pdf": "^2.2.0", | |||
@@ -60,6 +61,8 @@ | |||
"@rollup/plugin-node-resolve": "^7.1.1", | |||
"@rollup/plugin-replace": "^2.3.1", | |||
"@rollup/pluginutils": "^3.0.8", | |||
"axios": "^0.20.0", | |||
"axios-cookiejar-support": "^1.0.0", | |||
"chalk": "^4.0.0", | |||
"chromedriver": "^80.0.1", | |||
"commander": "^5.1.0", | |||
@@ -99,6 +102,7 @@ | |||
"svelte": "^3.20.1", | |||
"svelte-preprocess": "^3.5.0", | |||
"swagger-editor-dist": "^3.11.1", | |||
"tough-cookie": "^4.0.0", | |||
"wait-on": "^5.0.0", | |||
"watch": "^1.0.2", | |||
"webtorrent": "^0.108.1", | |||
@@ -1,6 +1,6 @@ | |||
const Queue = require('bull'); | |||
const secrets = require('../../lib/secrets'); | |||
const { User, Auth, knex } = require('../../lib/models'); | |||
const { User } = require('../../lib/models'); | |||
const { log } = require('../../lib/logging'); | |||
const assert = require('assert'); | |||
const { transporter, template } = require('../../lib/mail'); | |||
@@ -48,7 +48,7 @@ const send_email = (mail_opts) => { | |||
const send_reg_email = (to, user) => { | |||
send_email({ | |||
to: to, | |||
to, | |||
from: '"Zed from LJSTHW" help@learnjsthehardway.com', | |||
subject: 'Registration request from Learn JS The Hard Way', | |||
text: register_email.text({user, company}), | |||
@@ -59,7 +59,7 @@ const send_reg_email = (to, user) => { | |||
const send_reset_finished_email = (to, username, browser, ip_addr) => { | |||
send_email({ | |||
to: to, | |||
to, | |||
from: '"Zed from LJSTHW" help@learnjsthehardway.com', | |||
subject: 'Password Has Been Changed', | |||
text: reset_finished_email.text({username, company, browser, ip_addr}), | |||
@@ -70,7 +70,7 @@ const send_reset_finished_email = (to, username, browser, ip_addr) => { | |||
const send_reset_email = (to, user, reset_code, browser, ip_addr) => { | |||
send_email({ | |||
to: to, | |||
to, | |||
from: '"Zed from LJSTHW" help@learnjsthehardway.com', | |||
subject: 'Reset Password Request from Learn JS The Hard Way', | |||
text: reset_email.text({user, reset_code, browser, company, ip_addr}), | |||
@@ -0,0 +1,2 @@ | |||
wsl -u root /etc/init.d/redis-server start | |||
wsl -u root /etc/init.d/postgresql start |
@@ -16,6 +16,7 @@ | |||
} | |||
</script> | |||
{#if active} | |||
<div class="modal" class:active={active} class:modal-lg={ large } id="modal-id" data-testid="{testid}"> | |||
<span class="modal-overlay" aria-label="Close" on:click={close_modal}> </span> | |||
<div class="modal-container"> | |||
@@ -41,3 +42,4 @@ | |||
</div> | |||
</div> | |||
</div> | |||
{/if} |
@@ -7,7 +7,9 @@ class SapperApp { | |||
constructor(req, res) { | |||
this.req = req; | |||
this.res = res; | |||
this.session = req.session.$session; | |||
// NOTE: sapper middleware doesn't add a session on REST requests | |||
req.session.$session = req.session.$session || {}; | |||
this.session = req.session.$session | |||
} | |||
logout() { | |||
@@ -1,16 +1,14 @@ | |||
import { log } from 'logging'; | |||
import { restricted } from 'auth'; | |||
import { stores } from 'sabaton'; | |||
export const get = async (req, res) => { | |||
const {$session, $msg, $app} = stores(req, res); | |||
const {$app} = stores(req, res); | |||
$app.logout(); | |||
$app.redirect('/'); | |||
} | |||
export const post = async (req, res) => { | |||
const {$session, $msg, $app} = stores(req, res); | |||
const {$app} = stores(req, res); | |||
$app.logout(); | |||
$app.done({message: "OK"}); | |||
} | |||
@@ -1,7 +1,7 @@ | |||
import { log } from 'logging'; | |||
import { User, Comment } from '../../../lib/models'; | |||
import * as auth from 'auth'; | |||
import assert from 'assert'; | |||
import { Comment } from '../../../lib/models'; | |||
import { log } from 'logging'; | |||
import { stores } from 'sabaton'; | |||
const collect_comments = async ($app, url) => { | |||
@@ -13,7 +13,7 @@ const collect_comments = async ($app, url) => { | |||
export const get = auth.restricted( | |||
async (req, res) => { | |||
const { $session, $msg, $app} = stores(req, res); | |||
const { $app } = stores(req, res); | |||
assert(req.query && req.query.url, "No url parameter provided."); | |||
try { | |||
@@ -26,7 +26,7 @@ export const get = auth.restricted( | |||
export const post = auth.restricted( | |||
async (req, res) => { | |||
const { $session, $msg, $app} = stores(req, res); | |||
const { $msg, $app} = stores(req, res); | |||
try { | |||
assert($msg.url, "A URL is required."); | |||
@@ -1,13 +1,11 @@ | |||
import { log } from 'logging'; | |||
import { User } from '../../../lib/models'; | |||
import * as auth from 'auth'; | |||
import assert from 'assert'; | |||
import { stores } from 'sabaton'; | |||
import { send_invoice } from 'mq'; | |||
export const post = auth.restricted( | |||
async (req, res) => { | |||
const { $session, $msg, $app} = stores(req, res); | |||
const { $msg, $app} = stores(req, res); | |||
try { | |||
send_invoice(req.user.id, $msg.recipient.email, $msg.recipient); | |||
@@ -52,7 +52,7 @@ export const post = async (req, res) => { | |||
$app.error(402, "Payment is stored incorrectly in the database. Email help@learnjsthehardway.com."); | |||
} | |||
} else { | |||
log.error('Paymjents failure at the server', $msg); | |||
log.error('Payments failure at the server', $msg); | |||
$app.error(402, "Payment failure. Please email help@learnjsthehardway.com."); | |||
} | |||
} catch(error) { | |||
@@ -1,20 +1,23 @@ | |||
import { log } from 'logging'; | |||
import { Payment, User } from '../../../../lib/models'; | |||
import * as auth from 'auth'; | |||
import assert from 'assert'; | |||
import { log } from 'logging'; | |||
import { Payment } from '../../../../lib/models'; | |||
import { stores } from 'sabaton'; | |||
/* | |||
* Returns only payments currently since the user is | |||
* already in the $session returned. | |||
*/ | |||
export const get = auth.restricted( | |||
async (req, res) => { | |||
const { $session, $msg, $app} = stores(req, res); | |||
const { $app} = stores(req, res); | |||
try { | |||
const user = {full_name: req.user.full_name, username: req.user.username}; | |||
const payments = await Payment.all({user_id: req.user.id}); | |||
const clean_payments = payments.map(x => ({ created_at: x.created_at, system: x.system}) ); | |||
const clean_payments = payments.map(x => ({ created_at: x.created_at, system: x.system})); | |||
$app.done({user, payments: clean_payments }); | |||
$app.done({payments: clean_payments }); | |||
} catch (error) { | |||
log.error(error); | |||
$app.error(403, 'Unauthorized'); | |||
@@ -25,19 +28,20 @@ export const get = auth.restricted( | |||
export const post = auth.restricted( | |||
async (req, res) => { | |||
const { $session, $msg, $app} = stores(req, res); | |||
const user = req.user; | |||
try { | |||
log.debug('Updating user.'); | |||
// SECURITY: assumes the User.update call will scrub the input for correct values | |||
const count = await req.user.update($msg); | |||
const count = await user.update($msg); | |||
assert(count === 1, `Attempted to update a user but got count ${count} instead of 1.`); | |||
// TODO: why do I have to do this so much? | |||
req.user.full_name = $msg.full_name; | |||
req.user.username = $msg.username; | |||
$session.user.initials = req.user.initials; | |||
user.full_name = $msg.full_name; | |||
user.username = $msg.username; | |||
$session.user.initials = user.initials; | |||
$session.user.full_name = $msg.full_name; | |||
$session.user.username = $msg.username; | |||
@@ -90,7 +90,7 @@ | |||
<label class="form-label" for="email-field"> | |||
Email <Icon name="help-circle" tooltip="Email on this site" tooltip_bottom="true"/> | |||
</label> | |||
<input class="form-input" name="email" type="email" data-testid="email" id="email-field" bind:value={ recipient.email } placeholder="Email Address"> | |||
<input class="form-input" name="email" type="email" data-testid="email-field" id="email-field" bind:value={ recipient.email } placeholder="Email Address"> | |||
<FormError errors={errors.email} visible={show_errors} /> | |||
</div> | |||
@@ -4,11 +4,8 @@ | |||
let res = await this.fetch(`/api/user/settings.json`, {credentials: 'same-origin'}); | |||
let data = await res.json(); | |||
if(data.user) { | |||
return { | |||
full_name: data.user.full_name, password: '', email: '', username: data.user.username, | |||
payments: data.payments | |||
}; | |||
if(data.payments) { | |||
return { payments: data.payments }; | |||
} else { | |||
this.error(500, 'Unable to retreive your user record.'); | |||
} | |||
@@ -34,7 +31,6 @@ | |||
let show_errors = false; | |||
let errors = {}; | |||
export let payments = []; | |||
export let form = { | |||
full_name: user.full_name, password: '', email: '', username: user.username, | |||
}; | |||
@@ -127,7 +123,7 @@ | |||
<FormError visible={show_errors} errors={ errors.full_name } /> | |||
</div> | |||
<div class="form-group" class:has-error={ show_errors && errors.email }> | |||
<div class="form-group" data-testid="email-group" class:has-error={ show_errors && errors.email }> | |||
<label class="form-label" for="email-field"> | |||
Email (Obfuscated) <a alt="Read the Privacy Policy" aria-label="Read the Privacy Policy" href="/privacy" on:click|preventDefault={toggle_privacy}><Icon name="help-circle" tooltip="Read the Radical Privacy Policy" /></a> | |||
</label> | |||
@@ -0,0 +1,48 @@ | |||
module.exports = { | |||
apps: [ | |||
{ | |||
name: 'auth', | |||
script: './scripts/services/auth.js', | |||
instances: 1, | |||
autorestart: true, | |||
watch: ["lib", "scripts/services", "src/email"], | |||
max_memory_restart: '1G', | |||
env: { | |||
NODE_ENV: 'development' | |||
}, | |||
env_production: { | |||
NODE_ENV: 'production' | |||
} | |||
}, | |||
{ | |||
name: 'invoipt', | |||
script: './scripts/services/invoipt.js', | |||
instances: 1, | |||
autorestart: true, | |||
watch: ["lib", "scripts/services", "src/email"], | |||
max_memory_restart: '1G', | |||
env: { | |||
NODE_ENV: 'development' | |||
}, | |||
env_production: { | |||
NODE_ENV: 'production' | |||
} | |||
}, | |||
{ | |||
name: 'tracker', | |||
script: './scripts/services/tracker.js', | |||
instances: 1, | |||
autorestart: true, | |||
watch: ["lib", "scripts/services"], | |||
max_memory_restart: '1G', | |||
env: { | |||
NODE_ENV: 'development' | |||
}, | |||
env_production: { | |||
NODE_ENV: 'production' | |||
} | |||
}, | |||
], | |||
}; | |||