23 changed files with 606 additions and 189 deletions
@ -0,0 +1,93 @@ |
|||
// auth.js
|
|||
// Copyright (C) 2024 DTP Technologies, LLC
|
|||
// All Rights Reserved
|
|||
|
|||
'use strict'; |
|||
|
|||
import express from 'express'; |
|||
|
|||
import { SiteController, SiteError } from '../../lib/site-lib.js'; |
|||
|
|||
export default class ChatController extends SiteController { |
|||
|
|||
static get name ( ) { return 'ChatController'; } |
|||
static get slug ( ) { return 'chat'; } |
|||
|
|||
constructor (dtp) { |
|||
super(dtp, ChatController); |
|||
} |
|||
|
|||
async start ( ) { |
|||
const { |
|||
// csrfToken: csrfTokenService,
|
|||
// limiter: limiterService,
|
|||
session: sessionService, |
|||
} = this.dtp.services; |
|||
|
|||
const authRequired = sessionService.authCheckMiddleware({ requireLogin: true }); |
|||
|
|||
const router = express.Router(); |
|||
this.dtp.app.use('/chat', router); |
|||
|
|||
router.use( |
|||
async (req, res, next) => { |
|||
res.locals.currentView = 'auth'; |
|||
return next(); |
|||
}, |
|||
authRequired, |
|||
); |
|||
|
|||
router.param('roomId', this.populateRoomId.bind(this)); |
|||
|
|||
router.post( |
|||
'/room', |
|||
// limiterService.create(limiterService.config.chat.postCreateRoom),
|
|||
this.postCreateRoom.bind(this), |
|||
); |
|||
|
|||
router.get( |
|||
'/room/create', |
|||
this.getRoomCreateView.bind(this), |
|||
); |
|||
|
|||
router.get( |
|||
'/room/:roomId', |
|||
// limiterService.create(limiterService.config.chat.getRoomView),
|
|||
this.getRoomView.bind(this), |
|||
); |
|||
|
|||
return router; |
|||
} |
|||
|
|||
async populateRoomId (req, res, next, roomId) { |
|||
const { chat: chatService } = this.dtp.services; |
|||
try { |
|||
res.locals.room = await chatService.getRoomById(roomId); |
|||
if (!res.locals.room) { |
|||
throw new SiteError(404, "The chat room doesn't exist."); |
|||
} |
|||
return next(); |
|||
} catch (error) { |
|||
return next(error); |
|||
} |
|||
} |
|||
|
|||
async postCreateRoom (req, res, next) { |
|||
const { chat: chatService } = this.dtp.services; |
|||
try { |
|||
res.locals.room = await chatService.createRoom(req.user, req.body); |
|||
res.redirect(`/chat/room/${res.locals.room._id}`); |
|||
} catch (error) { |
|||
return next(error); |
|||
} |
|||
} |
|||
|
|||
async getRoomCreateView (req, res) { |
|||
res.render('chat/room/create'); |
|||
} |
|||
|
|||
async getRoomView (req, res) { |
|||
res.locals.currentView = 'chat-room'; |
|||
res.render('chat/room/view'); |
|||
} |
|||
} |
@ -0,0 +1,79 @@ |
|||
// status.js
|
|||
// Copyright (C) 2024 DTP Technologies, LLC
|
|||
// All Rights Reserved
|
|||
|
|||
'use strict'; |
|||
|
|||
import mongoose from 'mongoose'; |
|||
const Schema = mongoose.Schema; |
|||
|
|||
const LinkSchema = new Schema({ |
|||
created: { type: Date, default: Date.now, required: true, index: -1, }, |
|||
lastShared: { type: Date, required: true, index: -1 }, |
|||
lastPreviewFetched: { type: Date }, |
|||
submittedBy: { type: [Schema.ObjectId], required: true, index: 1, ref: 'User' }, |
|||
domain: { type: String, lowercase: true, required: true }, |
|||
url: { type: String, required: true }, |
|||
title: { type: String }, |
|||
siteName: { type: String }, |
|||
description: { type: String }, |
|||
tags: { type: [String] }, |
|||
mediaType: { type: String }, |
|||
contentType: { type: String }, |
|||
images: { type: [String] }, |
|||
videos: { type: [String] }, |
|||
audios: { type: [String] }, |
|||
favicons: { type: [String] }, |
|||
oembed: { |
|||
href: { type: String }, |
|||
fetched: { type: Date, required: true }, |
|||
version: { type: String }, |
|||
type: { type: String }, |
|||
cache_age: { type: Number }, |
|||
title: { type: String }, |
|||
provider_name: { type: String }, |
|||
provider_url: { type: String }, |
|||
author_name: { type: String }, |
|||
author_url: { type: String }, |
|||
thumbnail_url: { type: String }, |
|||
thumbnail_width: { type: String }, |
|||
thumbnail_height: { type: String }, |
|||
html: { type: String }, |
|||
url: { type: String }, |
|||
width: { type: String }, |
|||
height: { type: String }, |
|||
}, |
|||
flags: { |
|||
isBlocked: { type: Boolean, default: false, required: true }, |
|||
havePreview: { type: Boolean, default: false, required: true }, |
|||
}, |
|||
stats: { |
|||
shareCount: { type: Number, default: 1, required: true }, |
|||
visitCount: { type: Number, default: 0, required: true }, |
|||
}, |
|||
}); |
|||
|
|||
LinkSchema.index({ |
|||
domain: 1, |
|||
url: 1, |
|||
}, { |
|||
unique: true, |
|||
name: 'link_domain_unique', |
|||
}); |
|||
|
|||
LinkSchema.index({ |
|||
title: 'text', |
|||
description: 'text', |
|||
siteName: 'text', |
|||
domain: 'text', |
|||
}, { |
|||
weights: { |
|||
title: 5, |
|||
siteName: 3, |
|||
domain: 2, |
|||
description: 1, |
|||
}, |
|||
name: 'link_text_idx', |
|||
}); |
|||
|
|||
export default mongoose.model('Link', LinkSchema); |
@ -0,0 +1,21 @@ |
|||
extends ../../layout/main |
|||
block view-content |
|||
|
|||
.uk-section.uk-section-default |
|||
.uk-container |
|||
|
|||
form(method="POST", action="/chat/room").uk-form |
|||
.uk-card.uk-card-default.uk-card-small |
|||
.uk-card-header |
|||
h1.uk-card-title Create Room |
|||
|
|||
.uk-card-body |
|||
.uk-margin |
|||
label(for="name") Room Name |
|||
input(id="name", name="name", type="text", placeholder="Enter room name").uk-input |
|||
.uk-margin |
|||
label(for="topic") Topic |
|||
input(id="topic", name="topic", type="text", placeholder="Enter room topic or leave blank").uk-input |
|||
|
|||
.uk-card-footer.uk-flex.uk-flex-right |
|||
button(type="submit").uk-button.uk-button-default.uk-border-rounded Create Room |
@ -0,0 +1,41 @@ |
|||
extends ../../layout/main |
|||
block view-content |
|||
|
|||
.dtp-chat-stage |
|||
.chat-sidebar |
|||
.chat-stage-header Active Members |
|||
.sidebar-panel This is a sidebar content panel. It should word-wrap correctly and will be used to display usernames in the room with status. |
|||
|
|||
.chat-stage-header Idle Members |
|||
.sidebar-panel This is a sidebar content panel. It should word-wrap correctly and will be used to display usernames in the room with status. |
|||
|
|||
.chat-container |
|||
.chat-stage-header |
|||
div(uk-grid) |
|||
.uk-width-expand= room.name |
|||
|
|||
|
|||
.chat-content-panel |
|||
.chat-media |
|||
div(uk-grid) |
|||
.uk-width-1-4 |
|||
.live-member |
|||
video(src="/static/video/gdl-crush.mp4", autoplay, muted, loop, disablepictureinpicture, disableremoteplayback) |
|||
.uk-flex.live-meta |
|||
.live-username.uk-width-expand Rob Colbert |
|||
.uk-width-auto |
|||
i.fas.fa-volume-off |
|||
.uk-width-auto |
|||
.uk-margin-small-left |
|||
i.fas.fa-cog |
|||
|
|||
.chat-messages This is the chat content panel. It should word-wrap and scroll correctly, and will be where individual chat messages will render as they arrive and are sent. |
|||
|
|||
.chat-input-panel |
|||
textarea(id="chat-input", name="chatInput", rows=2).uk-textarea.uk-resize-none.uk-border-rounded |
|||
.uk-margin-small |
|||
.uk-flex |
|||
.uk-width-expand |
|||
.uk-width-auto |
|||
button(type="submit", uk-tooltip={ title: 'Send message' }).uk-button.uk-button-default.uk-button-small.uk-border-rounded |
|||
i.fas.fa-paper-plane |
@ -1,15 +1,38 @@ |
|||
extends layout/main |
|||
block view-content |
|||
|
|||
.dtp-chat-stage |
|||
.chat-sidebar |
|||
.chat-stage-header Active Members |
|||
.sidebar-panel This is a sidebar content panel. It should word-wrap correctly and will be used to display usernames in the room with status. |
|||
|
|||
.chat-stage-header Idle Members |
|||
.sidebar-panel This is a sidebar content panel. It should word-wrap correctly and will be used to display usernames in the room with status. |
|||
|
|||
.chat-container |
|||
.chat-stage-header Chat Room and Host names go here. |
|||
.chat-content-panel This is the chat content panel. It should word-wrap and scroll correctly, and will be where individual chat messages will render as they arrive and are sent. |
|||
.chat-input-panel This is the chat input panel. It will be where text is entered and sent, and contain some menu items, icons, and buttons. |
|||
mixin renderRoomListEntry (room) |
|||
a(href=`/chat/room/${room._id}`).uk-link-reset |
|||
.uk-text-bold= room.name |
|||
.uk-text-small= room.topic || '(no topic assigned)' |
|||
|
|||
section.uk-section.uk-section-default |
|||
.uk-container |
|||
|
|||
.uk-margin-medium |
|||
div(uk-grid) |
|||
.uk-width-expand |
|||
.uk-text-lead YOUR ROOMS |
|||
.uk-width-auto |
|||
a(href="/chat/room/create").uk-button.uk-button-default.uk-border-rounded |
|||
span |
|||
i.fas.fa-plus |
|||
span.uk-margin-small-left Create Room |
|||
|
|||
if (Array.isArray(ownerRooms) && (ownerRooms.length > 0)) |
|||
ul.uk-list |
|||
each room in ownerRooms |
|||
li.uk-list-divider |
|||
+renderRoomListEntry(room) |
|||
else |
|||
p You don't own any rooms. |
|||
|
|||
.uk-margin-medium |
|||
.uk-text-lead ROOM MEMBERSHIPS |
|||
if (Array.isArray(memberRooms) && (memberRooms.length > 0)) |
|||
ul.uk-list |
|||
each room in memberRooms |
|||
li.uk-list-divider |
|||
+renderRoomListEntry(room) |
|||
else |
|||
p You haven't joined any rooms that you don't own. |
@ -1,3 +1,4 @@ |
|||
@import "site/main.less"; |
|||
@import "site/navbar.less"; |
|||
@import "site/stage.less"; |
|||
@import "site/image.less"; |
@ -1,8 +1,18 @@ |
|||
html, body { |
|||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; |
|||
|
|||
&[data-current-view="home"] { |
|||
&[data-current-view="chat-room"] { |
|||
position: fixed; |
|||
top: 0; right: 0; bottom: 0; left: 0; |
|||
} |
|||
} |
|||
|
|||
.no-select { |
|||
user-select: none; |
|||
-moz-user-select: none; |
|||
-webkit-user-select: none; |
|||
} |
|||
|
|||
.uk-resize-none { |
|||
resize: none; |
|||
} |
@ -0,0 +1,4 @@ |
|||
.dtp-navbar-container { |
|||
padding-left: 10px; |
|||
padding-right: 10px; |
|||
} |
Loading…
Reference in new issue