working group controller

This commit is contained in:
ansible user/allowed to read system logs 2023-07-24 18:40:22 -07:00
parent bac7b6f275
commit 22340d8a96
19 changed files with 253 additions and 12 deletions

1
.gitignore vendored
View file

@ -146,3 +146,4 @@ dist
# misc # misc
.DS_Store .DS_Store
.idea

View file

@ -1,5 +1,7 @@
# chatbridge # chatbridge
Testing running
Invite people to a bridged nextcloud chat with a token Invite people to a bridged nextcloud chat with a token
Using matterbridge - https://github.com/42wim/matterbridge Using matterbridge - https://github.com/42wim/matterbridge

View file

@ -0,0 +1,11 @@
export default {
port: 'PORT',
postgresConfig: {
host: 'POSTGRES_HOST',
port: 'POSTGRES_PORT',
username: 'POSTGRES_USER',
password: 'POSTGRES_PASSWORD',
database: 'POSTGRES_DB',
},
admin_token: 'ADMIN_TOKEN'
}

7
config/defaults.ts Normal file → Executable file
View file

@ -1,10 +1,3 @@
export default { export default {
port: '8999', port: '8999',
postgresConfig: {
host: '127.0.0.1',
port: '6500',
username: 'POSTGRES_USER',
password: 'POSTGRES_PASSWORD',
database: 'POSTGRES_DB',
},
} }

View file

@ -6,3 +6,5 @@ POSTGRES_PORT=6500
POSTGRES_USER= POSTGRES_USER=
POSTGRES_PASSWORD= POSTGRES_PASSWORD=
POSTGRES_DB= POSTGRES_DB=
ADMIN_TOKEN=

View file

