Merge branch 'develop' of git.digitaltelepresence.com:digital-telepresence/dtp-base into develop

master
rob 2 years ago
commit 863433eb27

@ -33,6 +33,8 @@ class CoreNodeController extends SiteController {
router.get('/:coreNodeId', this.getCoreNodeView.bind(this));
router.get('/', this.getIndex.bind(this));
router.delete('/:coreNodeId', this.deleteCoreNode.bind(this));
return router;
}
@ -137,6 +139,23 @@ class CoreNodeController extends SiteController {
return next(error);
}
}
async deleteCoreNode (req, res) {
const { coreNode: coreNodeService } = this.dtp.services;
try {
await coreNodeService.disconnect(res.locals.coreNode);
const displayList = this.createDisplayList('core-disconnect');
displayList.navigateTo('/admin/core-node');
res.status(200).json({ success: true, displayList });
} catch (error) {
this.log.error('failed to disconnect from Core', { error });
return res.status(error.statusCode || 500).json({
success: false,
message: error.message,
});
}
}
}
module.exports = {

@ -20,6 +20,7 @@ const KaleidoscopeEventSchema = new Schema({
href: { type: String },
thumbnail: { type: String },
source: {
client: { type: Schema.ObjectId, index: 1, ref: 'OAuth2Client' },
pkg: {
name: { type: String, required: true },
version: { type: String, required: true },

@ -9,11 +9,17 @@ const Schema = mongoose.Schema;
module.exports.DTP_THEME_LIST = ['dtp-light', 'dtp-dark'];
module.exports.DTP_USER_TYPE_LIST = ['CoreUser', 'User'];
module.exports.DtpUserSchema = new Schema({
userType: { type: String, enum: module.exports.DTP_USER_TYPE_LIST, required: true },
user: { type: Schema.ObjectId, required: true, index: true, refPath: 'userType' },
}, { _id: false });
module.exports.UserFlagsSchema = new Schema({
isAdmin: { type: Boolean, default: false, required: true },
isModerator: { type: Boolean, default: false, required: true },
isEmailVerified: { type: Boolean, default: false, required: true },
});
}, { _id: false });
module.exports.UserPermissionsSchema = new Schema({
canLogin: { type: Boolean, default: true, required: true },
@ -24,9 +30,9 @@ module.exports.UserPermissionsSchema = new Schema({
canAuthorPosts: { type: Boolean, default: false, required: true },
canPublishPages: { type: Boolean, default: false, required: true },
canPublishPosts: { type: Boolean, default: false, required: true },
});
}, { _id: false });
module.exports.UserOptInSchema = new Schema({
system: { type: Boolean, default: true, required: true },
marketing: { type: Boolean, default: true, required: true },
});
}, { _id: false });

@ -7,9 +7,11 @@
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const { DtpUserSchema } = require('./lib/user-types.js');
const UserBlockSchema = new Schema({
user: { type: Schema.ObjectId, required: true, index: true, ref: 'User' },
blockedUsers: { type: [Schema.ObjectId], ref: 'User' },
member: { type: DtpUserSchema, required: true },
blockedMembers: { type: [DtpUserSchema] },
});
module.exports = mongoose.model('UserBlock', UserBlockSchema);

@ -61,6 +61,12 @@ class CoreNodeService extends SiteService {
async start ( ) {
await super.start();
const https = require('https');
this.httpsAgent = new https.Agent({
// read it out-loud: Reject unauthorized when not the 'local' environment.
rejectUnauthorized: (process.env.NODE_ENV !== 'local'),
});
const cores = await this.getConnectedCores(null, true);
this.log.info('Core Node service starting', { connectedCoreCount: cores.length });
@ -187,7 +193,7 @@ class CoreNodeService extends SiteService {
url: '/core/info/package',
});
await CoreNode.updateOne(
core = await CoreNode.findOneAndUpdate(
{ _id: core._id },
{
$set: {
@ -201,10 +207,11 @@ class CoreNodeService extends SiteService {
'meta.supportEmail': txSite.response.site.supportEmail,
},
},
{ new: true },
);
core = await CoreNode.findOne({ _id: core._id }).lean();
this.log.info('resolved Core node', { core });
this.emitDtpEvent('resolve', { core, host });
return { core, networkPolicy: txSite.response.site.networkPolicy };
}
@ -257,6 +264,8 @@ class CoreNodeService extends SiteService {
body: { event },
};
await this.emitDtpEvent('kaleidoscope.event', { event, recipients, request });
if (!recipients) {
return this.broadcast(request);
}
@ -303,6 +312,7 @@ class CoreNodeService extends SiteService {
async broadcast (request) {
const results = [ ];
await this.emitDtpEvent('kaleidoscope.broadcast', { request });
await CoreNode
.find({
'flags.isConnected': true,
@ -327,6 +337,7 @@ class CoreNodeService extends SiteService {
try {
const req = new CoreNodeRequest();
const options = {
agent: this.httpsAgent,
headers: {
'Content-Type': 'application/json',
},
@ -353,8 +364,10 @@ class CoreNodeService extends SiteService {
options.body = JSON.stringify(request.body);
}
this.log.info('sending Core node request', { request: req });
const requestUrl = this.getCoreRequestUrl(core, request.url);
await this.emitDtpEvent('kaleidoscope.request', { core, request, requestUrl });
this.log.info('sending Core node request', { request: req });
const response = await fetch(requestUrl, options);
if (!response.ok) {
let json;
@ -384,6 +397,10 @@ class CoreNodeService extends SiteService {
async setRequestResponse (request, response, json) {
const DONE = new Date();
const ELAPSED = DONE.valueOf() - request.created.valueOf();
/*
* Build the default update operation
*/
const updateOp = {
$set: {
'response.received': DONE,
@ -391,9 +408,20 @@ class CoreNodeService extends SiteService {
'response.statusCode': response.status,
},
};
if (json) {
updateOp.$set['response.success'] = json.success;
}
/*
* Provide an opportunity for anything to alter the operation or cancel it.
*/
await this.emitDtpEvent('kaleidoscope.response', {
core: request.core,
request, response, json,
updateOp,
});
await CoreNodeRequest.updateOne({ _id: request._id }, updateOp);
}
@ -433,6 +461,52 @@ class CoreNodeService extends SiteService {
},
},
);
await this.emitDtpEvent('connect', { core: request.core, request });
}
async disconnect (core) {
this.log.alert('disconnecting from Core', {
name: core.meta.name,
domain: core.meta.domain,
});
// provides an abort point if any listener throws
await this.emitDtpEvent('disconnect-pre', { core });
const disconnect = await this.sendRequest(core, {
method: 'DELETE',
url: `/core/connect/node/${core.oauth.clientId}`,
});
this.log.alert('Core disconnect request complete', {
name: core.meta.name,
domain: core.meta.domain,
disconnect,
});
try {
await this.emitDtpEvent('disconnect-post', { core, disconnect });
} catch (error) {
this.log.error('failed to emit dtp.core.disconnect-post', { error });
// keep going
}
await CoreUser
.find({ core: core._id })
.cursor()
.eachAsync(this.removeUser.bind(this, core), 1);
// await CoreNodeConnect.deleteMany({ 'site.domainKey': core.meta.domainKey });
// await CoreNodeRequest.deleteMany({ core: core._id });
try {
await this.emitDtpEvent('disconnect', { core, disconnect });
} catch (error) {
this.log.error('failed to emit dtp.core.disconnect', { error });
// keep going
}
return disconnect;
}
async queueServiceNodeConnect (requestToken, appNode) {
@ -478,7 +552,10 @@ class CoreNodeService extends SiteService {
await request.save();
return request.toObject();
request = request.toObject();
await this.emitDtpEvent('service-node.connect', { request });
return request;
}
async getServiceNodeQueue (pagination) {
@ -498,7 +575,6 @@ class CoreNodeService extends SiteService {
return request;
}
async acceptServiceNode (requestToken, appNode) {
const { oauth2: oauth2Service } = this.dtp.services;
const response = { token: requestToken };
@ -506,13 +582,15 @@ class CoreNodeService extends SiteService {
this.log.info('accepting app node', { requestToken, appNode });
response.client = await oauth2Service.createClient(appNode.site);
await this.emitDtpEvent('service-node.accept', { client: response.client });
return response;
}
async setCoreOAuth2Credentials (core, credentials) {
const { client } = credentials;
this.log.info('updating Core Connect credentials', { core, client });
await CoreNode.updateOne(
core = await CoreNode.findOneAndUpdate(
{ _id: core._id },
{
$set: {
@ -524,7 +602,9 @@ class CoreNodeService extends SiteService {
'kaleidoscope.token': client.kaleidoscope.token,
},
},
{ new: true },
);
await this.emitDtpEvent('set-oauth2-credentials', { core });
}
registerPassportCoreOAuth2 (core) {
@ -591,6 +671,7 @@ class CoreNodeService extends SiteService {
);
user = user.toObject();
user.type = 'CoreUser';
this.emitDtpEvent('user.login', { user });
return cb(null, user);
} catch (error) {
return cb(error);
@ -713,6 +794,11 @@ class CoreNodeService extends SiteService {
},
);
}
async removeUser (core, user) {
this.log.alert('remove Core user', { core: core.meta.name, user: user.username });
await this.emitDtpEvent('user.remove', { core, user });
}
}
module.exports = {

@ -7,6 +7,8 @@
const mongoose = require('mongoose');
const UserSubscription = mongoose.model('UserSubscription');
const UserNotification = mongoose.model('UserSubscription');
const KaleidoscopeEvent = mongoose.model('KaleidoscopeEvent');
const slug = require('slug');
@ -20,6 +22,25 @@ class HiveService extends SiteService {
super(dtp, module.exports);
}
async start ( ) {
const { oauth2: oauth2Service } = this.dtp.services;
this.eventHandlers = {
onOAuth2RemoveClient: this.onOAuth2RemoveClient.bind(this),
};
oauth2Service.on(oauth2Service.getEventName('client.remove'), this.eventHandlers.onOAuth2RemoveClient);
}
async stop ( ) {
const { oauth2: oauth2Service } = this.dtp.services;
oauth2Service.off(oauth2Service.getEventName('client.remove'), this.eventHandlers.onOAuth2RemoveClient);
delete this.eventHandlers.onOAuth2RemoveClient;
delete this.eventHandlers;
}
async subscribe (user, client, emitterId) {
await UserSubscription.updateOne(
{ user: user._id },
@ -85,7 +106,7 @@ class HiveService extends SiteService {
throw new SiteError(403, 'Unknown client domain key');
}
const event = await this.createKaleidoscopeEvent(eventDefinition);
const event = await this.createKaleidoscopeEvent(eventDefinition, client);
await UserSubscription
.find({
'subscriptions.client': client._id,
@ -100,7 +121,7 @@ class HiveService extends SiteService {
this.emit('kaleidoscope:event', event, client);
}
async createKaleidoscopeEvent (eventDefinition) {
async createKaleidoscopeEvent (eventDefinition, sourceClient) {
const NOW = new Date();
/*
@ -185,8 +206,16 @@ class HiveService extends SiteService {
throw new SiteError(406, 'Missing source emitter href');
}
/*
* Create the KaleidoscopeEvent document
*/
const event = new KaleidoscopeEvent();
event.created = NOW;
if (eventDefinition.created) {
event.created = new Date(eventDefinition.created);
} else {
event.created = NOW;
}
if (eventDefinition.recipientType && eventDefinition.recipient) {
event.recipientType = eventDefinition.recipientType;
@ -219,6 +248,10 @@ class HiveService extends SiteService {
},
};
if (sourceClient) {
event.source.client = sourceClient._id;
}
if (eventDefinition.source.emitter) {
event.source.emitter = {
emitterType: striptags(eventDefinition.source.emitter.emitterType),
@ -273,6 +306,27 @@ class HiveService extends SiteService {
.lean();
return { events, totalEventCount };
}
/*
* OAuth2 event handlers
*/
/**
* This event fires when an OAuth2Client is being disconnected and removed by a
* Core, or a client app is being removed from a Service Node. The Hive service
* will remove all KaleidoscopeEvent records created on behalf of the client.
* @param {OAuth2Client} client the client being removed
*/
async onOAuth2RemoveClient (client) {
this.log.alert('removing KaleidoscopeEvent records from OAuth2Client', { clientId: client._id, domain: client.site.domain });
await KaleidoscopeEvent
.find({ 'source.client': client._id })
.cursor()
.eachAsync(async (event) => {
await UserNotification.deleteMany({ event: event._id });
await KaleidoscopeEvent.deleteOne({ _id: event._id });
}, 1);
}
}
module.exports = {

@ -464,6 +464,10 @@ class OAuth2Service extends SiteService {
* @param {OAuth2Client} client the client to be removed
*/
async removeClient (client) {
// provides opportunity to allow or disallow and, if allowed, perform any
// additional cleanup needed when removing a client.
await this.emitDtpEvent('client.remove', client);
this.log.info('removing client', { clientId: client._id, });
await OAuth2Client.deleteOne({ _id: client._id });
}

@ -738,33 +738,38 @@ class UserService extends SiteService {
await User.updateOne({ _id: user._id }, { $unset: { 'picture': '' } });
}
async blockUser (userId, blockedUserId) {
userId = mongoose.Types.ObjectId(userId);
blockedUserId = mongoose.Types.ObjectId(blockedUserId);
if (userId.equals(blockedUserId)) {
async blockUser (user, blockedUser) {
if (user._id.equals(blockedUser._id)) {
throw new SiteError(406, "You can't block yourself");
}
await UserBlock.updateOne(
{ user: userId },
{ 'member.user': user._id },
{
$addToSet: { blockedUsers: blockedUserId },
$addToSet: {
blockedMembers: {
userType: blockedUser.type,
user: blockedUser._id,
},
},
},
{ upsert: true },
);
}
async unblockUser (userId, blockedUserId) {
userId = mongoose.Types.ObjectId(userId);
blockedUserId = mongoose.Types.ObjectId(blockedUserId);
if (userId.equals(blockedUserId)) {
async unblockUser (user, blockedUser) {
if (user._id.equals(blockedUser._id)) {
throw new SiteError(406, "You can't un-block yourself");
}
await UserBlock.updateOne(
{ user: userId },
{ 'member.user': user._id },
{
$removeFromSet: { blockedUsers: blockedUserId },
$removeFromSet: {
blockedUsers: {
userType: blockedUser.type,
user: blockedUser._id,
},
},
},
{ upsert: true },
);
}

@ -5,7 +5,7 @@ block content
.uk-width-expand
h1 Announcements
.uk-width-auto
a(href="/admin/announcement/create").uk-button.dtp-button-primary
a(href="/admin/announcement/create").uk-button.dtp-button-primary.uk-border-rounded
span
i.fas.fa-plus
span.uk-margin-small-left Create
@ -21,7 +21,7 @@ block content
i(class=`fas ${announcement.title.icon.class}`)
span.uk-margin-small-left= announcement.title.content
.uk-width-auto
button(type="button", data-announcement-id= announcement._id, onclick="return dtp.adminApp.deleteAnnouncement(event);").uk-button.dtp-button-danger
button(type="button", data-announcement-id= announcement._id, onclick="return dtp.adminApp.deleteAnnouncement(event);").uk-button.dtp-button-danger.uk-border-rounded
span
i.fas.fa-trash
else

@ -8,20 +8,33 @@ block content
div(uk-grid).uk-grid-small.uk-flex-middle
div(class="uk-width-1-1 uk-width-expand@m")
h1(style="line-height: 1em;") Core Node
div(class="uk-width-1-1 uk-width-auto@m")
.uk-width-auto
a(href=`mailto:${coreNode.meta.supportEmail}?subject=${encodeURIComponent(`Support request from ${site.name}`)}`)
span
i.fas.fa-envelope
span.uk-margin-small-left Email Support
div(class="uk-width-1-1 uk-width-auto@m")
.uk-width-auto
span.uk-label(style="line-height: 1.75em;", class={
'uk-label-success': coreNode.flags.isConnected,
'uk-label-warning': !coreNode.flags.isConnected && !coreNode.flags.isBlocked,
'uk-label-danger': coreNode.flags.isBlocked,
}).no-select= coreNode.flags.isConnected ? 'Connected' : 'Pending'
+renderCoreNodeListItem(coreNode)
.uk-margin
button(
type="button",
data-core={ _id: coreNode._id, name: coreNode.meta.name},
onclick="return dtp.adminApp.disconnectCore(event);",
).uk-button.dtp-button-danger.uk-border-rounded
span
i.fas.fa-window-close
span.uk-margin-small-left Disconnect
.uk-margin
table.uk-table.uk-table-small
thead

@ -21,7 +21,7 @@ block content
data-newsletter-id= newsletter._id,
data-newsletter-title= newsletter.title,
onclick="return dtp.adminApp.deleteNewsletter(event);",
).uk-button.uk-button-danger
).uk-button.uk-button-danger.uk-border-rounded
+renderButtonIcon('fa-trash', 'Delete')
.uk-width-auto
@ -30,7 +30,7 @@ block content
data-newsletter-id= newsletter._id,
data-newsletter-title= newsletter.title,
onclick="return dtp.adminApp.sendNewsletter(event);",
).uk-button.uk-button-default
).uk-button.uk-button-default.uk-border-rounded
+renderButtonIcon('fa-paper-plane', 'Send')
else
div There are no newsletters at this time.

@ -6,40 +6,59 @@ block content
.uk-margin
h1 Job Queue: #{queueName}
div(uk-grid).uk-flex-between
- var pendingJobCount = jobCounts.waiting + jobCounts.delayed + jobCounts.paused + jobCounts.active
.uk-width-auto Total#[br]#{numeral(pendingJobCount).format('0,0')}
.uk-width-auto Waiting#[br]#{numeral(jobCounts.waiting).format('0,0')}
.uk-width-auto Delayed#[br]#{numeral(jobCounts.delayed).format('0,0')}
.uk-width-auto Paused#[br]#{numeral(jobCounts.paused).format('0,0')}
.uk-width-auto Active#[br]#{numeral(jobCounts.active).format('0,0')}
.uk-width-auto Completed#[br]#{numeral(jobCounts.completed).format('0,0')}
.uk-width-auto Failed#[br]#{numeral(jobCounts.failed).format('0,0')}
.uk-width-auto
label.uk-form-label.uk-text-primary Active
.uk-text-large= numeral(jobCounts.active).format('0,0')
.uk-width-auto
label.uk-form-label.uk-text-success Completed
.uk-text-large= numeral(jobCounts.completed).format('0,0')
.uk-width-auto
label.uk-form-label.uk-text-warning Delayed
.uk-text-large= numeral(jobCounts.delayed).format('0,0')
.uk-width-auto
label.uk-form-label.uk-text-danger Failed
.uk-text-large= numeral(jobCounts.failed).format('0,0')
.uk-width-auto
label.uk-form-label.uk-text-muted Waiting
.uk-text-large= numeral(jobCounts.waiting).format('0,0')
.uk-width-auto
label.uk-form-label.uk-text-muted Paused
.uk-text-large= numeral(jobCounts.paused).format('0,0')
div(uk-grid)
div(class="uk-width-1-1 uk-width-1-2@l")
.uk-card.uk-card-default.uk-card-small
.uk-card-header
h3.uk-card-title Active
.uk-card-body
+renderJobQueueJobList(newsletterQueue, jobs.active)
.uk-margin
.uk-card.uk-card-default.uk-card-small
.uk-card-header
h3.uk-card-title Active
.uk-card-body
+renderJobQueueJobList(newsletterQueue, jobs.active)
div(class="uk-width-1-1 uk-width-1-2@l")
.uk-card.uk-card-default.uk-card-small
.uk-card-header
h3.uk-card-title Waiting
.uk-card-body
+renderJobQueueJobList(newsletterQueue, jobs.waiting)
.uk-margin
.uk-card.uk-card-default.uk-card-small
.uk-card-header
h3.uk-card-title Delayed
.uk-card-body
+renderJobQueueJobList(newsletterQueue, jobs.delayed)
div(class="uk-width-1-1 uk-width-1-2@l")
.uk-card.uk-card-default.uk-card-small
.uk-card-header
h3.uk-card-title Delayed
.uk-card-body
+renderJobQueueJobList(newsletterQueue, jobs.delayed)
.uk-margin
.uk-card.uk-card-default.uk-card-small
.uk-card-header
h3.uk-card-title Paused
.uk-card-body
+renderJobQueueJobList(newsletterQueue, jobs.paused)
div(class="uk-width-1-1 uk-width-1-2@l")
.uk-card.uk-card-default.uk-card-small
.uk-card-header
h3.uk-card-title Failed
.uk-card-body
+renderJobQueueJobList(newsletterQueue, jobs.failed)
.uk-margin
.uk-card.uk-card-default.uk-card-small
.uk-card-header
h3.uk-card-title Waiting
.uk-card-body
+renderJobQueueJobList(newsletterQueue, jobs.waiting)
.uk-margin
.uk-card.uk-card-default.uk-card-small
.uk-card-header
h3.uk-card-title Failed
.uk-card-body
+renderJobQueueJobList(newsletterQueue, jobs.failed)

@ -34,6 +34,9 @@ block content
.uk-card-footer
div(uk-grid).uk-flex-right.uk-flex-middle
.uk-width-expand
+renderBackButton()
if feed
.uk-width-auto
button(
@ -41,6 +44,13 @@ block content
data-feed-id= feed._id,
data-feed-title= feed.title,
onclick="return dtp.adminApp.removeNewsroomFeed(event);",
).uk-button.uk-button-danger.uk-border-rounded Remove Feed
).uk-button.uk-button-danger.uk-border-rounded
span
i.fas.fa-trash
span.uk-margin-small-left Remove Feed
.uk-width-auto
button(type="submit").uk-button.uk-button-primary.uk-border-rounded= feed ? 'Update Feed' : 'Add Feed'
button(type="submit").uk-button.uk-button-primary.uk-border-rounded
span
i.fas.fa-save
span.uk-margin-small-left= feed ? 'Update Feed' : 'Add Feed'

@ -28,16 +28,6 @@ block content
legend Featured Embed
textarea(id="featured-embed", name="featuredEmbed", rows="4").uk-textarea.uk-resize-vertical= site.featuredEmbed
fieldset
legend Shing.tv Widget Key
div(uk-grid).uk-grid-small
div(class="uk-width-1-1 uk-width-1-2@m uk-width-1-3@xl")
label(for="shing-channel-slug").uk-form-label Shing.tv Channel Slug
input(id="shing-channel-slug", name="shingChannelSlug", type="text", placeholder="Enter Shing.tv channel slug", value= site.shingChannelSlug).uk-input
div(class="uk-width-1-1 uk-width-1-2@m uk-width-1-3@xl")
label(for="shing-widget-key").uk-form-label Shing.tv Widget Key
input(id="shing-widget-key", name="shingWidgetKey", type="text", placeholder="Enter Shing.tv widget key", value= site.shingWidgetKey).uk-input
fieldset
legend Gab links
div(uk-grid).uk-grid-small
@ -95,4 +85,4 @@ block content
label(for="spreaker-url").uk-form-label Spreaker URL
input(id="spreaker-url", name="spreakerUrl", type="url", placeholder="Enter Spreaker URL", value= site.spreakerUrl).uk-input
button(type="submit").uk-button.dtp-button-primary Save Settings
button(type="submit").uk-button.dtp-button-primary.uk-border-rounded Save Settings

@ -52,7 +52,7 @@ mixin renderCell (label, value, className)
mixin renderBackButton (options)
- options = Object.assign({ includeLabel: true, label: 'Back' }, options)
button(type="button", onclick="window.history.back();").uk-button.uk-button-default
button(type="button", onclick="window.history.back();").uk-button.uk-button-default.uk-border-rounded
span
i.fas.fa-chevron-left
if options.includeLabel

@ -13,13 +13,24 @@ block content
div(uk-grid).uk-flex-center
div(class="uk-width-1-1 uk-width-1-3@m")
.uk-margin-small
a(href="/auth/core").uk-button.uk-button-primary.uk-border-rounded DTP Connect
a(href="/auth/core").uk-button.uk-button-primary.uk-border-rounded
span
i.fas.fa-plug
span.uk-margin-small-left DTP Connect
.uk-text-small Connect using DTP Core
div(class="uk-width-1-1 uk-width-1-3@m")
.uk-margin-small
a(href="/welcome/signup").uk-button.uk-button-secondary.uk-border-rounded Create Account
a(href="/welcome/signup").uk-button.uk-button-secondary.uk-border-rounded
span
i.fas.fa-user-plus
span.uk-margin-small-left Create Account
.uk-text-small Create a local account
div(class="uk-width-1-1 uk-width-1-3@m")
.uk-margin-small
a(href="/welcome/login").uk-button.uk-button-default.uk-border-rounded Sign In
a(href="/welcome/login").uk-button.uk-button-default.uk-border-rounded
span
i.fas.fa-door-open
span.uk-margin-small-left Sign In
.uk-text-small Log in with your local account

@ -32,4 +32,4 @@ block content
.uk-width-auto
a(href="/").uk-text-muted Forgot password
.uk-width-auto
button(type="submit").uk-button.dtp-button-primary Login
button(type="submit").uk-button.dtp-button-primary.uk-border-rounded Login

@ -56,4 +56,4 @@ block content
.uk-width-expand
+renderBackButton()
.uk-width-auto
button(type="submit").uk-button.uk-button-primary Create Account
button(type="submit").uk-button.uk-button-primary.uk-border-rounded Create Account

@ -128,7 +128,7 @@ class StickerIngestJob extends SiteWorkerProcess {
throw new Error(`unsupported sticker type: ${job.data.sticker.original.type}`);
}
this.jobLog(job, 'fetching original media', { // blah 62096adcb69874552a0f87bc
this.jobLog(job, 'fetching original media', {
stickerId: job.data.sticker._id,
slug: job.data.sticker.slug,
type: job.data.sticker.original.type,

@ -404,7 +404,6 @@ export default class DtpSiteAdminHostStatsApp extends DtpApp {
try {
await UIkit.modal.confirm(`Are you sure you want to remove post "${postTitle}"?`);
} catch (error) {
// canceled
return false;
}
@ -429,7 +428,6 @@ export default class DtpSiteAdminHostStatsApp extends DtpApp {
try {
await UIkit.modal.confirm(`Are you sure you want to remove page "${pageTitle}"?`);
} catch (error) {
// canceled
return false;
}
@ -453,7 +451,6 @@ export default class DtpSiteAdminHostStatsApp extends DtpApp {
try {
await UIkit.modal.confirm(`Are you sure you want to remove site link "${link.label}"?`);
} catch (error) {
// canceled
return false;
}
@ -477,7 +474,6 @@ export default class DtpSiteAdminHostStatsApp extends DtpApp {
try {
await UIkit.modal.confirm(`Are you sure you want to remove channel "${channel.name}"?`);
} catch (error) {
// canceled
return false;
}
@ -487,6 +483,27 @@ export default class DtpSiteAdminHostStatsApp extends DtpApp {
} catch (error) {
UIkit.modal.alert(`Failed to remove site link: ${error.message}`);
}
return false;
}
async disconnectCore (event) {
event.preventDefault();
event.stopPropagation();
const target = event.currentTarget || event.target;
const core = JSON.parse(target.getAttribute('data-core'));
try {
await UIkit.modal.confirm(`Are you sure you want to disconnect from Core "${core.name}"?`);
} catch (error) {
return false;
}
try {
const response = await fetch(`/admin/core-node/${core._id}`, { method: 'DELETE' });
await this.processResponse(response);
} catch (error) {
UIkit.modal.alert(`Failed to disconnect from Core: ${error.message}`);
}
return false;
}

@ -97,7 +97,9 @@ button.uk-button.dtp-button-subscribe {
}
a.uk-button.dtp-button-default,
button.uk-button.dtp-button-default {
a.uk-button.uk-button-default,
button.uk-button.dtp-button-default,
button.uk-button.uk-button-default {
background: none;
outline: none;
border: solid 2px rgb(75, 75, 75);
@ -107,47 +109,54 @@ button.uk-button.dtp-button-default {
&:hover {
background-color: rgb(75, 75, 75);
color: #e8e8e8;
}
}
a.uk-button.dtp-button-primary,
button.uk-button.dtp-button-primary {
a.uk-button.uk-button-primary,
button.uk-button.dtp-button-primary,
button.uk-button.uk-button-primary {
background: none;
outline: none;
border: solid 2px #1e87f0;
color: #c8c8c8;
color: @button-label-color;
transition: background-color 0.2s;
&:hover {
background-color: #1e87f0;
color: #e8e8e8;
}
}
a.uk-button.dtp-button-secondary,
button.uk-button.dtp-button-secondary {
a.uk-button.uk-button-secondary,
button.uk-button.dtp-button-secondary,
button.uk-button.uk-button-secondary {
background: none;
outline: none;
border: solid 2px rgb(75, 75, 75);
color: #c8c8c8;
border: solid 2px rgb(160,160,160);
color: @button-label-color;
&:hover {
background-color: rgb(75, 75, 75);
background-color: rgb(160,160,160);
color: #e8e8e8;
}
}
a.uk-button.dtp-button-danger,
button.uk-button.dtp-button-danger {
a.uk-button.uk-button-danger,
button.uk-button.dtp-button-danger,
button.uk-button.uk-button-danger {
background: none;
outline: none;
border: solid 2px rgb(255, 0, 0);
color: @global-color;
color: @button-label-color;
&:hover {
background-color: rgb(255, 0, 0);
color: #ffffff;
color: #e8e8e8;
}
}

@ -12,11 +12,29 @@ const striptags = require('striptags');
const { SiteLog } = require(path.join(__dirname, 'site-log'));
const { SiteAsync } = require(path.join(__dirname, 'site-async'));
const Events = require('events');
class SiteCommon extends Events {
const EventEmitter2 = require('eventemitter2');
class SiteCommon extends EventEmitter2 {
constructor (dtp, component, options) {
// ensure valid options
options = Object.assign({ }, options);
// provide DTP's default EventEmitter2 configuration
options.emitter = Object.assign({
wildcard: false,
delimiter: '.',
newListener: false,
removeListener: false,
maxListeners: 64,
verboseMemoryLeak: process.env.NODE_ENV === 'local',
ignoreErrors: false,
}, options);
// construct the EventEmitter2 instance via super()
super(options.emitter);
constructor (dtp, component) {
super();
// store options *after* super() because you can't alter `this` prior
this.options = options;
this.dtp = dtp;
this.component = component;
@ -43,6 +61,14 @@ class SiteCommon extends Events {
}, 1);
}
getEventName (name) {
return `dtp.${this.component.slug}.${name}`;
}
async emitDtpEvent (name, params) {
await this.emitAsync(this.getEventName(name), params);
}
async getJobQueue (name) {
if (this.jobQueues[name]) {
return this.jobQueues[name];

@ -184,6 +184,12 @@ module.exports.startPlatform = async (dtp) => {
try {
module.log = new SiteLog(module, dtp.config.component);
if (process.env.NODE_ENV === 'local') {
module.log.alert('allowing self-signed certificates for host-to-host communications', { env: process.env.NODE_ENV });
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
}
dtp.config.jobQueues = require(path.join(dtp.config.root, 'config', 'job-queues'));
await module.connectDatabase(dtp);

@ -28,10 +28,11 @@
"diskusage-ng": "^1.0.2",
"disposable-email-provider-domains": "^1.0.9",
"dotenv": "^16.0.0",
"dtp-jshint-reporter": "ssh://git@git.digitaltelepresence.com:digital-telepresence/dtp-jshint-reporter.git#master",
"dtp-jshint-reporter": "git+https://git.digitaltelepresence.com/digital-telepresence/dtp-jshint-reporter.git#master",
"ein-validator": "^1.0.1",
"email-domain-check": "^1.1.4",
"email-validator": "^2.0.4",
"eventemitter2": "^6.4.9",
"execa": "^6.1.0",
"express": "^4.17.3",
"express-limiter": "^1.6.1",

@ -3146,9 +3146,9 @@ drange@^1.0.2:
resolved "https://registry.yarnpkg.com/drange/-/drange-1.1.1.tgz#b2aecec2aab82fcef11dbbd7b9e32b83f8f6c0b8"
integrity sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==
"dtp-jshint-reporter@ssh://git@git.digitaltelepresence.com:digital-telepresence/dtp-jshint-reporter.git#master":
version "1.0.2"
resolved "ssh://git@git.digitaltelepresence.com:digital-telepresence/dtp-jshint-reporter.git#68b078b75cd6d048a9bf9bdc9b30ccc2a2145c4f"
"dtp-jshint-reporter@git+https://git.digitaltelepresence.com/digital-telepresence/dtp-jshint-reporter.git#master":
version "1.0.4"
resolved "git+https://git.digitaltelepresence.com/digital-telepresence/dtp-jshint-reporter.git#517c2f8055140b89cd3bbfff1cdf33669b416322"
dependencies:
chalk "^4.1.1"
@ -3525,6 +3525,11 @@ etag@1.8.1, etag@^1.8.1, etag@~1.8.1:
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
eventemitter2@^6.4.9:
version "6.4.9"
resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.9.tgz#41f2750781b4230ed58827bc119d293471ecb125"
integrity sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==
eventemitter3@^4.0.0:
version "4.0.7"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"

Loading…
Cancel
Save