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

master
rob 11 months ago
commit 3c40f55468

@ -62,6 +62,9 @@ MAILGUN_DOMAIN=
MONGODB_HOST=localhost:27017 MONGODB_HOST=localhost:27017
MONGODB_DATABASE=dtp-sites MONGODB_DATABASE=dtp-sites
MONGODB_USERNAME=mongo-user
MONGODB_PASSWORD=change-me!
MONGODB_OPTIONS=
# #
# Redis configuration # Redis configuration

@ -12,6 +12,15 @@ The only qualified operated system for hosting a DTP Sites suite is [Ubuntu 20.0
apt-get -y install build-essential python3-pip apt-get -y install build-essential python3-pip
``` ```
## Host Preparation
The following commands must be exeucted on any host expected to run DTP Framework applications.
```sh
apt -y update && apt -y upgrade
apt -y install linux-headers-generic linux-headers-virtual linux-image-virtual linux-virtual
apt -y install build-essential ffmpeg supervisor
```
## Install Data Tier Components ## Install Data Tier Components
You will need MongoDB and MinIO installed and running before you can start DTP Sites web services. You will need MongoDB and MinIO installed and running before you can start DTP Sites web services.

@ -22,37 +22,36 @@ class UserController extends SiteController {
return next(); return next();
}); });
router.param('userId', this.populateUserId.bind(this)); router.param('localUserId', this.populateLocalUserId.bind(this));
router.post('/local/:localUserId', this.postUpdateLocalUser.bind(this));
router.get('/local/:localUserId', this.getLocalUserView.bind(this));
router.post('/:userId', this.postUpdateUser.bind(this));
router.get('/:userId', this.getUserView.bind(this));
router.get('/', this.getHomeView.bind(this)); router.get('/', this.getHomeView.bind(this));
return router; return router;
} }
async populateUserId (req, res, next, userId) { async populateLocalUserId (req, res, next, localUserId) {
const { user: userService } = this.dtp.services; const { user: userService } = this.dtp.services;
try { try {
res.locals.userAccount = await userService.getUserAccount(userId); res.locals.userAccount = await userService.getLocalUserAccount(localUserId);
return next(); return next();
} catch (error) { } catch (error) {
return next(error); return next(error);
} }
} }
async postUpdateUser (req, res, next) { async postUpdateLocalUser (req, res, next) {
const { user: userService } = this.dtp.services; const { user: userService } = this.dtp.services;
try { try {
await userService.updateForAdmin(res.locals.userAccount, req.body); await userService.updateLocalForAdmin(res.locals.userAccount, req.body);
res.redirect('/admin/user'); res.redirect('/admin/user');
} catch (error) { } catch (error) {
return next(error); return next(error);
} }
} }
async getUserView (req, res, next) { async getLocalUserView (req, res, next) {
const { comment: commentService } = this.dtp.services; const { comment: commentService } = this.dtp.services;
try { try {
res.locals.pagination = this.getPaginationParameters(req, 20); res.locals.pagination = this.getPaginationParameters(req, 20);
@ -68,7 +67,7 @@ class UserController extends SiteController {
const { user: userService } = this.dtp.services; const { user: userService } = this.dtp.services;
try { try {
res.locals.pagination = this.getPaginationParameters(req, 10); res.locals.pagination = this.getPaginationParameters(req, 10);
res.locals.userAccounts = await userService.getUserAccounts(res.locals.pagination, req.query.u); res.locals.userAccounts = await userService.searchLocalUserAccounts(res.locals.pagination, req.query.u);
res.locals.totalUserCount = await userService.getTotalCount(); res.locals.totalUserCount = await userService.getTotalCount();
res.render('admin/user/index'); res.render('admin/user/index');
} catch (error) { } catch (error) {

@ -78,7 +78,7 @@ class HiveUserController extends SiteController {
throw new SiteError(406, 'Must include search term'); throw new SiteError(406, 'Must include search term');
} }
res.locals.q = await userService.filterUsername(req.query.q); res.locals.q = userService.filterUsername(req.query.q);
res.locals.pagination = this.getPaginationParameters(req, 20); res.locals.pagination = this.getPaginationParameters(req, 20);
res.locals.userProfiles = await userService.getUserAccounts(res.locals.pagination, res.locals.q); res.locals.userProfiles = await userService.getUserAccounts(res.locals.pagination, res.locals.q);
res.locals.userProfiles = res.locals.userProfiles.map((user) => { res.locals.userProfiles = res.locals.userProfiles.map((user) => {

@ -23,7 +23,11 @@ class UserController extends SiteController {
session: sessionService, session: sessionService,
} = dtp.services; } = dtp.services;
const upload = this.createMulter(); const upload = this.createMulter('user', {
limits: {
fileSize: 1024 * 1000 * 5, // 5MB
},
});
const router = express.Router(); const router = express.Router();
dtp.app.use('/user', router); dtp.app.use('/user', router);
@ -60,8 +64,10 @@ class UserController extends SiteController {
return next(); return next();
} }
router.param('username', this.populateUsername.bind(this)); router.param('localUsername', this.populateLocalUsername.bind(this));
router.param('userId', this.populateUserId.bind(this)); router.param('coreUsername', this.populateCoreUsername.bind(this));
router.param('localUserId', this.populateLocalUserId.bind(this));
router.param('coreUserId', this.populateCoreUserId.bind(this)); router.param('coreUserId', this.populateCoreUserId.bind(this));
router.post( router.post(
@ -73,7 +79,7 @@ class UserController extends SiteController {
); );
router.post( router.post(
'/:userId/profile-photo', '/:localUserId/profile-photo',
limiterService.createMiddleware(limiterService.config.user.postProfilePhoto), limiterService.createMiddleware(limiterService.config.user.postProfilePhoto),
checkProfileOwner, checkProfileOwner,
upload.single('imageFile'), upload.single('imageFile'),
@ -81,7 +87,7 @@ class UserController extends SiteController {
); );
router.post( router.post(
'/:userId/settings', '/:localUserId/settings',
limiterService.createMiddleware(limiterService.config.user.postUpdateSettings), limiterService.createMiddleware(limiterService.config.user.postUpdateSettings),
checkProfileOwner, checkProfileOwner,
upload.none(), upload.none(),
@ -124,7 +130,7 @@ class UserController extends SiteController {
); );
router.get( router.get(
'/:userId/settings', '/:localUsername/settings',
limiterService.createMiddleware(limiterService.config.user.getSettings), limiterService.createMiddleware(limiterService.config.user.getSettings),
authRequired, authRequired,
otpMiddleware, otpMiddleware,
@ -132,7 +138,7 @@ class UserController extends SiteController {
this.getUserSettingsView.bind(this), this.getUserSettingsView.bind(this),
); );
router.get( router.get(
'/:username', '/:localUsername',
limiterService.createMiddleware(limiterService.config.user.getUserProfile), limiterService.createMiddleware(limiterService.config.user.getUserProfile),
authRequired, authRequired,
otpMiddleware, otpMiddleware,
@ -148,48 +154,84 @@ class UserController extends SiteController {
); );
} }
async populateUsername (req, res, next, username) { async populateCoreUsername (req, res, next, coreUsername) {
const { user: userService } = this.dtp.services; const { user: userService } = this.dtp.services;
try { try {
res.locals.userProfile = await userService.getPublicProfile('User', username); res.locals.username = userService.filterUsername(coreUsername);
if (!res.locals.userProfile) { res.locals.userProfileId = await userService.getCoreUserId(res.locals.username);
throw new SiteError(404, 'Member not found'); if (!res.locals.userProfileId) {
throw new SiteError(404, 'Core member not found');
} }
return next(); // manually chain over to the ID parameter resolver
return this.populateCoreUserId(req, res, next, res.locals.userProfileId);
} catch (error) { } catch (error) {
this.log.error('failed to populate username with public profile', { username, error }); this.log.error('failed to populate core username', { coreUsername, error });
return next(error); return next(error);
} }
} }
async populateUserId (req, res, next, userId) { async populateCoreUserId (req, res, next, coreUserId) {
const { user: userService } = this.dtp.services; const { user: userService } = this.dtp.services;
try { try {
userId = mongoose.Types.ObjectId(userId); res.locals.userProfileId = mongoose.Types.ObjectId(coreUserId);
} catch (error) {
return next(new SiteError(406, 'Invalid User')); if (req.user && (req.user.type === 'CoreUser') && req.user._id.equals(res.locals.userProfileId)) {
} res.locals.userProfile = await userService.getCoreUserAccount(res.locals.userProfileId);
try { } else {
res.locals.userProfile = await userService.getUserAccount(userId); res.locals.userProfile = await userService.getCoreUserProfile(res.locals.userProfileId);
}
if (!res.locals.userProfile) {
throw new SiteError(404, 'Core member not found');
}
return next(); return next();
} catch (error) { } catch (error) {
this.log.error('failed to populate userId', { userId, error }); this.log.error('failed to populate core user id', { coreUserId, error });
return next(error); return next(error);
} }
} }
async populateCoreUserId (req, res, next, coreUserId) { async populateLocalUsername (req, res, next, username) {
const { coreNode: coreNodeService } = this.dtp.services; const { user: userService } = this.dtp.services;
try { try {
coreUserId = mongoose.Types.ObjectId(coreUserId); res.locals.username = userService.filterUsername(username);
res.locals.userProfileId = await userService.getLocalUserId(res.locals.username);
if (!res.locals.userProfileId) {
throw new SiteError(404, 'Local member not found');
}
if (req.user && (req.user.type === 'User') && req.user._id.equals(res.locals.userProfileId)) {
res.locals.userProfile = await userService.getLocalUserAccount(res.locals.userProfileId);
} else {
res.locals.userProfile = await userService.getLocalUserProfile(res.locals.userProfileId);
}
return next();
} catch (error) { } catch (error) {
return next(new SiteError(406, 'Invalid User')); this.log.error('failed to populate local username', { username, error });
return next(error);
} }
}
async populateLocalUserId (req, res, next, userId) {
const { user: userService } = this.dtp.services;
try { try {
res.locals.userProfile = await coreNodeService.getUserByLocalId(coreUserId); res.locals.userProfileId = mongoose.Types.ObjectId(userId);
if (req.user && (req.user.type === 'User') && req.user._id.equals(res.locals.userProfileId)) {
res.locals.userProfile = await userService.getLocalUserAccount(res.locals.userProfileId);
} else {
res.locals.userProfile = await userService.getLocalUserProfile(res.locals.userProfileId);
}
if (!res.locals.userProfile) {
throw new SiteError(404, 'Local member not found');
}
return next(); return next();
} catch (error) { } catch (error) {
this.log.error('failed to populate coreUserId', { coreUserId, error }); this.log.error('failed to populate local user id', { userId, error });
return next(error); return next(error);
} }
} }
@ -229,8 +271,9 @@ class UserController extends SiteController {
async postProfilePhoto (req, res) { async postProfilePhoto (req, res) {
const { user: userService } = this.dtp.services; const { user: userService } = this.dtp.services;
try { try {
const displayList = this.createDisplayList('profile-photo');
await userService.updatePhoto(req.user, req.file); await userService.updatePhoto(req.user, req.file);
const displayList = this.createDisplayList('profile-photo');
displayList.showNotification( displayList.showNotification(
'Profile photo updated successfully.', 'Profile photo updated successfully.',
'success', 'success',
@ -250,8 +293,9 @@ class UserController extends SiteController {
async postHeaderImage (req, res) { async postHeaderImage (req, res) {
const { user: userService } = this.dtp.services; const { user: userService } = this.dtp.services;
try { try {
const displayList = this.createDisplayList('header-image');
await userService.updateHeaderImage(req.user, req.file); await userService.updateHeaderImage(req.user, req.file);
const displayList = this.createDisplayList('header-image');
displayList.showNotification( displayList.showNotification(
'Header image updated successfully.', 'Header image updated successfully.',
'success', 'success',
@ -271,10 +315,9 @@ class UserController extends SiteController {
async postUpdateCoreSettings (req, res) { async postUpdateCoreSettings (req, res) {
const { coreNode: coreNodeService } = this.dtp.services; const { coreNode: coreNodeService } = this.dtp.services;
try { try {
const displayList = this.createDisplayList('app-settings');
await coreNodeService.updateUserSettings(req.user, req.body); await coreNodeService.updateUserSettings(req.user, req.body);
const displayList = this.createDisplayList('app-settings');
displayList.reload(); displayList.reload();
res.status(200).json({ success: true, displayList }); res.status(200).json({ success: true, displayList });
} catch (error) { } catch (error) {

@ -29,4 +29,6 @@ const AnnouncementSchema = new Schema({
resourceStats: { type: ResourceStats, default: ResourceStatsDefaults, required: true }, resourceStats: { type: ResourceStats, default: ResourceStatsDefaults, required: true },
}); });
module.exports = mongoose.model('Announcement', AnnouncementSchema); module.exports = (conn) => {
return conn.model('Announcement', AnnouncementSchema);
};

@ -61,4 +61,6 @@ AttachmentSchema.index({
name: 'attachment_item_idx', name: 'attachment_item_idx',
}); });
module.exports = mongoose.model('Attachment', AttachmentSchema); module.exports = (conn) => {
return conn.model('Attachment', AttachmentSchema);
};

@ -28,4 +28,6 @@ const ChatMessageSchema = new Schema({
attachments: { type: [Schema.ObjectId], ref: 'Attachment' }, attachments: { type: [Schema.ObjectId], ref: 'Attachment' },
}); });
module.exports = mongoose.model('ChatMessage', ChatMessageSchema); module.exports = (conn) => {
return conn.model('ChatMessage', ChatMessageSchema);
};

@ -27,4 +27,6 @@ ChatRoomInviteSchema.index({
name: 'chatroom_invite_unique_idx', name: 'chatroom_invite_unique_idx',
}); });
module.exports = mongoose.model('ChatRoomInvite', ChatRoomInviteSchema); module.exports = (conn) => {
return conn.model('ChatRoomInvite', ChatRoomInviteSchema);
};

@ -41,4 +41,6 @@ ChatRoomSchema.index({
name: 'chatroom_public_open_idx', name: 'chatroom_public_open_idx',
}); });
module.exports = mongoose.model('ChatRoom', ChatRoomSchema); module.exports = (conn) => {
return conn.model('ChatRoom', ChatRoomSchema);
};

@ -72,4 +72,6 @@ CommentSchema.index({
name: 'comment_replies', name: 'comment_replies',
}); });
module.exports = mongoose.model('Comment', CommentSchema); module.exports = (conn) => {
return conn.model('Comment', CommentSchema);
};

@ -16,4 +16,6 @@ const ConnectTokenSchema = new Schema({
claimed: { type: Date }, claimed: { type: Date },
}); });
module.exports = mongoose.model('ConnectToken', ConnectTokenSchema); module.exports = (conn) => {
return conn.model('ConnectToken', ConnectTokenSchema);
};

@ -28,4 +28,6 @@ ContentReportSchema.index({
name: 'unique_user_content_report', name: 'unique_user_content_report',
}); });
module.exports = mongoose.model('ContentReport', ContentReportSchema); module.exports = (conn) => {
return conn.model('ContentReport', ContentReportSchema);
};

@ -23,4 +23,6 @@ ContentVoteSchema.index({
name: 'unique_user_content_vote', name: 'unique_user_content_vote',
}); });
module.exports = mongoose.model('ContentVote', ContentVoteSchema); module.exports = (conn) => {
return conn.model('ContentVote', ContentVoteSchema);
};

@ -30,4 +30,6 @@ const CoreNodeConnectSchema = new Schema({
}, },
}); });
module.exports = mongoose.model('CoreNodeConnect', CoreNodeConnectSchema); module.exports = (conn) => {
return conn.model('CoreNodeConnect', CoreNodeConnectSchema);
};

@ -35,4 +35,6 @@ const CoreNodeRequestSchema = new Schema({
}, },
}); });
module.exports = mongoose.model('CoreNodeRequest', CoreNodeRequestSchema); module.exports = (conn) => {
return conn.model('CoreNodeRequest', CoreNodeRequestSchema);
};

@ -45,4 +45,6 @@ CoreNodeSchema.index({
name: 'core_address_idx', name: 'core_address_idx',
}); });
module.exports = mongoose.model('CoreNode', CoreNodeSchema); module.exports = (conn) => {
return conn.model('CoreNode', CoreNodeSchema);
};

@ -52,4 +52,6 @@ CoreUserSchema.index({
name: 'core_username_lc_unique', name: 'core_username_lc_unique',
}); });
module.exports = mongoose.model('CoreUser', CoreUserSchema); module.exports = (conn) => {
return conn.model('CoreUser', CoreUserSchema);
};

