client: fetch configurations from server at runtime
Permissions, regex filters, app title, email info, and safety now fetched using server's Info API
This commit is contained in:
parent
2bf361c64a
commit
3972b902d8
21 changed files with 137 additions and 91 deletions
|
@ -8,6 +8,7 @@ const progress = require('./util/progress.js');
|
|||
const uri = require('./util/uri.js');
|
||||
|
||||
let fileTokens = {};
|
||||
let remoteConfig = null;
|
||||
|
||||
class Api extends events.EventTarget {
|
||||
constructor() {
|
||||
|
@ -65,14 +66,53 @@ class Api extends events.EventTarget {
|
|||
return this._wrappedRequest(url, request.delete, data, {}, options);
|
||||
}
|
||||
|
||||
fetchConfig() {
|
||||
if (remoteConfig === null) {
|
||||
return this.get(uri.formatApiLink('info'))
|
||||
.then(response => {
|
||||
remoteConfig = response.config;
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
getName() {
|
||||
return remoteConfig.name;
|
||||
}
|
||||
|
||||
getTagNameRegex() {
|
||||
return remoteConfig.tagNameRegex;
|
||||
}
|
||||
|
||||
getPasswordRegex() {
|
||||
return remoteConfig.passwordRegex;
|
||||
}
|
||||
|
||||
getUserNameRegex() {
|
||||
return remoteConfig.userNameRegex;
|
||||
}
|
||||
|
||||
getContactEmail() {
|
||||
return remoteConfig.contactEmail;
|
||||
}
|
||||
|
||||
canSendMails() {
|
||||
return !!remoteConfig.canSendMails;
|
||||
}
|
||||
|
||||
safetyEnabled() {
|
||||
return !!remoteConfig.enableSafety;
|
||||
}
|
||||
|
||||
hasPrivilege(lookup) {
|
||||
let minViableRank = null;
|
||||
for (let privilege of Object.keys(config.privileges)) {
|
||||
if (!privilege.startsWith(lookup)) {
|
||||
for (let p of Object.keys(remoteConfig.privileges)) {
|
||||
if (!p.startsWith(lookup)) {
|
||||
continue;
|
||||
}
|
||||
const rankName = config.privileges[privilege];
|
||||
const rankIndex = this.allRanks.indexOf(rankName);
|
||||
const rankIndex = this.allRanks.indexOf(
|
||||
remoteConfig.privileges[p]);
|
||||
if (minViableRank === null || rankIndex < minViableRank) {
|
||||
minViableRank = rankIndex;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ class HomeController {
|
|||
topNavigation.setTitle('Home');
|
||||
|
||||
this._homeView = new HomeView({
|
||||
name: config.name,
|
||||
name: api.getName(),
|
||||
version: config.meta.version,
|
||||
buildDate: config.meta.buildDate,
|
||||
canListSnapshots: api.hasPrivilege('snapshots:list'),
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
'use strict';
|
||||
|
||||
const config = require('../config.js');
|
||||
const router = require('../router.js');
|
||||
const api = require('../api.js');
|
||||
const settings = require('../models/settings.js');
|
||||
|
@ -33,9 +32,9 @@ class PostListController {
|
|||
this._headerView = new PostsHeaderView({
|
||||
hostNode: this._pageController.view.pageHeaderHolderNode,
|
||||
parameters: ctx.parameters,
|
||||
enableSafety: config.enableSafety,
|
||||
canBulkEditTags: api.hasPrivilege('posts:bulkEdit:tags'),
|
||||
canBulkEditSafety: api.hasPrivilege('posts:bulkEdit:safety'),
|
||||
enableSafety: api.safetyEnabled(),
|
||||
canBulkEditTags: api.hasPrivilege('posts:bulk-edit:tags'),
|
||||
canBulkEditSafety: api.hasPrivilege('posts:bulk-edit:safety'),
|
||||
bulkEdit: {
|
||||
tags: this._bulkEditTags
|
||||
},
|
||||
|
@ -97,9 +96,9 @@ class PostListController {
|
|||
pageRenderer: pageCtx => {
|
||||
Object.assign(pageCtx, {
|
||||
canViewPosts: api.hasPrivilege('posts:view'),
|
||||
canBulkEditTags: api.hasPrivilege('posts:bulkEdit:tags'),
|
||||
canBulkEditTags: api.hasPrivilege('posts:bulk-edit:tags'),
|
||||
canBulkEditSafety:
|
||||
api.hasPrivilege('posts:bulkEdit:safety'),
|
||||
api.hasPrivilege('posts:bulk-edit:safety'),
|
||||
bulkEdit: {
|
||||
tags: this._bulkEditTags,
|
||||
},
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const api = require('../api.js');
|
||||
const config = require('../config.js');
|
||||
const router = require('../router.js');
|
||||
const uri = require('../util/uri.js');
|
||||
const misc = require('../util/misc.js');
|
||||
|
@ -31,7 +30,7 @@ class PostUploadController {
|
|||
this._view = new PostUploadView({
|
||||
canUploadAnonymously: api.hasPrivilege('posts:create:anonymous'),
|
||||
canViewPosts: api.hasPrivilege('posts:view'),
|
||||
enableSafety: config.enableSafety,
|
||||
enableSafety: api.safetyEnabled(),
|
||||
});
|
||||
this._view.addEventListener('change', e => this._evtChange(e));
|
||||
this._view.addEventListener('submit', e => this._evtSubmit(e));
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
'use strict';
|
||||
|
||||
const api = require('../api.js');
|
||||
const config = require('../config.js');
|
||||
const topNavigation = require('../models/top_navigation.js');
|
||||
const TopNavigationView = require('../views/top_navigation_view.js');
|
||||
|
||||
class TopNavigationController {
|
||||
constructor() {
|
||||
this._topNavigationView = new TopNavigationView();
|
||||
api.fetchConfig().then(() => {
|
||||
this._topNavigationView = new TopNavigationView();
|
||||
|
||||
topNavigation.addEventListener(
|
||||
'activate', e => this._evtActivate(e));
|
||||
topNavigation.addEventListener(
|
||||
'activate', e => this._evtActivate(e));
|
||||
|
||||
api.addEventListener('login', e => this._evtAuthChange(e));
|
||||
api.addEventListener('logout', e => this._evtAuthChange(e));
|
||||
api.addEventListener('login', e => this._evtAuthChange(e));
|
||||
api.addEventListener('logout', e => this._evtAuthChange(e));
|
||||
|
||||
this._render();
|
||||
this._render();
|
||||
});
|
||||
}
|
||||
|
||||
_evtAuthChange(e) {
|
||||
|
@ -65,7 +66,7 @@ class TopNavigationController {
|
|||
this._updateNavigationFromPrivileges();
|
||||
this._topNavigationView.render({
|
||||
items: topNavigation.getAll(),
|
||||
name: config.name
|
||||
name: api.getName()
|
||||
});
|
||||
this._topNavigationView.activate(
|
||||
topNavigation.activeItem ? topNavigation.activeItem.key : '');
|
||||
|
|
|
@ -4,7 +4,6 @@ const router = require('../router.js');
|
|||
const api = require('../api.js');
|
||||
const uri = require('../util/uri.js');
|
||||
const misc = require('../util/misc.js');
|
||||
const config = require('../config.js');
|
||||
const views = require('../util/views.js');
|
||||
const User = require('../models/user.js');
|
||||
const UserToken = require('../models/user_token.js');
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const api = require('../api.js');
|
||||
const config = require('../config.js');
|
||||
const events = require('../events.js');
|
||||
const misc = require('../util/misc.js');
|
||||
const views = require('../util/views.js');
|
||||
|
@ -26,7 +25,7 @@ class PostEditSidebarControl extends events.EventTarget {
|
|||
|
||||
views.replaceContent(this._hostNode, template({
|
||||
post: this._post,
|
||||
enableSafety: config.enableSafety,
|
||||
enableSafety: api.safetyEnabled(),
|
||||
hasClipboard: document.queryCommandSupported('copy'),
|
||||
canEditPostSafety: api.hasPrivilege('posts:edit:safety'),
|
||||
canEditPostSource: api.hasPrivilege('posts:edit:source'),
|
||||
|
|
|
@ -21,7 +21,7 @@ class PostReadonlySidebarControl extends events.EventTarget {
|
|||
|
||||
views.replaceContent(this._hostNode, template({
|
||||
post: this._post,
|
||||
enableSafety: config.enableSafety,
|
||||
enableSafety: api.safetyEnabled(),
|
||||
canListPosts: api.hasPrivilege('posts:list'),
|
||||
canEditPosts: api.hasPrivilege('posts:edit'),
|
||||
canViewTags: api.hasPrivilege('tags:view'),
|
||||
|
|
|
@ -26,46 +26,51 @@ router.enter(
|
|||
next();
|
||||
});
|
||||
|
||||
// register controller routes
|
||||
let controllers = [];
|
||||
controllers.push(require('./controllers/home_controller.js'));
|
||||
controllers.push(require('./controllers/help_controller.js'));
|
||||
controllers.push(require('./controllers/auth_controller.js'));
|
||||
controllers.push(require('./controllers/password_reset_controller.js'));
|
||||
controllers.push(require('./controllers/comments_controller.js'));
|
||||
controllers.push(require('./controllers/snapshots_controller.js'));
|
||||
controllers.push(require('./controllers/post_detail_controller.js'));
|
||||
controllers.push(require('./controllers/post_main_controller.js'));
|
||||
controllers.push(require('./controllers/post_list_controller.js'));
|
||||
controllers.push(require('./controllers/post_upload_controller.js'));
|
||||
controllers.push(require('./controllers/tag_controller.js'));
|
||||
controllers.push(require('./controllers/tag_list_controller.js'));
|
||||
controllers.push(require('./controllers/tag_categories_controller.js'));
|
||||
controllers.push(require('./controllers/settings_controller.js'));
|
||||
controllers.push(require('./controllers/user_controller.js'));
|
||||
controllers.push(require('./controllers/user_list_controller.js'));
|
||||
controllers.push(require('./controllers/user_registration_controller.js'));
|
||||
|
||||
// 404 controller needs to be registered last
|
||||
controllers.push(require('./controllers/not_found_controller.js'));
|
||||
|
||||
for (let controller of controllers) {
|
||||
controller(router);
|
||||
}
|
||||
|
||||
const tags = require('./tags.js');
|
||||
const api = require('./api.js');
|
||||
tags.refreshCategoryColorMap(); // we don't care about errors
|
||||
api.loginFromCookies().then(() => {
|
||||
router.start();
|
||||
}, error => {
|
||||
if (window.location.href.indexOf('login') !== -1) {
|
||||
api.forget();
|
||||
|
||||
api.fetchConfig().then(() => {
|
||||
// register controller routes
|
||||
let controllers = [];
|
||||
controllers.push(require('./controllers/home_controller.js'));
|
||||
controllers.push(require('./controllers/help_controller.js'));
|
||||
controllers.push(require('./controllers/auth_controller.js'));
|
||||
controllers.push(require('./controllers/password_reset_controller.js'));
|
||||
controllers.push(require('./controllers/comments_controller.js'));
|
||||
controllers.push(require('./controllers/snapshots_controller.js'));
|
||||
controllers.push(require('./controllers/post_detail_controller.js'));
|
||||
controllers.push(require('./controllers/post_main_controller.js'));
|
||||
controllers.push(require('./controllers/post_list_controller.js'));
|
||||
controllers.push(require('./controllers/post_upload_controller.js'));
|
||||
controllers.push(require('./controllers/tag_controller.js'));
|
||||
controllers.push(require('./controllers/tag_list_controller.js'));
|
||||
controllers.push(require('./controllers/tag_categories_controller.js'));
|
||||
controllers.push(require('./controllers/settings_controller.js'));
|
||||
controllers.push(require('./controllers/user_controller.js'));
|
||||
controllers.push(require('./controllers/user_list_controller.js'));
|
||||
controllers.push(require('./controllers/user_registration_controller.js'));
|
||||
|
||||
// 404 controller needs to be registered last
|
||||
controllers.push(require('./controllers/not_found_controller.js'));
|
||||
|
||||
for (let controller of controllers) {
|
||||
controller(router);
|
||||
}
|
||||
}, error => {
|
||||
window.alert('Could not fetch basic configuration from server');
|
||||
}).then(() => {
|
||||
api.loginFromCookies().then(() => {
|
||||
router.start();
|
||||
} else {
|
||||
const ctx = router.start('/');
|
||||
ctx.controller.showError(
|
||||
'An error happened while trying to log you in: ' +
|
||||
error.message);
|
||||
}
|
||||
});
|
||||
}, error => {
|
||||
if (window.location.href.indexOf('login') !== -1) {
|
||||
api.forget();
|
||||
router.start();
|
||||
} else {
|
||||
const ctx = router.start('/');
|
||||
ctx.controller.showError(
|
||||
'An error happened while trying to log you in: ' +
|
||||
error.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -37,7 +37,7 @@ class PostList extends AbstractList {
|
|||
static _decorateSearchQuery(text) {
|
||||
const browsingSettings = settings.get();
|
||||
const disabledSafety = [];
|
||||
if (config.enableSafety) {
|
||||
if (api.safetyEnabled()) {
|
||||
for (let key of Object.keys(browsingSettings.listPosts)) {
|
||||
if (browsingSettings.listPosts[key] === false) {
|
||||
disabledSafety.push(key);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
const config = require('../config.js');
|
||||
const events = require('../events.js');
|
||||
const api = require('../api.js');
|
||||
|
||||
class TopNavigationItem {
|
||||
constructor(accessKey, title, url, available, imageUrl) {
|
||||
|
@ -53,8 +53,10 @@ class TopNavigation extends events.EventTarget {
|
|||
}
|
||||
|
||||
setTitle(title) {
|
||||
document.oldTitle = null;
|
||||
document.title = config.name + (title ? (' – ' + title) : '');
|
||||
api.fetchConfig().then(() => {
|
||||
document.oldTitle = null;
|
||||
document.title = api.getName() + (title ? (' – ' + title) : '');
|
||||
});
|
||||
}
|
||||
|
||||
showAll() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const config = require('../config.js');
|
||||
const api = require('../api.js');
|
||||
const views = require('../util/views.js');
|
||||
|
||||
const template = views.getTemplate('help');
|
||||
|
@ -26,7 +26,7 @@ class HelpView {
|
|||
|
||||
const sourceNode = template();
|
||||
const ctx = {
|
||||
name: config.name,
|
||||
name: api.getName(),
|
||||
};
|
||||
|
||||
section = section || 'about';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
const config = require('../config.js');
|
||||
const events = require('../events.js');
|
||||
const api = require('../api.js');
|
||||
const views = require('../util/views.js');
|
||||
|
||||
const template = views.getTemplate('login');
|
||||
|
@ -12,15 +12,15 @@ class LoginView extends events.EventTarget {
|
|||
this._hostNode = document.getElementById('content-holder');
|
||||
|
||||
views.replaceContent(this._hostNode, template({
|
||||
userNamePattern: config.userNameRegex,
|
||||
passwordPattern: config.passwordRegex,
|
||||
canSendMails: config.canSendMails,
|
||||
userNamePattern: api.getUserNameRegex(),
|
||||
passwordPattern: api.getPasswordRegex(),
|
||||
canSendMails: api.canSendMails(),
|
||||
}));
|
||||
views.syncScrollPosition();
|
||||
|
||||
views.decorateValidator(this._formNode);
|
||||
this._userNameInputNode.setAttribute('pattern', config.userNameRegex);
|
||||
this._passwordInputNode.setAttribute('pattern', config.passwordRegex);
|
||||
this._userNameInputNode.setAttribute('pattern', api.getUserNameRegex());
|
||||
this._passwordInputNode.setAttribute('pattern', api.getPasswordRegex());
|
||||
this._formNode.addEventListener('submit', e => {
|
||||
e.preventDefault();
|
||||
this.dispatchEvent(new CustomEvent('submit', {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
'use strict';
|
||||
|
||||
const config = require('../config.js');
|
||||
const views = require('../util/views.js');
|
||||
|
||||
const template = views.getTemplate('not-found');
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
const config = require('../config.js');
|
||||
const events = require('../events.js');
|
||||
const api = require('../api.js');
|
||||
const views = require('../util/views.js');
|
||||
|
||||
const template = views.getTemplate('password-reset');
|
||||
|
@ -12,8 +12,8 @@ class PasswordResetView extends events.EventTarget {
|
|||
this._hostNode = document.getElementById('content-holder');
|
||||
|
||||
views.replaceContent(this._hostNode, template({
|
||||
canSendMails: config.canSendMails,
|
||||
contactEmail: config.contactEmail,
|
||||
canSendMails: api.canSendMails(),
|
||||
contactEmail: api.getContactEmail(),
|
||||
}));
|
||||
views.syncScrollPosition();
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
'use strict';
|
||||
|
||||
const config = require('../config.js');
|
||||
const events = require('../events.js');
|
||||
const views = require('../util/views.js');
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
const config = require('../config.js');
|
||||
const events = require('../events.js');
|
||||
const api = require('../api.js');
|
||||
const views = require('../util/views.js');
|
||||
|
||||
const template = views.getTemplate('user-registration');
|
||||
|
@ -11,8 +11,8 @@ class RegistrationView extends events.EventTarget {
|
|||
super();
|
||||
this._hostNode = document.getElementById('content-holder');
|
||||
views.replaceContent(this._hostNode, template({
|
||||
userNamePattern: config.userNameRegex,
|
||||
passwordPattern: config.passwordRegex,
|
||||
userNamePattern: api.getUserNameRegex(),
|
||||
passwordPattern: api.getPasswordRegex(),
|
||||
}));
|
||||
views.syncScrollPosition();
|
||||
views.decorateValidator(this._formNode);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
const config = require('../config.js');
|
||||
const events = require('../events.js');
|
||||
const api = require('../api.js');
|
||||
const misc = require('../util/misc.js');
|
||||
const views = require('../util/views.js');
|
||||
const TagInputControl = require('../controls/tag_input_control.js');
|
||||
|
@ -64,7 +64,7 @@ class TagEditView extends events.EventTarget {
|
|||
}
|
||||
|
||||
_evtNameInput(e) {
|
||||
const regex = new RegExp(config.tagNameRegex);
|
||||
const regex = new RegExp(api.getTagNameRegex());
|
||||
const list = misc.splitByWhitespace(this._namesFieldNode.value);
|
||||
|
||||
if (!list.length) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
const config = require('../config.js');
|
||||
const events = require('../events.js');
|
||||
const api = require('../api.js');
|
||||
const views = require('../util/views.js');
|
||||
const TagAutoCompleteControl =
|
||||
require('../controls/tag_auto_complete_control.js');
|
||||
|
@ -14,7 +14,7 @@ class TagMergeView extends events.EventTarget {
|
|||
|
||||
this._tag = ctx.tag;
|
||||
this._hostNode = ctx.hostNode;
|
||||
ctx.tagNamePattern = config.tagNameRegex;
|
||||
ctx.tagNamePattern = api.getTagNameRegex();
|
||||
views.replaceContent(this._hostNode, template(ctx));
|
||||
|
||||
views.decorateValidator(this._formNode);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
const config = require('../config.js');
|
||||
const events = require('../events.js');
|
||||
const api = require('../api.js');
|
||||
const views = require('../util/views.js');
|
||||
const FileDropperControl = require('../controls/file_dropper_control.js');
|
||||
|
||||
|
@ -11,8 +11,8 @@ class UserEditView extends events.EventTarget {
|
|||
constructor(ctx) {
|
||||
super();
|
||||
|
||||
ctx.userNamePattern = config.userNameRegex + /|^$/.source;
|
||||
ctx.passwordPattern = config.passwordRegex + /|^$/.source;
|
||||
ctx.userNamePattern = api.getUserNameRegex() + /|^$/.source;
|
||||
ctx.passwordPattern = api.getPasswordRegex() + /|^$/.source;
|
||||
|
||||
this._user = ctx.user;
|
||||
this._hostNode = ctx.hostNode;
|
||||
|
|
|
@ -35,11 +35,15 @@ def get_info(
|
|||
'diskUsage': _get_disk_usage(),
|
||||
'serverTime': datetime.utcnow(),
|
||||
'config': {
|
||||
'name': config.config['name'],
|
||||
'userNameRegex': config.config['user_name_regex'],
|
||||
'passwordRegex': config.config['password_regex'],
|
||||
'tagNameRegex': config.config['tag_name_regex'],
|
||||
'tagCategoryNameRegex': config.config['tag_category_name_regex'],
|
||||
'defaultUserRank': config.config['default_rank'],
|
||||
'enableSafety': config.config['enable_safety'],
|
||||
'contactEmail': config.config['contactEmail'],
|
||||
'canSendMails': bool(config.config['smtp']['host']),
|
||||
'privileges':
|
||||
util.snake_case_to_lower_camel_case_keys(
|
||||
config.config['privileges']),
|
||||
|
|
Loading…
Reference in a new issue