Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port Sweetgreen to tracker #3

Draft
wants to merge 1 commit into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ TW_CONSUMER_KEY=
TW_CONSUMER_SECRET=
TW_ACCESS_KEY=
TW_ACCESS_SECRET=
ENABLE_WEBMIN=true
WEBMIN_PORT=3030
WEBMIN_USER=
WEBMIN_PASS=
GH_TOKEN=
Expand Down
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@
"license": "AGPL-3.0-or-later",
"dependencies": {
"@cryb/mesa": "^1.5.8",
"@types/express": "^4.17.13",
"@types/node-cron": "^3.0.0",
"axios": "^0.24.0",
"cpx": "^1.5.0",
"discord.js": "^13.3.1",
"dotenv": "^10.0.0",
"express": "^4.17.1",
"express-basic-auth": "^1.2.0",
"node-cron": "^3.0.0",
"pug": "^3.0.2",
"telegraf": "^4.4.2",
"ts-node": "^10.4.0",
"twitter-lite": "^1.1.0",
Expand All @@ -29,6 +34,7 @@
},
"scripts": {
"build": "tsc",
"postbuild": "cpx \"./src/sweetgreen/views/**/*.pug\" ./dist/sweetgreen/views && cpx \"./src/sweetgreen/assets/**/*\" ./dist/sweetgreen/assets",
"test": "jest"
}
}
13 changes: 12 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,18 @@ const timeoutTime = parseInt((process.env.TIMEOUT_TIME as string))
let cycle = 0

debug('HypeTrack started on %s.', new Date())
// TODO: Web and Mesa.
// TODO: Mesa.
if ((process.env.ENABLE_WEBMIN as string).toLowerCase() === 'true') {
const sweetgreenDebug = debug.extend('sweetgreen')
sweetgreenDebug('Importing Sweetgreen...')
const { default: sweetgreen } = await import('./sweetgreen/index.js')

const port = parseInt((process.env.WEBMIN_PORT as string))

sweetgreen.listen(port, () => {
sweetgreenDebug('Listening at %s.', `http://localhost:${port}`)
})
}

// Initialize DB2.
await init()
Expand Down
Binary file added src/sweetgreen/assets/scott.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
80 changes: 80 additions & 0 deletions src/sweetgreen/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import dotenv from 'dotenv'
dotenv.config()

import express from 'express'
import { URL } from 'url'
import type { Request, Response } from 'express'
import basicAuth from 'express-basic-auth'

import { tweet } from '../utils/twitter.js'
import { tg } from '../utils/telegram.js'
import { get } from '../utils/db2.js'

const app = express()

app.set('view engine', 'pug')
// Workaround for __dirname not being defined in an ES module scope.
app.set('views', new URL('.', import.meta.url).pathname + 'views')
app.use(express.json())
app.use(basicAuth({
challenge: true,
users: {
[(process.env.WEBMIN_USER as string)]: (process.env.WEBMIN_PASS as string)
},
realm: 'sweetgreen'
}))
app.use('/assets', express.static(new URL('.', import.meta.url).pathname + 'assets'))

app.get('/', async (req: Request, res: Response): Promise<void> => {
const prodApi = await get<string>('lastRevision_hypeapi')
const prodWebSocket = await get<string>('lastRevision_hypeapi-websocket')
const prodTelemetry = await get<string>('lastRevision_api-telemetry')

res.render('dash', {
ts: new Date().toISOString(),
hypeapi: prodApi,
hypeapi_ws: prodWebSocket,
hypeapi_tm: prodTelemetry
})
})

app.get('/post-as', async (req: Request, res: Response): Promise<void> => {
res.render('sus')
})

app.post('/api/twt', async (req, res) => {
const status = req.body.status
console.log(status)
try {
await tweet(status)
res.status(200).send({
success: true,
status
})
} catch (error: any) {
console.log(error)
return res.status(500).send({
error: error.message
})
}
})

app.post('/api/tg', async (req, res) => {
const msg = req.body.msg
console.log(msg)

try {
await tg(msg)

res.status(200).send({
success: true,
message: msg
})
} catch (error: any) {
return res.status(500).send({
error: error.message
})
}
})

