working on discord

This commit is contained in:
ansible user/allowed to read system logs 2023-08-01 20:13:11 -07:00
parent e9e5e8f7ec
commit 8504775ff3
17 changed files with 195 additions and 54 deletions

View File

@ -14,6 +14,7 @@ Very unfinished!! Mostly a programming exercise for me for now to practice fulls
- [ ] Sanitize user inputs
- [ ] Check status of matterbridge processes
- [ ] Complete slack login workflow
- [ ] Kill group processes & delete config when group is deleted
## Supported Clients

View File

@ -0,0 +1,7 @@
export const getDiscordInstallURL = (callback: CallableFunction) => {
fetch('api/discord/install')
.then(res => res.json())
.then(res => {
callback(res.data.url)
})
}

View File

@ -10,4 +10,18 @@ export const groupInvite = (token: string, callback: CallableFunction) => {
})
.then(result => result.json())
.then(result => callback(result))
}
export const deleteGroup = (id: string, callback: CallableFunction) => {
fetch('api/groups', {
method: "DELETE",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
id: id
})
})
.then(result => result.json())
.then(result => callback(result))
}

View File

@ -9,10 +9,12 @@ import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import Typography from "@mui/material/Typography";
import GroupRow from "./groupRow";
export default function GroupPanel({
groups
groups,
fetchGroups
}){
return(
@ -24,6 +26,7 @@ export default function GroupPanel({
<TableCell>Group</TableCell>
<TableCell align="right">Created At</TableCell>
<TableCell align="right">Invite Token</TableCell>
<TableCell align="right">Delete</TableCell>
</TableRow>
</TableHead>
<TableBody>
@ -34,6 +37,7 @@ export default function GroupPanel({
id={group.id}
invite_token={group.invite_token}
created_at={group.created_at}
fetchGroups={fetchGroups}
></GroupRow>
)) : undefined}
</TableBody>

View File

@ -2,17 +2,30 @@ import TableCell from '@mui/material/TableCell';
import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
import IconButton from "@mui/material/IconButton"
import {deleteGroup} from "../../api/groups";
export interface GroupRowProps {
name: string;
id: string;
invite_token: string;
created_at: string;
fetchGroups: CallableFunction;
}
export default function GroupRow(
{name, id, invite_token, created_at}: GroupRowProps
{name, id, invite_token, created_at, fetchGroups}: GroupRowProps
){
const handleDeleteGroup = () => {
deleteGroup(id, deleteGroupCallback)
}
const deleteGroupCallback = () => {
fetchGroups()
}
return(
<TableRow
key={id}
@ -23,6 +36,13 @@ export default function GroupRow(
</TableCell>
<TableCell align="right">{created_at}</TableCell>
<TableCell align="right">{invite_token}</TableCell>
<TableCell align="right">
<IconButton
onClick={handleDeleteGroup}
>
<DeleteForeverIcon/>
</IconButton>
</TableCell>
</TableRow>
)

View File

@ -9,9 +9,11 @@ import FormControl from '@mui/material/FormControl';
import Select from '@mui/material/Select';
import {SlackLogin} from "../platforms/slackLogin";
import {DiscordLogin} from "../platforms/discordLogin";
enum PLATFORMS {
Slack = 'Slack'
Slack = 'Slack',
Discord = 'Discord'
}
export const JoinLogin = ({
@ -22,7 +24,6 @@ export const JoinLogin = ({
stepComplete,
setStepComplete
}) => {
const [loginComponent, setLoginComponent] = useState(<></>);
const handleSelect = (event) => {
@ -30,19 +31,6 @@ export const JoinLogin = ({
setPlatform(event.target.value);
}
useEffect(() => {
switch(platform){
case "Slack":
setLoginComponent(<SlackLogin
bridge={bridge}
setBridge={setBridge}
/>)
default:
setLoginComponent(<></>)
}
}, [platform])
useEffect(() => {
if (bridge !== undefined) {
setStepComplete({...stepComplete, login:true})
@ -60,11 +48,20 @@ export const JoinLogin = ({
label={"Select Platform"}
>
<MenuItem value={'Slack'}>Slack</MenuItem>
<MenuItem value={'Discord'}>Discord</MenuItem>
</Select>
</FormControl>
{
platform ? loginComponent : <div style={{width: "50%"}}></div>
platform === "Slack" ? <SlackLogin
bridge={bridge}
setBridge={setBridge}
/>
: platform === "Discord" ? <DiscordLogin
bridge={bridge}
setBridge={setBridge}
/>
: <div style={{width: "50%"}}></div>
}
</div>

View File

@ -135,6 +135,7 @@ export default function ManagePanel(){
loggedIn ?
<GroupPanel
groups={groups}
fetchGroups={fetchGroups}
/>
:
null

View File

@ -0,0 +1,56 @@
import {useEffect, useRef, useState} from "react";
import {getDiscordInstallURL} from "../../api/discord";
import {getBridgeByStateToken} from "../../api/bridge";
import Button from "@mui/material/Button";
export const DiscordLogin = ({
bridge,
setBridge
}) => {
const [installLink, setInstallLink] = useState();
const [installStarted, setInstallStarted] = useState(false)
const pingTimeout = useRef(null);
const openInstallTab = () => {
window.open(installLink, '_blank').focus();
setInstallStarted(true)
}
useEffect(() => {
getDiscordInstallURL(handleInstallLink)
}, [])
const handleInstallLink = (url) => {
setInstallLink(url);
}
useEffect(() => {
const pingForBridge = () => {
if (bridge === undefined) {
getBridgeByStateToken(setBridge);
pingTimeout.current = setTimeout(pingForBridge, 1000);
}
}
if (installStarted) {
if (bridge === undefined){
pingForBridge()
}
}
return () => {clearInterval(pingTimeout.current)}
}, [installStarted, bridge])
return(
<Button
variant={"outlined"}
onClick={openInstallTab}
color={bridge !== undefined ? 'success': undefined}
disabled={installLink === undefined}
>
{installLink === undefined ? 'Waiting for Install Link...' : 'Add to Slack'}
</Button>
)
}

View File

@ -6,7 +6,7 @@ import {getBridgeByStateToken} from "../../api/bridge";
export const SlackLogin = ({
bridge,
setBridge
}) => {
}) => {
const [installLink, setInstallLink] = useState();
const [installStarted, setInstallStarted] = useState(false)
const pingTimeout = useRef(null);

View File

@ -21,7 +21,6 @@ export const checkAdminToken = async(
});
} else {
req.session.logged_in = false;
logger.warning("Login with admin token failed")
return res.status(403).json({
status:'fail',

View File

@ -20,6 +20,13 @@ export const DiscordInstallLinkHandler = async(
const url = `https://discordapp.com/oauth2/authorize?&client_id=${discordConfig.client_id}&scope=bot&permissions=536870912&state=${state_token}`
res.status(200).json({
status: 'success',
data: {
url
}
})
}

View File

@ -86,6 +86,26 @@ export const getGroupWithInviteHandler = async(
}
export const deleteGroupHandler = async(
req: Request,
res:Response,
) => {
let group = await groupRepository.findOneBy({id: req.body.id})
if (group){
await groupRepository.remove(group)
return res.status(200).json({
status: 'success',
message: 'Group successfully deleted!'
})
} else {
return res.status(404).json({
status: 'failed',
message: 'no group with matching ID found'
})
}
}
export const getGroupsHandler = async(
req: Request,
res: Response

View File

@ -1,20 +1,28 @@
const fs = require('fs')
const path = require('path')
import config from "config";
import { Logger, format, createLogger, transports } from 'winston';
// import { Logger, format, createLogger, transports } from 'winston';
const winston = require('winston');
let Logger = winston.Logger
let format = winston.format
let createLogger = winston.createLogger
let transports = winston.transports
const logDir = config.get<string>('logDir')
const makeLogdir = () => {
fs.mkdir(logDir, (err:Error|undefined) => {
if (err) throw err;
})
if (!fs.existsSync(logDir)){
fs.mkdir(logDir, (err:Error|undefined) => {
if (err) throw err;
})
}
}
const makeLogger = (): Logger => {
const makeLogger = (): typeof Logger => {
makeLogdir()
const logger = createLogger({
levels: winston.config.syslog.levels,
level: process.env.NODE_ENV === 'development' ? 'debug' : 'info',
format: format.combine(
format.timestamp({
@ -26,11 +34,11 @@ const makeLogger = (): Logger => {
),
transports: [
new transports.File({
filename: path.join([logDir, 'chatbridge.error.log']),
filename: path.join(logDir, 'chatbridge.error.log'),
level: 'error'
}),
new transports.File({
filename: path.join([logDir, 'chatbridge.debug.log']),
filename: path.join(logDir, 'chatbridge.debug.log'),
level: 'debug'
}),
...(process.env.NODE_ENV === 'development' ? [

View File

@ -175,7 +175,7 @@ export const writeTOML = (gateway_toml: object, out_file: string) => {
}
)
logger.debug('toml string', toml_string)
// logger.debug('toml string', toml_string)
fs.writeFileSync(out_file, toml_string)

View File

@ -58,30 +58,35 @@ class MatterbridgeManager {
let group_name_slug = slugify(group_name)
let group_filename = `${this.matterbridge_config_dir}/matterbridge-${group_name_slug}.toml`
await writeGroupConfig(group_name, group_filename);
if (!this.process_list.includes(group_name_slug)){
logger.info('Spawning new matterbridge process: %s', group_name_slug)
await pm2.start(
{
name: group_name_slug,
script: this.matterbridge_bin,
args: `-conf ${group_filename}`,
interpreter: 'none'
},
(err:any, apps:object) => {
if (err) {
logger.error('error starting matterbridge process', err, apps)
}
}
)
this.process_list.push(group_name_slug)
} else {
logger.info('Restarting existing matterbridge process: %s', group_name_slug)
await pm2.restart(group_name_slug, (err:any, proc:any) => {
if (err) {
logger.error('error restarting matterbridge process', err)
}}
)
}
pm2.connect(async(err:any) => {
if (!this.process_list.includes(group_name_slug)) {
logger.info('Spawning new matterbridge process: %s', group_name_slug)
await pm2.start(
{
name: group_name_slug,
script: this.matterbridge_bin,
args: `-conf ${group_filename}`,
interpreter: 'none'
},
(err: any, apps: object) => {
if (err) {
logger.error('error starting matterbridge process', err, apps)
}
}
)
this.process_list.push(group_name_slug)
} else {
logger.info('Restarting existing matterbridge process: %s', group_name_slug)
await pm2.restart(group_name_slug, (err: any, proc: any) => {
if (err) {
logger.error('error restarting matterbridge process', err)
}
}
)
}
// await pm2.disconnect()
})
}
async refreshConfig(group_name: string) {
@ -111,7 +116,7 @@ class MatterbridgeManager {
status: proc.pm2_env.status,
monit: proc.monit,
pm2_env: {
created_at: proc.pm2_env.proc.pm2_env.created_at,
created_at: proc.pm2_env.created_at,
exec_interpreter: proc.pm2_env.exec_interpreter,
exec_mode: proc.pm2_env.exec_mode,
instances: proc.pm2_env.instances,

View File

@ -3,7 +3,8 @@ import express from 'express';
import {
createGroupHandler,
getGroupHandler,
getGroupWithInviteHandler
getGroupWithInviteHandler,
deleteGroupHandler
} from "../controllers/group.controller";
import {
@ -19,6 +20,7 @@ router
.route('/')
.post(validate(createGroupSchema), requireAdmin, createGroupHandler)
.get(requireAdmin, getGroupHandler)
.delete(requireAdmin, deleteGroupHandler)
router
.route('/invite')