@ -7,7 +7,7 @@
"author": "sneakers-the-rat <JLSaunders987@gmail.com>", "author": "sneakers-the-rat <JLSaunders987@gmail.com>",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"scripts": { "scripts": {
"start": "ts-node-dev --respawn --transpile-only --exit-child src/app.ts", "start": "ts-node-dev --respawn --transpile-only --exit-child server/app.ts",
"build": "tsc -p .", "build": "tsc -p .",
"typeorm": "typeorm-ts-node-commonjs", "typeorm": "typeorm-ts-node-commonjs",
"migrate": "rm -rf build && yarn build && yarn typeorm migration:generate ./src/migrations/added-user-entity -d ./src/utils/data-source.ts", "migrate": "rm -rf build && yarn build && yarn typeorm migration:generate ./src/migrations/added-user-entity -d ./src/utils/data-source.ts",
@ -32,6 +32,7 @@
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-scripts": "^5.0.1", "react-scripts": "^5.0.1",
"redis": "^4.6.7", "redis": "^4.6.7",
"reflect-metadata": "^0.1.13",
"typeorm": "^0.3.17", "typeorm": "^0.3.17",
"typescript": "^5.1.6", "typescript": "^5.1.6",
"zod": "^3.21.4" "zod": "^3.21.4"

View file

@ -1,3 +1,5 @@
require('dotenv').config(); require('dotenv').config();
import express, { NextFunction, Request, Response } from 'express'; import express, { NextFunction, Request, Response } from 'express';
import config from 'config'; import config from 'config';
@ -5,6 +7,7 @@ import cors from 'cors';
import { AppDataSource } from './db/data-source'; import { AppDataSource } from './db/data-source';
import AppError from './errors/appError'; import AppError from './errors/appError';
import groupRoutes from "./routes/group.routes";
@ -12,9 +15,23 @@ AppDataSource.initialize()
.then(async () => { .then(async () => {
const app = express(); const app = express();
app.use(express.json({limit: "10kb"}));
app.get('/healthcheck', async (_, res: Response) => {
res.status(200).json({
status: "online!!!!"
})
})
app.use('/groups', groupRoutes);
app.all('*', (req: Request, res: Response, next: NextFunction) => { app.all('*', (req: Request, res: Response, next: NextFunction) => {
next(new AppError(404, `Route ${req.originalUrl} not found`)); next(new AppError(404, `Route ${req.originalUrl} not found`));
}); });
const port = config.get<number>('port');
app.listen(port);
console.log(`Server started on port: ${port}`)
}) })

View file

View file

View file

@ -0,0 +1,52 @@
import {NextFunction, Request, Response} from 'express';
import {CreateGroupInput, GetGroupInput, getGroupSchema} from "../schemas/group.schema";
import config from 'config';
import {Group} from "../entities/group.entity";
import AppError from "../errors/appError";
import {AppDataSource} from "../db/data-source";
const groupRepository = AppDataSource.getRepository(Group)
export const createGroupHandler = async(
req: Request<{}, {}, CreateGroupInput>,
res: Response
) => {
console.log(req.body);
const admin_token = config.get<string>('admin_token');
if (req.body.token !== admin_token){
return res.status(403).json({
status: 'fail',
message: 'Not authorized to create group without correct admin token'
})
}
let group = await groupRepository.create({...req.body})
let result = await groupRepository.save(group)
return res.send(result)
}
export const getGroupHandler = async(
req: Request<GetGroupInput>,
res: Response,
next: NextFunction
) => {
try {
let group = await groupRepository.findOneBy({name: req.params.name})
if (!group) {
return next(new AppError(404, "No group with matching name exists"))
}
return res.status(200).json({
status: 'success',
data: {
group
}
})
} catch (err: any) {
next(err);
}
};

View file

@ -14,7 +14,7 @@ const postgresConfig = config.get<{
export const AppDataSource = new DataSource({ export const AppDataSource = new DataSource({
...postgresConfig, ...postgresConfig,
type: 'postgres', type: 'postgres',
synchronize: false, synchronize: true,
logging: false, logging: false,
entities: ['server/entities/**/*.entity{.ts,.js}'], entities: ['server/entities/**/*.entity{.ts,.js}'],
migrations: ['server/migrations/**/*{.ts,.js}'], migrations: ['server/migrations/**/*{.ts,.js}'],

View file

@ -0,0 +1,55 @@
import { Entity, Column, Index, OneToMany, TableInheritance, ChildEntity } from 'typeorm';
import Model from './model.entity';
import { Channel } from "./channel.entity";
export enum BridgeEnumType {
SLACK = 'slack',
DISCORD = 'discord',
MATRIX = 'matrix'
}
@Entity('bridges')
@TableInheritance({column: { type: "varchar", name: "type" }})
export class Bridge extends Model {
@Column({
type: "enum",
enum: BridgeEnumType
})
Protocol: string;
@Column()
Label: string;
@Column({
default: true
})
PrefixMessagesWithNick: boolean;
// See https://github.com/42wim/matterbridge/wiki/Settings#prefixmessageswithnick
@Column({
default: "[{LABEL}] <{NICK}> "
})
RemoteNickFormat: string;
@Column({
unique: true
})
Token: string;
@OneToMany(() => Channel, (channel) => channel.bridge)
channels: Channel[]
}
@ChildEntity()
export class MatrixBridge extends Bridge {
// Matrix-specific
@Column()
Server: string;
@Column()
Login: string;
@Column()
Password: string;
}

View file

@ -0,0 +1,18 @@
import { Entity, Column, Index, ManyToOne, OneToOne } from 'typeorm';
import Model from './model.entity';
import { Bridge } from "./bridge.entity";
import { Group } from "./group.entity";
@Entity('channels')
export class Channel extends Model {
@Column()
name: string;
@ManyToOne(() => Bridge, (bridge) => bridge.channels)
bridge: Bridge
@ManyToOne(() => Group, (group) => group.channels)
group: Group
}

View file

@ -0,0 +1,20 @@
import { Entity, Column, Index, OneToMany } from 'typeorm';
import Model from './model.entity';
import { Channel } from "./channel.entity";
@Entity('groups')
export class Group extends Model {
@Column({
unique: true
})
name: string;
@Column({
default: true
})
enable: boolean;
@OneToMany(() => Channel, (channel) => channel.bridge)
channels: Channel[]
}

View file

@ -14,4 +14,6 @@ export default abstract class Model extends BaseEntity {
@UpdateDateColumn() @UpdateDateColumn()
updated_at: Date; updated_at: Date;
} }

View file

@ -0,0 +1,24 @@
import { Request, Response, NextFunction } from 'express';
import { AnyZodObject, ZodError } from 'zod';
export const validate =
(schema: AnyZodObject) =>
(req: Request, res: Response, next: NextFunction) => {
try {
schema.parse({
params: req.params,
query: req.query,
body: req.body,
});
next();
} catch (error) {
if (error instanceof ZodError) {
return res.status(400).json({
status: 'fail',
errors: error.errors,
});
}
next(error);
}
};

View file

@ -0,0 +1,21 @@
import express from 'express';
import {
createGroupHandler,
getGroupHandler
} from "../controllers/group.controller";
import {
createGroupSchema
} from "../schemas/group.schema";
import { validate } from "../middleware/validate";
const router = express.Router();
router
.route('/')
.post(createGroupHandler)
.get(getGroupHandler)
export default router

View file

@ -0,0 +1,21 @@
import { object, string, TypeOf } from 'zod';
export const createGroupSchema = object({
body: object({
name: string({
required_error: "Name for group required"
}),
token: string({
required_error: "Administration token required for creating groups"
})
})
})
export const getGroupSchema = object({
name: string({
required_error: "Name of group required to get group"
})
})
export type CreateGroupInput = TypeOf<typeof createGroupSchema>['body'];
export type GetGroupInput = TypeOf<typeof getGroupSchema>;

View file

@ -11,7 +11,8 @@
"skipLibCheck": true, "skipLibCheck": true,
"outDir": "./build", "outDir": "./build",
"rootDir": ".", "rootDir": ".",
},
"include": ["server/**/*"], "include": ["server/**/*"],
"exclude": ["node_modules", "roles"] "exclude": ["node_modules", "roles"]
} }
}