export default app
39 changes: 39 additions & 0 deletions src/sweetgreen/views/_partials/header.pug
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
doctype html
html(lang="en")
head
meta(charset="UTF-8")
meta(name="viewport", content="width=device-width, initial-scale=1.0")
link(href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet")
title HypeTrack Admin
script.
// embold active link
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('a').forEach(el => {
if (el.href === location.href) el.classList.add('font-medium')
})
})
block data
body
.bg-gray-900
.container.mx-auto.max-w-6xl.px-4.py-2
nav.flex.space-x-4
.flex.space-x-2.self-center
img(src="/assets/scott.png" class="w-6 h-6 rounded-lg self-center")
h1.font-bold.text-lg.text-white Sweetgreen
.flex-1.self-center.space-x-4.text-white
a.font-normal.text-md(href='/') Dashboard
a.font-normal.text-md(href='/post-as') Post as HypeTrack
//- TODO: These will probably eventually be a thing.
//- a.font-normal.text-md(href='/streams') Streams
//- a.font-normal.text-md(href='/db2') DB2
.flex.items-end.self-center.space-x-2
.relative.self-center
.bg-yellow-400.w-2.h-2.rounded-full.self-center
.bg-yellow-400.w-2.h-2.rounded-full.self-center.absolute.top-0
p.font-bold.text-white.text-sm Connecting...

div(class="bg-gray-100 mb-4")
div(class="px-4 py-8 container mx-auto max-w-6xl")
block head
.px-4.py-2.container.mx-auto.max-w-6xl
block content
105 changes: 105 additions & 0 deletions src/sweetgreen/views/dash.pug
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
extends _partials/header.pug
block data
//- script(src="https://cdn.jsdelivr.net/npm/mesa-js-client@latest/dist/browser/client.js")
script.
// TODO: This entire thing needs to be rewritten... eugh
/** hydrated #{locals.ts} */
window.hypetrack = window.hypetrack || {};
hypetrack.dashdat = hypetrack.dashdat || {
ts: '#{locals.ts}',
"hypeapi": '#{locals.hypeapi}',
"hypeapi-websocket": '#{locals.hypeapi_ws}',
"api-telemetry": '#{locals.hypeapi_tm}',
//- "hypeapi-loadtest": '#{locals.hypeapi_lt}',
};

document.addEventListener('DOMContentLoaded', () => {
['hypeapi', 'hypeapi-websocket', 'api-telemetry' /*, 'hypeapi-loadtest' */].forEach(el => {
const card = document.createElement('div')
card.classList.add('flex-row', 'bg-gray-100', 'shadow-lg', 'rounded-lg', 'space-y-2', 'p-4', 'border-2', 'border-gray-400')
card.dataset.apiName = el

const apiName = document.createElement('h1')
apiName.classList.add('font-bold', 'text-md')
apiName.textContent = el

const rev = document.createElement('p')
rev.classList.add('font-mono', 'text-sm')
rev.textContent = hypetrack.dashdat[el]

card.insertAdjacentElement('beforeend', apiName)
card.insertAdjacentElement('beforeend', rev)

document.querySelector('[data-root]').append(card)
})

// Mesa specific thing, comment out when Mesa is ported in.
document.querySelector("body > div.bg-gray-900 > div > nav > div.flex.items-end.self-center.space-x-2").remove()
})

hypetrack.updateRevision = (name, revision) => {
const elm = document.querySelector(`div[data-api-name=${name}]`)

if (elm === null) {
// silently fail
console.warn('Mesa returned non-existant thing')
return
}

const revElm = document.querySelector(`div[data-api-name=${name}] > p`)

revElm.textContent = revision
}

hypetrack.updateTimestamp = ts => document.querySelector('span[data-last-update]').textContent = `last updated ${ts}`