@ -17,4 +17,6 @@ const CsrfTokenSchema = new Schema({
ip: { type: String, required: true }, ip: { type: String, required: true },
}); });
module.exports = mongoose.model('CsrfToken', CsrfTokenSchema); module.exports = (conn) => {
return conn.model('CsrfToken', CsrfTokenSchema);
};

@ -31,4 +31,6 @@ EmailBlacklistSchema.index({
}, },
}); });
module.exports = mongoose.model('EmailBlacklist', EmailBlacklistSchema); module.exports = (conn) => {
return conn.model('EmailBlacklist', EmailBlacklistSchema);
};

@ -12,4 +12,6 @@ const EmailBodySchema = new Schema({
body: { type: String, required: true }, body: { type: String, required: true },
}); });
module.exports = mongoose.model('EmailBody', EmailBodySchema); module.exports = (conn) => {
return conn.model('EmailBody', EmailBodySchema);
};

@ -16,4 +16,6 @@ const EmailLogSchema = new Schema({
messageId: { type: String }, messageId: { type: String },
}); });
module.exports = mongoose.model('EmailLog', EmailLogSchema); module.exports = (conn) => {
return conn.model('EmailLog', EmailLogSchema);
};

@ -15,4 +15,6 @@ const EmailVerifySchema = new Schema({
token: { type: String, required: true }, token: { type: String, required: true },
}); });
module.exports = mongoose.model('EmailVerify', EmailVerifySchema); module.exports = (conn) => {
return conn.model('EmailVerify', EmailVerifySchema);
};

