diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..87d2410 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "pwa-node", + "request": "launch", + "name": "Launch Program", + "skipFiles": [ + "/**" + ], + "program": "${workspaceFolder:dtp-sites}/dtp-sites.js", + "console": "integratedTerminal", + "env": { + "HTTP_BIND_PORT": "3333" + } + } + ] +} \ No newline at end of file diff --git a/app/controllers/admin.js b/app/controllers/admin.js new file mode 100644 index 0000000..268c626 --- /dev/null +++ b/app/controllers/admin.js @@ -0,0 +1,68 @@ +// admin.js +// Copyright (C) 2021 Digital Telepresence, LLC +// All Rights Reserved + +'use strict'; + +const DTP_COMPONENT_NAME = 'admin'; + +const path = require('path'); +const express = require('express'); + +const mongoose = require('mongoose'); +const User = mongoose.model('User'); + +const { SiteError, SiteController } = require('../../lib/site-lib'); + +class AdminController extends SiteController { + + constructor (dtp) { + super(dtp, DTP_COMPONENT_NAME); + } + + async start ( ) { + const { otpAuth: otpAuthService } = this.dtp.services; + + const router = express.Router(); + this.dtp.app.use('/admin', router); + + router.use( + async (req, res, next) => { + res.locals.currentView = 'admin'; + res.locals.adminView = 'home'; + + if (!req.user || !req.user.flags.isAdmin) { + return next(new SiteError(403, 'Administrative privileges required')); + } + + return next(); + }, + otpAuthService.middleware('Admin', { + adminRequired: true, + otpRequired: true, + otpRedirectURL: '/admin', + }), + ); + + router.use('/domain',await this.loadChild(path.join(__dirname, 'admin', 'domain'))); + router.use('/host',await this.loadChild(path.join(__dirname, 'admin', 'host'))); + router.use('/job-queue',await this.loadChild(path.join(__dirname, 'admin', 'job-queue'))); + router.use('/user', await this.loadChild(path.join(__dirname, 'admin', 'user'))); + + router.get('/', this.getHomeView.bind(this)); + + return router; + } + + async getHomeView (req, res) { + res.locals.stats = { + memberCount: await User.estimatedDocumentCount(), + }; + res.render('admin/index'); + } +} + +module.exports = async (dtp) => { + let controller = new AdminController(dtp); + return controller; +}; diff --git a/app/views/admin/components/menu.pug b/app/views/admin/components/menu.pug index d31938e..811df24 100644 --- a/app/views/admin/components/menu.pug +++ b/app/views/admin/components/menu.pug @@ -9,11 +9,6 @@ ul.uk-nav.uk-nav-default li.uk-nav-divider - li(class={ 'uk-active': (adminView === 'domain') }) - a(href="/admin/domain") - span.nav-item-icon - i.fas.fa-globe-americas - span.uk-margin-small-left Domains li(class={ 'uk-active': (adminView === 'host') }) a(href="/admin/host") span.nav-item-icon @@ -27,47 +22,8 @@ ul.uk-nav.uk-nav-default li.uk-nav-divider - li(class={ 'uk-active': (adminView === 'channel-application') }) - a(href="/admin/channel-application") - span.nav-item-icon - i.fas.fa-user-tie - span.uk-margin-small-left Channel Applications - - li(class={ 'uk-active': (adminView === 'channel') }) - a(href="/admin/channel") - span.nav-item-icon - i.fas.fa-broadcast-tower - span.uk-margin-small-left Channels - li(class={ 'uk-active': (adminView === 'user') }) a(href="/admin/user") span.nav-item-icon i.fas.fa-user - span.uk-margin-small-left Users - - li.uk-nav-divider - - li(class={ 'uk-active': (adminView === 'stream') }) - a(href="/admin/stream") - span.nav-item-icon - i.fas.fa-tv - span.uk-margin-small-left Live Streams - - li(class={ 'uk-active': (adminView === 'replay') }) - a(href="/admin/replay") - span.nav-item-icon - i.fas.fa-hdd - span.uk-margin-small-left Replay DVR - - li.uk-nav-divider - - li(class={ 'uk-active': (adminView === 'category') }) - a(href="/admin/category") - span.nav-item-icon - i.fas.fa-box - span.uk-margin-small-left Categories - li(class={ 'uk-active': (adminView === 'survey') }) - a(href="/admin/survey") - span.nav-item-icon - i.fas.fa-poll-h - span.uk-margin-small-left Surveys \ No newline at end of file + span.uk-margin-small-left Users \ No newline at end of file diff --git a/app/views/components/navbar.pug b/app/views/components/navbar.pug index bf69944..36f6f10 100644 --- a/app/views/components/navbar.pug +++ b/app/views/components/navbar.pug @@ -1,79 +1,87 @@ -.uk-navbar-container.uk-padding-remove.uk-position-fixed.uk-position-top - nav(uk-navbar) - .uk-navbar-left - .uk-navbar-item - button(type="button", uk-toggle="target: #dtp-offcanvas").uk-button.uk-button-link.uk-padding-small - i.fas.fa-chevron-right +nav(uk-navbar).uk-navbar-container.uk-position-fixed.uk-position-top + .uk-navbar-left + .uk-navbar-item + button(type="button", uk-toggle="target: #dtp-offcanvas").uk-button.uk-button-link.uk-padding-small + i.fas.fa-bars + + //- Site icon + a(href="/", class="uk-visible@m").uk-navbar-item + img(src=`/img/icon/${site.domainKey}/icon-48x48.png`) + + //- Site name + a(href="/", class="uk-visible@xl").uk-navbar-item.uk-logo + span= site.name - .uk-navbar-center - a(href="/").uk-navbar-item.uk-logo - img(src=`/img/icon/${site.domainKey}/icon-48x48.png`) - - .uk-navbar-right - .uk-navbar-item - if user - div.no-select - if user.picture_url - img( - src= user.picture_url || '/img/default-member.png', - title="Member Menu", - ).profile-navbar - else - include missing-profile-icon - div(uk-dropdown={ mode: 'click' }).uk-navbar-dropdown - ul.uk-nav.uk-navbar-dropdown-nav(style="z-index: 1024;") - li.uk-nav-heading.uk-text-center= user.displayName || user.username - li.uk-nav-divider - if (user.channel) - li - a(href=`/channel/${user.channel.slug}`) - span.nav-item-icon - i.fas.fa-broadcast-tower - span(style="max-width: 120px;").uk-text-truncate= user.channel.name - if (user.channel.liveEpisode) - li - a(href=`/channel/${user.channel.slug}/broadcaster`) - span.nav-item-icon - i.fas.fa-box-open - span Broadcaster - li - a(href='/dashboard') - span.nav-item-icon - i.fas.fa-tachometer-alt - span Dashboard - - li.uk-nav-divider + //- Center menu (visible only on tablet and mobile) + div(class="uk-hidden@m").uk-navbar-center + a(href="/").uk-navbar-item + img(src=`/img/icon/${site.domainKey}/icon-48x48.png`) + .uk-navbar-right + .uk-navbar-item + if user + div.no-select + if user.picture_url + img( + src= user.picture_url || '/img/default-member.png', + title="Member Menu", + ).profile-navbar + else + include missing-profile-icon + div(uk-dropdown={ mode: 'click' }).uk-navbar-dropdown + ul.uk-nav.uk-navbar-dropdown-nav(style="z-index: 1024;") + li.uk-nav-heading.uk-text-center= user.displayName || user.username + li.uk-nav-divider + if (user.channel) li - a(href=`/user/${user._id}`) + a(href=`/channel/${user.channel.slug}`) span.nav-item-icon - i.fas.fa-user - span Profile - li - a(href=`/user/${user._id}/settings`) - span.nav-item-icon - i.fas.fa-cog - span Settings - - if user.flags && user.flags.isAdmin - li.uk-nav-divider + i.fas.fa-broadcast-tower + span(style="max-width: 120px;").uk-text-truncate= user.channel.name + if (user.channel.liveEpisode) li - a(href='/admin') + a(href=`/channel/${user.channel.slug}/broadcaster`) span.nav-item-icon - i.fas.fa-user-lock - span Admin + i.fas.fa-box-open + span Broadcaster + li + a(href='/dashboard') + span.nav-item-icon + i.fas.fa-tachometer-alt + span Dashboard li.uk-nav-divider + li + a(href=`/user/${user._id}`) + span.nav-item-icon + i.fas.fa-user + span Profile + li + a(href=`/user/${user._id}/settings`) + span.nav-item-icon + i.fas.fa-cog + span Settings + + if user.flags && user.flags.isAdmin + li.uk-nav-divider li - a(href='/auth/logout') + a(href='/admin') span.nav-item-icon - i.fas.fa-sign-out-alt - span Logout - else - ul.uk-navbar-nav + i.fas.fa-user-lock + span Admin + + li.uk-nav-divider + li - a(href='/welcome').uk-button.uk-button-link + a(href='/auth/logout') span.nav-item-icon - i.fas.fa-sign-in-alt - span(class="uk-visible@m").uk-margin-small-left GET STARTED! \ No newline at end of file + i.fas.fa-sign-out-alt + span Logout + else + ul.uk-navbar-nav + li + a(href='/welcome').uk-button.uk-button-link + span.nav-item-icon + i.fas.fa-sign-in-alt + span(class="uk-visible@m").uk-margin-small-left GET STARTED! \ No newline at end of file diff --git a/app/views/index.pug b/app/views/index.pug index 218bd36..b8d965d 100644 --- a/app/views/index.pug +++ b/app/views/index.pug @@ -50,7 +50,8 @@ block content //- Gab TV 3 Most Recent Episodes .uk-margin .dtp-border-bottom - h3.uk-heading-bullet Gab TV + h3.uk-heading-bullet + a(href= gabTvChannel.home_page_url, target= "_blank", title= `${gabTvChannel.title} on Gab`).uk-link-reset Gab TV ul.uk-list each episode in gabTvChannel.items.slice(0, 3) li diff --git a/app/views/otp/welcome.pug b/app/views/otp/welcome.pug index 1ec18be..2d4bb84 100644 --- a/app/views/otp/welcome.pug +++ b/app/views/otp/welcome.pug @@ -18,7 +18,7 @@ block content .uk-margin canvas(id="otp-qrcode", width="480", height="480") .uk-margin - a(href=otpKeyURI, title="Add to Authenticator App").uk-button.uk-button-default.uk-border-pill Add to authenticator + a(href=otpKeyURI, title="Add to Authenticator App").uk-button.dtp-button-default.uk-border-pill Add to authenticator div(class="uk-width-1-1 uk-width-expand@s uk-text-center uk-text-left@m") .uk-margin @@ -39,7 +39,7 @@ block content ).uk-input.uk-form-large.uk-text-center.uk-width-1-2 .uk-margin - button(type="submit").uk-button.uk-button-primary.uk-border-pill Enable 2FA + button(type="submit").uk-button.dtp-button-primary.uk-border-pill Enable 2FA div(class="uk-width-1-1 uk-text-center uk-text-left@m", hidden) .uk-margin