/*
const client = new MesaClient('ws://localhost:4000')
client.onConnected = () => {
const statusText = document.querySelector('body > div.bg-gray-900 > div > nav > div.flex.items-end.self-center.space-x-2 > p')
const statusLed = document.querySelector('body > div.bg-gray-900 > div > nav > div.flex.items-end.self-center.space-x-2 > div > div.bg-yellow-400.w-2.h-2.rounded-full.self-center.absolute.top-0')
const statusLed2 = document.querySelector("body > div.bg-gray-900 > div > nav > div.flex.items-end.self-center.space-x-2 > div > div:nth-child(1)")
console.log('client connected, polling')

statusText.textContent = "Online, polling with Mesa"
statusLed.classList.remove('bg-yellow-400')
statusLed2.classList.remove('bg-yellow-400')
statusLed2.classList.add('bg-green-400')
statusLed.classList.add('bg-green-400', 'animate-ping')
}

client.onError = e => {
const statusText = document.querySelector('body > div.bg-gray-900 > div > nav > div.flex.items-end.self-center.space-x-2 > p')
const statusLed = document.querySelector('body > div.bg-gray-900 > div > nav > div.flex.items-end.self-center.space-x-2 > div > div.bg-yellow-400.w-2.h-2.rounded-full.self-center.absolute.top-0')
const statusLed2 = document.querySelector("body > div.bg-gray-900 > div > nav > div.flex.items-end.self-center.space-x-2 > div > div:nth-child(1)")
console.log('error', e)

statusText.textContent = "Offline"
statusLed.classList.remove('bg-yellow-400')
statusLed.classList.add('bg-red-400')
statusLed2.classList.remove('bg-yellow-400')
statusLed2.classList.add('bg-red-400')
}

client.onMessage = ({ data, type }) => {
switch (type) {
case 'NEW_REVISION':
hypetrack.updateRevision(data.name, data.revision)
hypetrack.updateTimestamp(data.ts)
break
}
}
*/
block head
.flex
div(class="w-1/2")
h1(class="font-bold text-3xl") Dashboard
p(class="font-normal text-lg") See how often (#[em never]) HQ updates their server
div(class="w-1/2 text-right self-center")
h3(class="font-bold text-sm text-gray-500") Module
p(class="font-mono text-xs text-gray-500") sweetgreen-dash-v2
block content
div(class="grid grid-cols-3 grid-rows-1 gap-4" data-root)
span(class="float-right text-gray-400 text-sm mt-8" data-last-update) last updated #{locals.ts}


71 changes: 71 additions & 0 deletions src/sweetgreen/views/sus.pug
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
extends _partials/header.pug
block data
script(src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" referrerpolicy="no-referrer")
link(rel="stylesheet", href="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css")
script(src="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js")
script(src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js" integrity="sha512-bZS47S7sPOxkjU/4Bt0zrhEtWx0y0CRkhEp8IckzK+ltifIIE9EMIMTuT/mEzoIMewUINruDBIR/jJnbguonqQ==" crossorigin="anonymous" referrerpolicy="no-referrer")
script.
/** global axios */
document.addEventListener('DOMContentLoaded', () => {
document.querySelector("body > div.bg-gray-900 > div > nav > div.flex.items-end.self-center.space-x-2").remove()
const twtForm = document.querySelector('form#twitter-post')
const tgForm = document.querySelector('form#telegram-post')

twtForm.addEventListener('submit', async (e) => {
e.preventDefault()
const status = document.querySelector('textarea#status')
document.querySelector('input#twsubmit').setAttribute('disabled', true)

try {
await axios.post('/api/twt', {
status: status.value
})
toastr.success('Posted to Twitter.')
status.value = ""
} catch (error) {
toastr.error('Error occurred internally!')
}

document.querySelector('input#twsubmit').setAttribute('disabled', true)
})

tgForm.addEventListener('submit', async (e) => {
e.preventDefault()
const msg = document.querySelector('textarea#tg_status')
document.querySelector('input#tgsubmit').setAttribute('disabled', true)

try {
await axios.post('/api/tg', {
msg: msg.value
})
toastr.success('Posted to Telegram.')
msg.value = ""
} catch (error) {
toastr.error('Error occurred internally!')
}

document.querySelector('input#tgsubmit').setAttribute('disabled', false)
})
})
block head
div(class="flex")
div(class="w-1/2")
h1(class="font-bold text-3xl") Post as HypeTrack
p(class="font-normal text-lg") Impersonate HypeTrack and post on the socials
aside(class="w-1/2 text-right self-center")
h2(class="font-bold text-sm text-gray-500") Module
p(class="font-mono text-xs text-gray-500") sweetgreen-sus
block content
div(class="flex bg-yellow-400 text-black px-4 py-2 mb-4 rounded-md shadow-md")
span #[strong You're in #{process.env.NODE_ENV || 'production'} right now.] I hope you know what you're doing.
h3(class="text-xl font-bold") Twitter
form(action="/api/twt", method="POST", id="twitter-post", class="space-y-2")
label(for="status", class="text-md font-medium") Tweet Text
textarea(name="status", id="status", class="block border border-gray-400 w-full p-4 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-400", maxlength="260")
input(type="submit", id="twsubmit" value="Submit", class="px-2 py-1 rounded-md bg-blue-600 text-white font-medium float-right")
div(class="clear-right")
h3(class="text-xl font-bold") Telegram
form(action="/api/tg", method="POST", id="telegram-post", class="space-y-2")
label(for="tg_status", class="text-md font-medium") Post Text
textarea(id="tg_status", name="msg", class="block border border-gray-400 w-full p-4 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-400")
input(type="submit", id="tgsubmit", value="Submit", class="px-2 py-1 rounded-md bg-blue-600 text-white font-medium float-right")
Loading