@ -17,4 +17,6 @@ const EmailSchema = new Schema({
content: { type: Schema.ObjectId, required: true, index: true, refPath: 'contentType' }, content: { type: Schema.ObjectId, required: true, index: true, refPath: 'contentType' },
}); });
module.exports = mongoose.model('Email', EmailSchema); module.exports = (conn) => {
return conn.model('Email', EmailSchema);
};

@ -36,4 +36,6 @@ const EmojiReactionSchema = new Schema({
timestamp: { type: Number }, timestamp: { type: Number },
}); });
module.exports = mongoose.model('EmojiReaction', EmojiReactionSchema); module.exports = (conn) => {
return conn.model('EmojiReaction', EmojiReactionSchema);
};

@ -23,4 +23,6 @@ FeedEntrySchema.index({
name: 'feed_entry_by_feed_idx', name: 'feed_entry_by_feed_idx',
}); });
module.exports = mongoose.model('FeedEntry', FeedEntrySchema); module.exports = (conn) => {
return conn.model('FeedEntry', FeedEntrySchema);
};

@ -19,4 +19,6 @@ const FeedSchema = new Schema({
published: { type: Date }, published: { type: Date },
}); });
module.exports = mongoose.model('Feed', FeedSchema); module.exports = (conn) => {
return conn.model('Feed', FeedSchema);
};

