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 |
|||
block view-content |
|||
|
|||
section.uk-section.uk-section-default.uk-section-small |
|||
.uk-container |
|||
h1= site.name |
|||
div= site.description |
|||
.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. |
@ -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