22 changed files with 570 additions and 29 deletions
@ -0,0 +1,19 @@ |
|||||
|
// action-audit.js
|
||||
|
// Copyright (C) 2024 DTP Technologies, LLC
|
||||
|
// All Rights Reserved
|
||||
|
|
||||
|
'use strict'; |
||||
|
|
||||
|
import mongoose from 'mongoose'; |
||||
|
const Schema = mongoose.Schema; |
||||
|
|
||||
|
const ActionAuditSchema = new Schema({ |
||||
|
created: { type: Date, default: Date.now, required: true, index: -1, expires: '90d' }, |
||||
|
user: { type: Schema.ObjectId, required: true, index: 1, ref: 'User' }, |
||||
|
ip: { type: String }, |
||||
|
targetType: { type: String }, |
||||
|
target: { type: Schema.ObjectId, index: 1, refPath: 'targetType' }, |
||||
|
note: { type: String }, |
||||
|
}); |
||||
|
|
||||
|
export default mongoose.model('ActionAudit', ActionAuditSchema); |
@ -0,0 +1,15 @@ |
|||||
|
// chat-filter.js
|
||||
|
// Copyright (C) 2022,2023 DTP Technologies, LLC
|
||||
|
// All Rights Reserved
|
||||
|
|
||||
|
'use strict'; |
||||
|
|
||||
|
import mongoose from 'mongoose'; |
||||
|
const Schema = mongoose.Schema; |
||||
|
|
||||
|
const ChatFilterSchema = new Schema({ |
||||
|
created: { type: Date, required: true, default: Date.now, index: 1, expires: '300d' }, |
||||
|
filter: { type: String, required: true, unique: true, lowercase: true, index: 1 }, |
||||
|
}); |
||||
|
|
||||
|
export default mongoose.model('ChatFilter', ChatFilterSchema); |
@ -0,0 +1,30 @@ |
|||||
|
// action-audit.js
|
||||
|
// Copyright (C) 2024 DTP Technologies, LLC
|
||||
|
// All Rights Reserved
|
||||
|
|
||||
|
'use strict'; |
||||
|
|
||||
|
import mongoose from 'mongoose'; |
||||
|
const ActionAudit = mongoose.model('ActionAudit'); |
||||
|
|
||||
|
import { SiteService } from '../../lib/site-lib.js'; |
||||
|
|
||||
|
export default class ActionAuditService extends SiteService { |
||||
|
|
||||
|
static get slug () { return 'actionAudit'; } |
||||
|
static get name ( ) { return 'ActionAuditService'; } |
||||
|
|
||||
|
constructor (dtp) { |
||||
|
super(dtp, ActionAuditService); |
||||
|
} |
||||
|
|
||||
|
async auditRequest (req, note) { |
||||
|
const NOW = new Date(); |
||||
|
const audit = new ActionAudit(); |
||||
|
audit.created = NOW; |
||||
|
audit.user = req.user; |
||||
|
audit.ip = req.ip; |
||||
|
audit.note = note; |
||||
|
await audit.save(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,183 @@ |
|||||
|
// display-engine.js
|
||||
|
// Copyright (C) 2022,2023 DTP Technologies, LLC
|
||||
|
// All Rights Reserved
|
||||
|
|
||||
|
'use strict'; |
||||
|
|
||||
|
import path from 'node:path'; |
||||
|
|
||||
|
import pug from 'pug'; |
||||
|
import { v4 as uuidv4 } from 'uuid'; |
||||
|
|
||||
|
import { SiteService, SiteError } from '../../lib/site-lib.js'; |
||||
|
|
||||
|
class DisplayList { |
||||
|
|
||||
|
constructor (service, name) { |
||||
|
this.name = name; |
||||
|
this.id = uuidv4(); |
||||
|
this.commands = [ ]; |
||||
|
} |
||||
|
|
||||
|
showNotification (message, status, pos, timeout) { |
||||
|
this.commands.push({ |
||||
|
action: 'showNotification', |
||||
|
params: { message, status, pos, timeout }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
showModal (html) { |
||||
|
this.commands.push({ |
||||
|
action: 'showModal', |
||||
|
params: { html }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
playNotificationSound (action) { |
||||
|
this.commands.push({ |
||||
|
action: 'playNotificationSound', |
||||
|
params: { action }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
playSound (soundId) { |
||||
|
this.commands.push({ |
||||
|
action: 'playSound', |
||||
|
params: { soundId }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
addElement (selector, where, html) { |
||||
|
this.commands.push({ |
||||
|
selector, action: 'addElement', |
||||
|
params: { where, html }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
removeChildren (selector) { |
||||
|
this.commands.push({ |
||||
|
selector, action: 'removeChildren', |
||||
|
params: { }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
setTextContent (selector, text) { |
||||
|
this.commands.push({ |
||||
|
selector, action: 'setTextContent', |
||||
|
params: { text }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
replaceElement (selector, html) { |
||||
|
this.commands.push({ |
||||
|
selector, action: 'replaceElement', |
||||
|
params: { html }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
removeElement (selector) { |
||||
|
this.commands.push({ |
||||
|
selector, action: 'removeElement', |
||||
|
params: { }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
setAttribute (selector, name, value) { |
||||
|
this.commands.push({ |
||||
|
selector, action: 'setAttribute', |
||||
|
params: { name, value }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
removeAttribute (selector, name) { |
||||
|
this.commands.push({ |
||||
|
selector, action: 'removeAttribute', |
||||
|
params: { name }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
toggleAttribute (selector, name, force) { |
||||
|
this.commands.push({ |
||||
|
selector, action: 'toggleAttribute', |
||||
|
params: { name, force }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
addClass (selector, add) { |
||||
|
this.commands.push({ |
||||
|
selector, action: 'addClass', |
||||
|
params: { add }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
removeClass (selector, remove) { |
||||
|
this.commands.push({ |
||||
|
selector, action: 'removeClass', |
||||
|
params: { remove }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
replaceClass (selector, remove, add) { |
||||
|
this.commands.push({ |
||||
|
selector, action: 'replaceClass', |
||||
|
params: { remove, add }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
setValue (selector, value) { |
||||
|
this.commands.push({ |
||||
|
selector, action: 'setValue', |
||||
|
params: { value }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
navigateTo (href) { |
||||
|
this.commands.push({ |
||||
|
action: 'navigateTo', |
||||
|
params: { href }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
navigateBack ( ) { |
||||
|
this.commands.push({ |
||||
|
action: 'navigateBack', |
||||
|
params: { }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
reloadView ( ) { |
||||
|
this.commands.push({ |
||||
|
action: 'reloadView', |
||||
|
params: { }, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default class DisplayEngineService extends SiteService { |
||||
|
|
||||
|
static get name ( ) { return 'DisplayEngineService'; } |
||||
|
static get slug ( ) { return 'displayEngine'; } |
||||
|
|
||||
|
constructor (dtp) { |
||||
|
super(dtp, DisplayEngineService); |
||||
|
this.templates = { }; |
||||
|
} |
||||
|
|
||||
|
loadTemplate (name, pugScript) { |
||||
|
const scriptFile = path.join(this.dtp.config.root, 'app', 'views', pugScript); |
||||
|
this.templates[name] = pug.compileFile(scriptFile); |
||||
|
} |
||||
|
|
||||
|
executeTemplate (name, data) { |
||||
|
if (!this.templates[name]) { |
||||
|
this.log.error('view engine template undefined', { name }); |
||||
|
throw new SiteError(500, 'Unknown display engine template'); |
||||
|
} |
||||
|
data = Object.assign(this.dtp.app.locals, data); |
||||
|
return this.templates[name](data); |
||||
|
} |
||||
|
|
||||
|
createDisplayList (name = 'default') { |
||||
|
return new DisplayList(this, name); |
||||
|
} |
||||
|
} |
@ -1,7 +1,15 @@ |
|||||
extends layout/main |
extends layout/main |
||||
block view-content |
block view-content |
||||
|
|
||||
section.uk-section.uk-section-default.uk-section-small |
.dtp-chat-stage |
||||
.uk-container |
.chat-sidebar |
||||
h1= site.name |
.chat-stage-header Active Members |
||||
div= site.description |
.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. |
@ -0,0 +1,8 @@ |
|||||
|
@import "site/variables.less"; |
||||
|
|
||||
|
@import "uikit/src/less/uikit.less"; |
||||
|
@import "node_modules/uikit/src/less/uikit.theme.less"; |
||||
|
|
||||
|
@import "site/uikit-theme.dtp-dark.less"; |
||||
|
|
||||
|
@import "dtp-site.less"; |
@ -0,0 +1,8 @@ |
|||||
|
@import "site/variables.less"; |
||||
|
|
||||
|
@import "uikit/src/less/uikit.less"; |
||||
|
@import "node_modules/uikit/src/less/uikit.theme.less"; |
||||
|
|
||||
|
@import "site/uikit-theme.dtp-light.less"; |
||||
|
|
||||
|
@import "dtp-site.less"; |
@ -0,0 +1,3 @@ |
|||||
|
@import "site/main.less"; |
||||
|
@import "site/stage.less"; |
||||
|
@import "site/image.less"; |
@ -1,15 +0,0 @@ |
|||||
@import "uikit/src/less/uikit.less"; |
|
||||
|
|
||||
html, body { |
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; |
|
||||
} |
|
||||
|
|
||||
img.navbar-logo { |
|
||||
width: auto; |
|
||||
height: 48px; |
|
||||
} |
|
||||
img.profile-navbar { |
|
||||
width: auto; |
|
||||
height: 48px; |
|
||||
border-radius: 5px; |
|
||||
} |
|
@ -0,0 +1,10 @@ |
|||||
|
img.navbar-logo { |
||||
|
width: auto; |
||||
|
height: 48px; |
||||
|
} |
||||
|
|
||||
|
img.profile-navbar { |
||||
|
width: auto; |
||||
|
height: 48px; |
||||
|
border-radius: 5px; |
||||
|
} |
@ -0,0 +1,8 @@ |
|||||
|
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"] { |
||||
|
position: fixed; |
||||
|
top: 0; right: 0; bottom: 0; left: 0; |
||||
|
} |
||||
|
} |
@ -0,0 +1,62 @@ |
|||||
|
@stage-panel-padding: 4px 10px; |
||||
|
@stage-border-color: #4a4a4a; |
||||
|
|
||||
|
.dtp-chat-stage { |
||||
|
position: absolute; |
||||
|
top: @site-navbar-height; right: 0; bottom: 0; left: 0; |
||||
|
|
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
|
||||
|
display: flex; |
||||
|
|
||||
|
.chat-stage-header { |
||||
|
flex-grow: 0; |
||||
|
flex-shrink: 0; |
||||
|
|
||||
|
padding: @stage-panel-padding; |
||||
|
|
||||
|
font-weight: bold; |
||||
|
|
||||
|
background-color: #2a2a2a; |
||||
|
color: #e8e8e8; |
||||
|
} |
||||
|
|
||||
|
.chat-sidebar { |
||||
|
box-sizing: border-box; |
||||
|
width: 240px; |
||||
|
flex-shrink: 0; |
||||
|
flex-grow: 0; |
||||
|
|
||||
|
background-color: #1a1a1a; |
||||
|
color: #e8e8e8; |
||||
|
|
||||
|
border-right: solid 1px @stage-border-color; |
||||
|
|
||||
|
.sidebar-panel { |
||||
|
padding: @stage-panel-padding; |
||||
|
margin-bottom: 10px; |
||||
|
color: inherit; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.chat-container { |
||||
|
display: flex; |
||||
|
flex-grow: 1; |
||||
|
flex-direction: column; |
||||
|
|
||||
|
background-color: #e8e8e8; |
||||
|
color: #1a1a1a; |
||||
|
|
||||
|
.chat-content-panel { |
||||
|
padding: @stage-panel-padding; |
||||
|
flex-grow: 1; |
||||
|
} |
||||
|
.chat-input-panel { |
||||
|
height: 200px; |
||||
|
padding: @stage-panel-padding; |
||||
|
background-color: #1a1a1a; |
||||
|
color: #e8e8e8; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,117 @@ |
|||||
|
// |
||||
|
// Colors |
||||
|
// |
||||
|
|
||||
|
@page-background-color: #000000; |
||||
|
@content-background-color: #2a2a2a; |
||||
|
@content-border-color: #4a4a4a; |
||||
|
@content-container-color: #2a2a2a; |
||||
|
|
||||
|
@site-brand-color: #ff0013; |
||||
|
@button-label-color: #e8e8e8; |
||||
|
@button-label-hover: #2a2a2a; |
||||
|
@social-link-color: #e8e8e8; |
||||
|
@checkout-button-text-color: #e8e8e8; |
||||
|
|
||||
|
@emoji-react-button-active-color: #adc7a0; |
||||
|
|
||||
|
@scrollbar-border-color: @content-border-color; |
||||
|
@scrollbar-thumb-color: #ff001380; |
||||
|
|
||||
|
@global-background: #1a1a1a; |
||||
|
@global-muted-background: #3a3a3a; |
||||
|
@global-primary-background: #1e87f0; |
||||
|
@global-secondary-background: #222; |
||||
|
|
||||
|
@global-success-background: #32d296; |
||||
|
@global-warning-background: #faa05a; |
||||
|
@global-danger-background: #f0506e; |
||||
|
|
||||
|
@global-color: #c8c8c8; |
||||
|
@global-emphasis-color: #ffffff; |
||||
|
@global-muted-color: #9a9a9a; |
||||
|
|
||||
|
@global-link-color: #e00000; |
||||
|
@global-link-hover-color: #ff0000; |
||||
|
|
||||
|
@global-border: #4a4a4a; |
||||
|
@global-inverse-color: #e8e8e8; |
||||
|
|
||||
|
// |
||||
|
// Inverse |
||||
|
// |
||||
|
|
||||
|
@inverse-global-color-mode: light; |
||||
|
|
||||
|
@inverse-global-color: fade(@global-inverse-color, 70%); |
||||
|
@inverse-global-emphasis-color: @global-inverse-color; |
||||
|
@inverse-global-muted-color: #a8a8a8; |
||||
|
@inverse-global-inverse-color: #1a1a1a; |
||||
|
|
||||
|
@inverse-global-primary-background: @global-inverse-color; |
||||
|
@inverse-global-muted-background: fade(@global-inverse-color, 10%); |
||||
|
|
||||
|
@inverse-global-border: fade(@global-inverse-color, 20%); |
||||
|
|
||||
|
// |
||||
|
// Button |
||||
|
// |
||||
|
|
||||
|
@button-default-color: #000000; |
||||
|
@button-primary-color: #000000; |
||||
|
@button-secondary-color: #000000; |
||||
|
|
||||
|
@button-text-color: #000000; |
||||
|
@button-text-hover-color: #000000; |
||||
|
@button-text-disabled-color: #000000; |
||||
|
|
||||
|
button.uk-button.uk-button-default, |
||||
|
a.uk-button.uk-button-default { |
||||
|
color: @global-color; |
||||
|
} |
||||
|
|
||||
|
// |
||||
|
// Component: Navbar |
||||
|
// |
||||
|
|
||||
|
@navbar-background: #1a1a1a; |
||||
|
@navbar-nav-item-height: @site-navbar-height; |
||||
|
|
||||
|
// |
||||
|
// Off-Canvas |
||||
|
// |
||||
|
|
||||
|
@offcanvas-bar-background: #1a1a1a; |
||||
|
|
||||
|
// |
||||
|
// Navbar |
||||
|
// |
||||
|
|
||||
|
@navbar-dropdown-background: #2a2a2a; |
||||
|
@navbar-dropdown-border: #3a3a3a; |
||||
|
|
||||
|
.uk-navbar-dropdown { |
||||
|
border: solid 2px @navbar-dropdown-border; |
||||
|
border-radius: 4px; |
||||
|
|
||||
|
.uk-nav-divider { |
||||
|
border-color: @navbar-dropdown-border; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// |
||||
|
// Form |
||||
|
// |
||||
|
|
||||
|
// @internal-form-select-image: "/uikit/images/backgrounds/form-select.svg"; |
||||
|
// @internal-form-datalist-image: "/uikit/images/backgrounds/form-datalist.svg"; |
||||
|
// @internal-form-radio-image: "/uikit/images/backgrounds/form-radio.svg"; |
||||
|
// @internal-form-checkbox-image: "/uikit/images/backgrounds/form-checkbox.svg"; |
||||
|
// @internal-form-checkbox-indeterminate-image: "/uikit/images/backgrounds/form-checkbox-indeterminate.svg"; |
||||
|
|
||||
|
/* Disabled */ |
||||
|
.uk-input:disabled, |
||||
|
.uk-select:disabled, |
||||
|
.uk-textarea:disabled { |
||||
|
color: @inverse-global-muted-color; |
||||
|
} |
@ -0,0 +1,35 @@ |
|||||
|
// |
||||
|
// Colors |
||||
|
// |
||||
|
|
||||
|
@page-background-color: #e8e8e8; |
||||
|
@content-background-color: #ffffff; |
||||
|
@content-border-color: #a8a8a8; |
||||
|
@content-container-color: #c8c8c8; |
||||
|
|
||||
|
@site-brand-color: #ff0013; |
||||
|
@button-label-color: #2a2a2a; |
||||
|
@button-label-hover: #ffffff; |
||||
|
@social-link-color: #2a2a2a; |
||||
|
@checkout-button-text-color: #2a2a2a; |
||||
|
|
||||
|
@emoji-react-button-active-color: #adc7a0; |
||||
|
|
||||
|
@scrollbar-border-color: @content-border-color; |
||||
|
@scrollbar-thumb-color: #ff001380; |
||||
|
|
||||
|
// |
||||
|
// Component: Navbar |
||||
|
// |
||||
|
|
||||
|
@navbar-nav-item-height: @site-navbar-height; |
||||
|
|
||||
|
// |
||||
|
// Form |
||||
|
// |
||||
|
|
||||
|
// @internal-form-select-image: "/uikit/images/backgrounds/form-select.svg"; |
||||
|
// @internal-form-datalist-image: "/uikit/images/backgrounds/form-datalist.svg"; |
||||
|
// @internal-form-radio-image: "/uikit/images/backgrounds/form-radio.svg"; |
||||
|
// @internal-form-checkbox-image: "/uikit/images/backgrounds/form-checkbox.svg"; |
||||
|
// @internal-form-checkbox-indeterminate-image: "/uikit/images/backgrounds/form-checkbox-indeterminate.svg"; |
@ -0,0 +1,2 @@ |
|||||
|
@brand-color-gab: #00d178; |
||||
|
@site-navbar-height: 64px; |
Loading…
Reference in new issue