@ -32,4 +32,6 @@ const ImageSchema = new Schema({
}, },
}); });
module.exports = mongoose.model('Image', ImageSchema); module.exports = (conn) => {
return conn.model('Image', ImageSchema);
};

@ -58,4 +58,6 @@ KaleidoscopeEventSchema.index({
name: 'evtsrc_site_author_index', name: 'evtsrc_site_author_index',
}); });
module.exports = mongoose.model('KaleidoscopeEvent', KaleidoscopeEventSchema); module.exports = (conn) => {
return conn.model('KaleidoscopeEvent', KaleidoscopeEventSchema);
};

@ -29,4 +29,6 @@ const LogSchema = new Schema({
metadata: { type: Schema.Types.Mixed }, metadata: { type: Schema.Types.Mixed },
}); });
module.exports = mongoose.model('Log', LogSchema); module.exports = (conn) => {
return conn.model('Log', LogSchema);
};

@ -50,4 +50,6 @@ const MediaRouterSchema = new Schema({
} }
}); });
module.exports = mongoose.model('MediaRouter', MediaRouterSchema); module.exports = (conn) => {
return conn.model('MediaRouter', MediaRouterSchema);
};

@ -40,4 +40,6 @@ const MediaWorkerSchema = new Schema({
} }
}); });
module.exports = mongoose.model('MediaWorker', MediaWorkerSchema); module.exports = (conn) => {
return conn.model('MediaWorker', MediaWorkerSchema);
};

@ -72,4 +72,6 @@ const NetHostStatsSchema = new Schema({
network: { type: [NetworkInterfaceStatsSchema], required: true }, network: { type: [NetworkInterfaceStatsSchema], required: true },
}); });
module.exports = mongoose.model('NetHostStats', NetHostStatsSchema); module.exports = (conn) => {
return conn.model('NetHostStats', NetHostStatsSchema);
};

@ -42,4 +42,6 @@ const NetHostSchema = new Schema({
network: { type: [NetworkInterfaceSchema] }, network: { type: [NetworkInterfaceSchema] },
}); });
module.exports = mongoose.model('NetHost', NetHostSchema); module.exports = (conn) => {
return conn.model('NetHost', NetHostSchema);
};

@ -18,4 +18,6 @@ const NewsletterRecipientSchema = new Schema({
}, },
}); });
module.exports = mongoose.model('NewsletterRecipient', NewsletterRecipientSchema); module.exports = (conn) => {
return conn.model('NewsletterRecipient', NewsletterRecipientSchema);
};

@ -28,4 +28,6 @@ const NewsletterSchema = new Schema({
}, },
}); });
module.exports = mongoose.model('Newsletter', NewsletterSchema); module.exports = (conn) => {
return conn.model('Newsletter', NewsletterSchema);
};

@ -16,4 +16,6 @@ const OAuth2AuthorizationCodeSchema = new Schema({
scopes: { type: [String], required: true }, scopes: { type: [String], required: true },
}); });
module.exports = mongoose.model('OAuth2AuthorizationCode', OAuth2AuthorizationCodeSchema); module.exports = (conn) => {
return conn.model('OAuth2AuthorizationCode', OAuth2AuthorizationCodeSchema);
};

@ -40,4 +40,6 @@ OAuth2ClientSchema.index({
unique: true, unique: true,
}); });
module.exports = mongoose.model('OAuth2Client', OAuth2ClientSchema); module.exports = (conn) => {
return conn.model('OAuth2Client', OAuth2ClientSchema);
};

@ -24,4 +24,6 @@ OAuth2TokenSchema.index({
name: 'oauth2_token_unique', name: 'oauth2_token_unique',
}); });
module.exports = mongoose.model('OAuth2Token', OAuth2TokenSchema); module.exports = (conn) => {
return conn.model('OAuth2Token', OAuth2TokenSchema);
};

@ -33,4 +33,6 @@ OtpAccountSchema.index({
name: 'otp_user_svc_uniq_idx', name: 'otp_user_svc_uniq_idx',
}); });
module.exports = mongoose.model('OtpAccount', OtpAccountSchema); module.exports = (conn) => {
return conn.model('OtpAccount', OtpAccountSchema);
};

@ -29,4 +29,6 @@ ResourceViewSchema.index({
name: 'res_view_daily_unique', name: 'res_view_daily_unique',
}); });
module.exports = mongoose.model('ResourceView', ResourceViewSchema); module.exports = (conn) => {
return conn.model('ResourceView', ResourceViewSchema);
};

@ -26,4 +26,6 @@ ResourceVisitSchema.index({
name: 'resource_visits_for_user', name: 'resource_visits_for_user',
}); });
module.exports = mongoose.model('ResourceVisit', ResourceVisitSchema); module.exports = (conn) => {
return conn.model('ResourceVisit', ResourceVisitSchema);
};

@ -43,4 +43,6 @@ const StickerSchema = new Schema({
encoded: { type: StickerMediaSchema }, encoded: { type: StickerMediaSchema },
}); });
module.exports = mongoose.model('Sticker', StickerSchema); module.exports = (conn) => {
return conn.model('Sticker', StickerSchema);
};

@ -14,4 +14,6 @@ const UserBlockSchema = new Schema({
blockedMembers: { type: [DtpUserSchema] }, blockedMembers: { type: [DtpUserSchema] },
}); });
module.exports = mongoose.model('UserBlock', UserBlockSchema); module.exports = (conn) => {
return conn.model('UserBlock', UserBlockSchema);
};

@ -24,4 +24,6 @@ const UserNotificationSchema = new Schema({
event: { type: Schema.ObjectId, required: true, ref: 'KaleidoscopeEvent' }, event: { type: Schema.ObjectId, required: true, ref: 'KaleidoscopeEvent' },
}); });
module.exports = mongoose.model('UserNotification', UserNotificationSchema); module.exports = (conn) => {
return conn.model('UserNotification', UserNotificationSchema);
};

@ -24,4 +24,6 @@ const UserSubscriptionSchema = new Schema({
subscriptions: { type: [SubscriptionSchema] }, subscriptions: { type: [SubscriptionSchema] },
}); });
module.exports = mongoose.model('UserSubscription', UserSubscriptionSchema); module.exports = (conn) => {
return conn.model('UserSubscription', UserSubscriptionSchema);
};

@ -22,17 +22,18 @@ const {
const UserSchema = new Schema({ const UserSchema = new Schema({
created: { type: Date, default: Date.now, required: true, index: -1 }, created: { type: Date, default: Date.now, required: true, index: -1 },
email: { type: String, required: true, lowercase: true, unique: true }, email: { type: String, required: true, lowercase: true, unique: true, select: false },
username: { type: String, required: true }, username: { type: String, required: true },
username_lc: { type: String, required: true, lowercase: true, unique: true, index: 1 }, username_lc: { type: String, required: true, lowercase: true, unique: true, index: 1 },
passwordSalt: { type: String, required: true }, passwordSalt: { type: String, required: true, select: false },
password: { type: String, required: true }, password: { type: String, required: true, select: false },
displayName: { type: String }, displayName: { type: String },
bio: { type: String, maxlength: 300 }, bio: { type: String, maxlength: 300 },
picture: { picture: {
large: { type: Schema.ObjectId, ref: 'Image' }, large: { type: Schema.ObjectId, ref: 'Image' },
small: { type: Schema.ObjectId, ref: 'Image' }, small: { type: Schema.ObjectId, ref: 'Image' },
}, },
header: { type: Schema.ObjectId, ref: 'Image' },
badges: { type: [String] }, badges: { type: [String] },
flags: { type: UserFlagsSchema, select: false }, flags: { type: UserFlagsSchema, select: false },
permissions: { type: UserPermissionsSchema, select: false }, permissions: { type: UserPermissionsSchema, select: false },
@ -62,4 +63,6 @@ UserSchema.virtual('hasAuthorDashboard').get( function ( ) {
this.permissions.canPublishPosts; this.permissions.canPublishPosts;
}); });
module.exports = mongoose.model('User', UserSchema); module.exports = (conn) => {
return conn.model('User', UserSchema);
};

@ -473,7 +473,12 @@ class ChatService extends SiteService {
async createMessage (author, messageDefinition) { async createMessage (author, messageDefinition) {
const { sticker: stickerService, user: userService } = this.dtp.services; const { sticker: stickerService, user: userService } = this.dtp.services;
author = await userService.getUserAccount(author._id); this.log.alert('user record', { author });
if (author.type === 'User') {
author = await userService.getLocalUserAccount(author._id);
} else {
author = await userService.getCoreUserAccount(author._id);
}
if (!author || !author.permissions || !author.permissions.canChat) { if (!author || !author.permissions || !author.permissions.canChat) {
throw new SiteError(403, `You are not permitted to chat at all on ${this.dtp.config.site.name}`); throw new SiteError(403, `You are not permitted to chat at all on ${this.dtp.config.site.name}`);
} }
@ -744,7 +749,13 @@ class ChatService extends SiteService {
const { user: userService } = this.dtp.services; const { user: userService } = this.dtp.services;
const NOW = new Date(); const NOW = new Date();
const userCheck = await userService.getUserAccount(user._id); let userCheck;
if (user.type === '') {
userCheck = await userService.getLocalUserAccount(user._id);
} else {
userCheck = await userService.getCoreUserAccount(user._id);
}
if (!userCheck || !userCheck.permissions || !userCheck.permissions.canChat) { if (!userCheck || !userCheck.permissions || !userCheck.permissions.canChat) {
throw new SiteError(403, 'You are not permitted to chat'); throw new SiteError(403, 'You are not permitted to chat');
} }

@ -92,8 +92,9 @@ class SessionService extends SiteService {
delete user.stats._id; delete user.stats._id;
delete user.optIn._id; delete user.optIn._id;
break; break;
case 'local': case 'local':
user = await userService.getUserAccount(userId); user = await userService.getLocalUserAccount(userId);
user.type = 'User'; user.type = 'User';
break; break;
} }

@ -20,6 +20,12 @@ const uuidv4 = require('uuid').v4;
const { SiteError, SiteService } = require('../../lib/site-lib'); const { SiteError, SiteService } = require('../../lib/site-lib');
/*
* The entire concept of "get a user" is in flux right now. It's best to just
* ignore what's happening in this service right now, and focus on other
* features in the sytem.
*/
class UserService extends SiteService { class UserService extends SiteService {
constructor (dtp) { constructor (dtp) {
@ -102,25 +108,25 @@ class UserService extends SiteService {
user.password = maskedPassword; user.password = maskedPassword;
user.flags = { user.flags = {
isAdmin: userDefinition.isAdmin || false, isAdmin: false,
isModerator: userDefinition.isModerator || false, isModerator: false,
isEmailVerified: userDefinition.isEmailVerified || false, isEmailVerified: false,
}; };
user.permissions = { user.permissions = {
canLogin: userDefinition.canLogin || true, canLogin: true,
canChat: userDefinition.canChat || true, canChat: true,
canComment: userDefinition.canComment || true, canComment: true,
canReport: userDefinition.canReport || true, canReport: true,
canAuthorPosts: userDefinition.canAuthorPosts || false, canAuthorPosts: false,
canAuthorPages: userDefinition.canAuthorPages || false, canAuthorPages: false,
canPublishPosts: userDefinition.canPublishPosts || false, canPublishPosts: false,
canPublishPages: userDefinition.canPublishPages || false, canPublishPages: false,
}; };
user.optIn = { user.optIn = {
system: userDefinition.optInSystem || true, system: true,
marketing: userDefinition.optInMarketing || false, marketing: false,
}; };
this.log.info('creating new user account', { email: userDefinition.email }); this.log.info('creating new user account', { email: userDefinition.email });
@ -184,7 +190,7 @@ class UserService extends SiteService {
async emailOptOut (userId, category) { async emailOptOut (userId, category) {
userId = mongoose.Types.ObjectId(userId); userId = mongoose.Types.ObjectId(userId);
const user = await this.getUserAccount(userId); const user = await this.getLocalUserAccount(userId);
if (!user) { if (!user) {
throw new SiteError(406, 'Invalid opt-out token'); throw new SiteError(406, 'Invalid opt-out token');
} }
@ -209,7 +215,6 @@ class UserService extends SiteService {
throw SiteError(403, 'Invalid user account operation'); throw SiteError(403, 'Invalid user account operation');
} }
// strip characters we don't want to allow in username
userDefinition.username = striptags(userDefinition.username.trim().replace(/[^A-Za-z0-9\-_]/gi, '')); userDefinition.username = striptags(userDefinition.username.trim().replace(/[^A-Za-z0-9\-_]/gi, ''));
const username_lc = userDefinition.username.toLowerCase(); const username_lc = userDefinition.username.toLowerCase();
@ -231,8 +236,7 @@ class UserService extends SiteService {
); );
} }
async updateForAdmin (user, userDefinition) { async updateLocalForAdmin (user, userDefinition) {
// strip characters we don't want to allow in username
userDefinition.username = striptags(userDefinition.username.trim().replace(/[^A-Za-z0-9\-_]/gi, '')); userDefinition.username = striptags(userDefinition.username.trim().replace(/[^A-Za-z0-9\-_]/gi, ''));
const username_lc = userDefinition.username.toLowerCase(); const username_lc = userDefinition.username.toLowerCase();
@ -283,7 +287,6 @@ class UserService extends SiteService {
const updateOp = { $set: { }, $unset: { } }; const updateOp = { $set: { }, $unset: { } };
// strip characters we don't want to allow in username
updateOp.$set.username = striptags(userDefinition.username.trim().replace(/[^A-Za-z0-9\-_]/gi, '')); updateOp.$set.username = striptags(userDefinition.username.trim().replace(/[^A-Za-z0-9\-_]/gi, ''));
if (!updateOp.$set.username || (updateOp.$set.username.length === 0)) { if (!updateOp.$set.username || (updateOp.$set.username.length === 0)) {
throw new SiteError(400, 'Must include a username'); throw new SiteError(400, 'Must include a username');
@ -321,7 +324,7 @@ class UserService extends SiteService {
}, options); }, options);
const accountEmail = account.username.trim().toLowerCase(); const accountEmail = account.username.trim().toLowerCase();
const accountUsername = await this.filterUsername(accountEmail); const accountUsername = this.filterUsername(accountEmail);
this.log.debug('locating user record', { accountEmail, accountUsername }); this.log.debug('locating user record', { accountEmail, accountUsername });
let user = await User let user = await User
@ -469,28 +472,23 @@ class UserService extends SiteService {
); );
} }
filterUserObject (user) { async getLocalUserId (username) {
const filteredUser = { const user = await User.findOne({ username_lc: username }).select('_id').lean();
_id: user._id, if (!user) {
created: user.created, return; // undefined
displayName: user.displayName,
username: user.username,
username_lc: user.username_lc,
bio: user.bio,
flags: user.flags,
permissions: user.permissions,
picture: user.picture,
};
if (filteredUser.flags && filteredUser.flags._id) {
delete filteredUser.flags._id;
} }
if (filteredUser.permissions && filteredUser.permissions._id) { return user._id;
delete filteredUser.permissions._id; }
async getCoreUserId (username) {
const user = await CoreUser.findOne({ username_lc: username }).select('_id').lean();
if (!user) {
return; // undefined
} }
return filteredUser; return user._id;
} }
async getUserAccount (userId) { async getLocalUserAccount (userId) {
const user = await User const user = await User
.findById(userId) .findById(userId)
.select('+email +flags +permissions +optIn +picture') .select('+email +flags +permissions +optIn +picture')
@ -512,11 +510,47 @@ class UserService extends SiteService {
user.hasAuthorDashboard = user.hasAuthorPermissions || user.hasPublishPermissions; user.hasAuthorDashboard = user.hasAuthorPermissions || user.hasPublishPermissions;
} }
async getUserAccounts (pagination, username) { async getCoreUserAccount (userId) {
const user = await User
.findById(userId)
.select('+email +flags +permissions +optIn +picture')
.populate(this.populateUser)
.lean();
if (!user) {
throw new SiteError(404, 'Core member account not found');
}
user.type = 'CoreUser';
return user;
}
async getLocalUserProfile (userId) {
const user = await User
.findById(userId)
.select('+email +flags +settings')
.populate(this.populateUser)
.lean();
user.type = 'User';
return user;
}
async getCoreUserProfile (userId) {
const user = await CoreUser
.findById(userId)
.select('+core +flags +settings')
.populate(this.populateUser)
.lean();
user.type = 'CoreUser';
return user;
}
async searchLocalUserAccounts (pagination, username) {
let search = { }; let search = { };
if (username) { if (username) {
username = this.filterUsername(username);
search.username_lc = { $regex: `^${username.toLowerCase().trim()}` }; search.username_lc = { $regex: `^${username.toLowerCase().trim()}` };
} }
const users = await User const users = await User
.find(search) .find(search)
.sort({ username_lc: 1 }) .sort({ username_lc: 1 })
@ -529,60 +563,24 @@ class UserService extends SiteService {
return users.map((user) => { user.type = 'User'; return user; }); return users.map((user) => { user.type = 'User'; return user; });
} }
async getUserProfile (userId) { async searchCoreUserAccounts (pagination, username) {
let user; let search = { };
try {
userId = mongoose.Types.ObjectId(userId); // will throw if invalid format
user = User.findById(userId);
} catch (error) {
user = User.findOne({ username: userId });
}
user = await user
.select('+email +flags +settings')
.populate(this.populateUser)
.lean();
return user;
}
async getPublicProfile (type, username) {
if (!username || (typeof username !== 'string')) {
throw new SiteError(406, 'Invalid username');
}
username = username.trim().toLowerCase(); username = this.filterUsername(username);
if (username.length === 0) { if (username) {
throw new SiteError(406, 'Invalid username'); search.username_lc = { $regex: `^${username.toLowerCase().trim()}` };
} }
let user; const users = await CoreUser
switch (type) { .find(search)
case 'CoreUser': .sort({ username_lc: 1 })
user = await CoreUser .select('+core +coreUserId +flags +permissions +optIn')
.findOne({ username_lc: username }) .skip(pagination.skip)
.select('_id created username username_lc displayName bio picture header core') .limit(pagination.cpp)
.populate(this.populateUser) .lean()
.lean(); ;
if (user) {
user.type = 'CoreUser';
}
break;
case 'User':
user = await User
.findOne({ username_lc: username })
.select('_id created username username_lc displayName bio picture header')
.populate(this.populateUser)
.lean();
if (user) {
user.type = 'User';
}
break;
default:
throw new SiteError(400, 'Invalid user account type');
}
return user; return users.map((user) => { user.type = 'CoreUser'; return user; });
} }
async getRecent (maxCount = 3) { async getRecent (maxCount = 3) {
@ -664,10 +662,6 @@ class UserService extends SiteService {
return actions; return actions;
} }
async filterUsername (username) {
return striptags(username.trim().toLowerCase()).replace(/\W/g, '');
}
async checkUsername (username) { async checkUsername (username) {
if (!username || (typeof username !== 'string') || (username.length === 0)) { if (!username || (typeof username !== 'string') || (username.length === 0)) {
throw new SiteError(406, 'Invalid username'); throw new SiteError(406, 'Invalid username');
@ -683,6 +677,34 @@ class UserService extends SiteService {
} }
} }
filterUsername (username) {
while (username[0] === '@') {
username = username.slice(1);
}
return striptags(username.trim().toLowerCase()).replace(/\W/g, '');
}
filterUserObject (user) {
const filteredUser = {
_id: user._id,
created: user.created,
displayName: user.displayName,
username: user.username,
username_lc: user.username_lc,
bio: user.bio,
flags: user.flags,
permissions: user.permissions,
picture: user.picture,
};
if (filteredUser.flags && filteredUser.flags._id) {
delete filteredUser.flags._id;
}
if (filteredUser.permissions && filteredUser.permissions._id) {
delete filteredUser.permissions._id;
}
return filteredUser;
}
async recordProfileView (user, req) { async recordProfileView (user, req) {
const { resource: resourceService } = this.dtp.services; const { resource: resourceService } = this.dtp.services;
await resourceService.recordView(req, 'User', user._id); await resourceService.recordView(req, 'User', user._id);
@ -738,6 +760,41 @@ class UserService extends SiteService {
await User.updateOne({ _id: user._id }, { $unset: { 'picture': '' } }); await User.updateOne({ _id: user._id }, { $unset: { 'picture': '' } });
} }
async updateHeaderImage (user, file) {
const { image: imageService } = this.dtp.services;
await this.removeHeaderImage(user.header);
const images = [
{
width: 1400,
height: 400,
format: 'jpeg',
formatParameters: {
quality: 80,
},
},
];
await imageService.processImageFile(user, file, images);
await User.updateOne(
{ _id: user._id },
{
$set: {
'header': images[0].image._id,
},
},
);
}
async removeHeaderImage (user) {
const { image: imageService } = this.dtp.services;
user = await this.getUserAccount(user._id);
if (user.header) {
await imageService.deleteImage(user.header);
}
await User.updateOne({ _id: user._id }, { $unset: { 'header': '' } });
}
async blockUser (user, blockedUser) { async blockUser (user, blockedUser) {
if (user._id.equals(blockedUser._id)) { if (user._id.equals(blockedUser._id)) {
throw new SiteError(406, "You can't block yourself"); throw new SiteError(406, "You can't block yourself");
@ -812,4 +869,4 @@ module.exports = {
slug: 'user', slug: 'user',
name: 'user', name: 'user',
create: (dtp) => { return new UserService(dtp); }, create: (dtp) => { return new UserService(dtp); },
}; };

@ -13,7 +13,7 @@ block content
if userAccount.displayName if userAccount.displayName
.uk-text-large= userAccount.displayName .uk-text-large= userAccount.displayName
div div
a(href=`/user/${userAccount._id}`) @#{userAccount.username} a(href=`/user/${userAccount.username}`) @#{userAccount.username}
.uk-card-body .uk-card-body
.uk-margin .uk-margin

@ -5,7 +5,7 @@ block content
div(uk-grid).uk-grid-small div(uk-grid).uk-grid-small
div(class="uk-width-1-1 uk-width-2-3@l") div(class="uk-width-1-1 uk-width-2-3@l")
form(method="POST", action=`/admin/user/${userAccount._id}`).uk-form form(method="POST", action=`/admin/user/local/${userAccount._id}`).uk-form
input(type="hidden", name="username", value= userAccount.username) input(type="hidden", name="username", value= userAccount.username)
input(type="hidden", name="displayName", value= userAccount.displayName) input(type="hidden", name="displayName", value= userAccount.displayName)
.uk-card.uk-card-default.uk-card-small .uk-card.uk-card-default.uk-card-small
@ -20,7 +20,7 @@ block content
.uk-width-auto .uk-width-auto
a(href=`mailto:${userAccount.email}`)= userAccount.email a(href=`mailto:${userAccount.email}`)= userAccount.email
.uk-width-auto .uk-width-auto
a(href=`/user/${userAccount._id}`) @#{userAccount.username} a(href=`/user/${userAccount.username}`) @#{userAccount.username}
.uk-card-body .uk-card-body
.uk-margin .uk-margin

@ -22,10 +22,10 @@ block content
each userAccount in userAccounts each userAccount in userAccounts
tr tr
td td
a(href=`/admin/user/${userAccount._id}`)= userAccount.username a(href=`/admin/user/local/${userAccount._id}`)= userAccount.username
td td
if userAccount.displayName if userAccount.displayName
a(href=`/admin/user/${userAccount._id}`)= userAccount.displayName a(href=`/admin/user/local/${userAccount._id}`)= userAccount.displayName
else else
.uk-text-muted N/A .uk-text-muted N/A
td= moment(userAccount.created).format('YYYY-MM-DD hh:mm a') td= moment(userAccount.created).format('YYYY-MM-DD hh:mm a')

@ -72,7 +72,7 @@ nav(uk-navbar).uk-navbar-container.uk-position-fixed.uk-position-top
i.fas.fa-user i.fas.fa-user
span Profile span Profile
li li
a(href= user.core ? `/user/core/${user._id}/settings` : `/user/${user._id}/settings`) a(href= user.core ? `/user/core/${user.username}/settings` : `/user/${user.username}/settings`)
span.nav-item-icon span.nav-item-icon
i.fas.fa-cog i.fas.fa-cog
span Settings span Settings

@ -68,7 +68,7 @@ mixin renderMenuItem (iconClass, label)
.uk-width-expand Profile .uk-width-expand Profile
li(class={ "uk-active": (currentView === 'user-settings') }) li(class={ "uk-active": (currentView === 'user-settings') })
a(href=`/user/${user._id}/settings`).uk-display-block a(href=`/user/${user.username}/settings`).uk-display-block
div(uk-grid).uk-grid-collapse div(uk-grid).uk-grid-collapse
.uk-width-auto .uk-width-auto
.app-menu-icon .app-menu-icon

@ -0,0 +1,9 @@
.markdown-block {
font-size: @global-font-size;
line-height: @global-line-height;
color: @global-color;
p:last-of-type {
margin-bottom: 0;
}
}

@ -10,13 +10,14 @@
@import "site/kaleidoscope-event.less"; @import "site/kaleidoscope-event.less";
@import "site/nav.less"; @import "site/nav.less";
@import "site/button.less";
@import "site/content.less"; @import "site/content.less";
@import "site/core-node.less"; @import "site/core-node.less";
@import "site/dashboard.less"; @import "site/dashboard.less";
@import "site/site.less";
@import "site/form.less"; @import "site/form.less";
@import "site/button.less"; @import "site/markdown.less";
@import "site/sidebar.less";
@import "site/section.less"; @import "site/section.less";
@import "site/sidebar.less";
@import "site/site.less";
@import "site/chat.less"; @import "site/chat.less";

@ -34,6 +34,10 @@ module.grantPermission = async (target, permission) => {
const User = mongoose.model('User'); const User = mongoose.model('User');
try { try {
const user = await User.findOne({ email: target }).select('+permissions +flags'); const user = await User.findOne({ email: target }).select('+permissions +flags');
if (!user) {
throw new Error(`User not found (email: ${target})`);
}
switch (permission) { switch (permission) {
case 'admin': case 'admin':
user.flags.isAdmin = true; user.flags.isAdmin = true;

@ -43,7 +43,7 @@ module.config = {
module.log = new SiteLog(module, module.config.component); module.log = new SiteLog(module, module.config.component);
module.shutdown = async ( ) => { module.shutdown = async ( ) => {
await SitePlatform.shutdown(); return await SitePlatform.shutdown();
}; };
(async ( ) => { (async ( ) => {
@ -63,8 +63,8 @@ module.shutdown = async ( ) => {
process.once('SIGINT', async ( ) => { process.once('SIGINT', async ( ) => {
module.log.info('SIGINT received'); module.log.info('SIGINT received');
module.log.info('requesting shutdown...'); module.log.info('requesting shutdown...');
await module.shutdown(); const exitCode = await module.shutdown();
const exitCode = await SitePlatform.shutdown();
process.nextTick(( ) => { process.nextTick(( ) => {
process.exit(exitCode); process.exit(exitCode);
}); });

@ -1,4 +1,4 @@
// dtpweb-log.js // dtp-log.js
// Copyright (C) 2022 DTP Technologies, LLC // Copyright (C) 2022 DTP Technologies, LLC
// License: Apache-2.0 // License: Apache-2.0

@ -1,4 +1,4 @@
// dtpweb-socket.js // dtp-socket.js
// Copyright (C) 2022 DTP Technologies, LLC // Copyright (C) 2022 DTP Technologies, LLC
// License: Apache-2.0 // License: Apache-2.0

@ -15,12 +15,12 @@ const ConnectToken = mongoose.model('ConnectToken');
const marked = require('marked'); const marked = require('marked');
const { SiteLog } = require(path.join(__dirname, 'site-log')); const { SiteLog } = require(path.join(__dirname, 'site-log'));
const { SiteCommon } = require(path.join(__dirname, 'site-common'));
const Events = require('events'); class SiteIoServer extends SiteCommon {
class SiteIoServer extends Events {
constructor (dtp) { constructor (dtp) {
super(); super(dtp, { name: 'ioServer', slug: 'io-server' });
this.dtp = dtp; this.dtp = dtp;
this.log = new SiteLog(dtp, DTP_COMPONENT); this.log = new SiteLog(dtp, DTP_COMPONENT);
} }
@ -74,6 +74,10 @@ class SiteIoServer extends Events {
} }
async stop ( ) { async stop ( ) {
if (this.io) {
this.io.close();
delete this.io;
}
} }

@ -34,12 +34,22 @@ module.connectDatabase = async (/*dtp*/) => {
host: process.env.MONGODB_HOST, host: process.env.MONGODB_HOST,
database: process.env.MONGODB_DATABASE, database: process.env.MONGODB_DATABASE,
}); });
const mongoConnectUri = `mongodb://${process.env.MONGODB_HOST}/${process.env.MONGODB_DATABASE}`; const mongoConnectionInfo = {
host: process.env.MONGODB_HOST,
db: process.env.MONGODB_DATABASE,
username: encodeURIComponent(process.env.MONGODB_USERNAME),
password: encodeURIComponent(process.env.MONGODB_PASSWORD),
options: process.env.MONGODB_OPTIONS || '',
};
let mongoConnectUri = `mongodb://${process.env.MONGODB_HOST}/${process.env.MONGODB_DATABASE}`;
if (process.env.NODE_ENV === 'production'){
mongoConnectUri = `mongodb://${mongoConnectionInfo.username}:${mongoConnectionInfo.password}@${mongoConnectionInfo.host}/${mongoConnectionInfo.options}`;
}
module.db = await mongoose.connect(mongoConnectUri, { module.db = await mongoose.connect(mongoConnectUri, {
socketTimeoutMS: 0, socketTimeoutMS: 0,
keepAlive: true, keepAlive: true,
keepAliveInitialDelay: 300000, keepAliveInitialDelay: 300000,
dbName: process.env.MONGODB_DATABASE, dbName: mongoConnectionInfo.db,
}); });
module.log.info('connected to MongoDB'); module.log.info('connected to MongoDB');
} catch (error) { } catch (error) {
@ -48,12 +58,12 @@ module.connectDatabase = async (/*dtp*/) => {
} }
}; };
module.loadModels = async (dtp) => { module.loadModels = async (dtp) => {
dtp.models = module.models = [ ]; dtp.models = module.models = [ ];
const modelScripts = glob.sync(path.join(dtp.config.root, 'app', 'models', '*.js')); const modelScripts = glob.sync(path.join(dtp.config.root, 'app', 'models', '*.js'));
modelScripts.forEach((modelScript) => { modelScripts.forEach((modelScript) => {
const model = require(modelScript); const instance = require(modelScript);
const model = instance(module.db);
if (module.models[model.modelName]) { if (module.models[model.modelName]) {
module.log.error('model name collision', { name: model.modelName }); module.log.error('model name collision', { name: model.modelName });
process.exit(-1); process.exit(-1);
@ -146,7 +156,7 @@ module.loadControllers = async (dtp) => {
await SiteAsync.each(scripts, async (script) => { await SiteAsync.each(scripts, async (script) => {
const controller = await require(script); const controller = await require(script);
controller.instance = await controller.create(dtp); controller.instance = await controller.create(dtp);
module.log.info('controller loaded', { name: controller.name, slug: controller.slug });
dtp.controllers[controller.name] = controller; dtp.controllers[controller.name] = controller;
inits.push(controller); inits.push(controller);
@ -196,7 +206,7 @@ module.exports.startPlatform = async (dtp) => {
await module.connectRedis(dtp); await module.connectRedis(dtp);
await module.loadModels(dtp); await module.loadModels(dtp);
SiteLog.setModel(mongoose.model('Log')); SiteLog.setModel(module.db.model('Log'));
await module.loadServices(dtp); await module.loadServices(dtp);

Loading…
Cancel
Save