Add logging and fix channel merging and removing
parent
7055713d4b
commit
3575c910f4
|
@ -1,5 +1,6 @@
|
||||||
import type { Request, Response } from "express";
|
import type { Request, Response } from "express";
|
||||||
import * as ChannelService from "../services/channel-service";
|
import * as ChannelService from "../services/channel-service";
|
||||||
|
import { logger } from "../globals";
|
||||||
|
|
||||||
export const createChannel = async (req: Request, res: Response) => {
|
export const createChannel = async (req: Request, res: Response) => {
|
||||||
const { name } = req.body;
|
const { name } = req.body;
|
||||||
|
@ -7,7 +8,7 @@ export const createChannel = async (req: Request, res: Response) => {
|
||||||
return res.status(400).json({ error: 'Name is required' });
|
return res.status(400).json({ error: 'Name is required' });
|
||||||
}
|
}
|
||||||
const chan = await ChannelService.createChannel(name);
|
const chan = await ChannelService.createChannel(name);
|
||||||
|
logger.info(`Channel ${name} created`);
|
||||||
res.json(chan);
|
res.json(chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,8 +20,10 @@ export const deleteChannel = async (req: Request, res: Response) => {
|
||||||
const result = await ChannelService.deleteChannel(channelId);
|
const result = await ChannelService.deleteChannel(channelId);
|
||||||
|
|
||||||
if (result.changes === 0) {
|
if (result.changes === 0) {
|
||||||
|
logger.warn(`Channel ${channelId} not found while deleting`);
|
||||||
return res.status(404).json({ error: 'Channel not found' });
|
return res.status(404).json({ error: 'Channel not found' });
|
||||||
}
|
}
|
||||||
|
logger.info(`Channel ${channelId} deleted`);
|
||||||
|
|
||||||
res.json({ message: 'Channel deleted successfully' });
|
res.json({ message: 'Channel deleted successfully' });
|
||||||
}
|
}
|
||||||
|
@ -37,6 +40,7 @@ export const mergeChannel = async (req: Request, res: Response) => {
|
||||||
return res.status(400).json({ error: 'Channel ID and targetChannelId are required' });
|
return res.status(400).json({ error: 'Channel ID and targetChannelId are required' });
|
||||||
}
|
}
|
||||||
const result = await ChannelService.mergeChannel(channelId, targetChannelId);
|
const result = await ChannelService.mergeChannel(channelId, targetChannelId);
|
||||||
|
logger.info(`Channel ${targetChannelId} merged into ${channelId}`);
|
||||||
|
|
||||||
res.json({ message: 'Channels merged successfully' });
|
res.json({ message: 'Channels merged successfully' });
|
||||||
}
|
}
|
||||||
|
@ -52,6 +56,7 @@ export const updateChannel = async (req: Request, res: Response) => {
|
||||||
if (result.changes === 0) {
|
if (result.changes === 0) {
|
||||||
return res.status(404).json({ error: 'Channel not found' });
|
return res.status(404).json({ error: 'Channel not found' });
|
||||||
}
|
}
|
||||||
|
logger.info(`Channel ${channelId} updated as ${name}`);
|
||||||
|
|
||||||
res.json({ message: 'Channel updated successfully' });
|
res.json({ message: 'Channel updated successfully' });
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import type { Request, Response } from "express";
|
import type { Request, Response } from "express";
|
||||||
import * as FileService from "../services/file-service";
|
import * as FileService from "../services/file-service";
|
||||||
|
import { logger } from "../globals";
|
||||||
|
|
||||||
export const uploadFile = async (req: Request, res: Response) => {
|
export const uploadFile = async (req: Request, res: Response) => {
|
||||||
const { channelId, messageId } = req.params;
|
const { channelId, messageId } = req.params;
|
||||||
|
@ -16,6 +17,7 @@ export const uploadFile = async (req: Request, res: Response) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await FileService.uploadFile(channelId, messageId, filePath, fileType!, fileSize!, originalName!);
|
const result = await FileService.uploadFile(channelId, messageId, filePath, fileType!, fileSize!, originalName!);
|
||||||
|
logger.info(`File ${originalName} uploaded to message ${messageId} as ${filePath}`);
|
||||||
res.json({ id: result.lastInsertRowid, channelId, messageId, filePath, fileType });
|
res.json({ id: result.lastInsertRowid, channelId, messageId, filePath, fileType });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import type { Request, Response } from "express";
|
import type { Request, Response } from "express";
|
||||||
import * as MessageService from "../services/message-service";
|
import * as MessageService from "../services/message-service";
|
||||||
|
import { logger } from "../globals";
|
||||||
|
|
||||||
export const createMessage = async (req: Request, res: Response) => {
|
export const createMessage = async (req: Request, res: Response) => {
|
||||||
const { content } = req.body;
|
const { content } = req.body;
|
||||||
|
@ -8,6 +9,7 @@ export const createMessage = async (req: Request, res: Response) => {
|
||||||
return res.status(400).json({ error: 'Content and channel ID are required' });
|
return res.status(400).json({ error: 'Content and channel ID are required' });
|
||||||
}
|
}
|
||||||
const messageId = await MessageService.createMessage(channelId, content);
|
const messageId = await MessageService.createMessage(channelId, content);
|
||||||
|
logger.info(`Message ${messageId} created in channel ${channelId}`);
|
||||||
|
|
||||||
res.json({ id: messageId, channelId, content, createdAt: new Date().toISOString() });
|
res.json({ id: messageId, channelId, content, createdAt: new Date().toISOString() });
|
||||||
};
|
};
|
||||||
|
@ -19,6 +21,10 @@ export const updateMessage = async (req: Request, res: Response) => {
|
||||||
return res.status(400).json({ error: 'Content and message ID are required ' });
|
return res.status(400).json({ error: 'Content and message ID are required ' });
|
||||||
}
|
}
|
||||||
const result = await MessageService.updateMessage(messageId, content);
|
const result = await MessageService.updateMessage(messageId, content);
|
||||||
|
if (result.changes === 0) {
|
||||||
|
return res.status(404).json({ error: 'Message not found' });
|
||||||
|
}
|
||||||
|
logger.info(`Message ${messageId} updated`);
|
||||||
|
|
||||||
res.json({ id: messageId, content });
|
res.json({ id: messageId, content });
|
||||||
}
|
}
|
||||||
|
@ -32,6 +38,7 @@ export const deleteMessage = async (req: Request, res: Response) => {
|
||||||
if (result.changes === 0) {
|
if (result.changes === 0) {
|
||||||
return res.status(404).json({ error: 'Message not found' });
|
return res.status(404).json({ error: 'Message not found' });
|
||||||
}
|
}
|
||||||
|
logger.info(`Message ${messageId} deleted`);
|
||||||
|
|
||||||
res.json({ message: 'Message deleted successfully' });
|
res.json({ message: 'Message deleted successfully' });
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import type { Request, Response } from "express";
|
import type { Request, Response } from "express";
|
||||||
import * as SearchService from "../services/search-service";
|
import * as SearchService from "../services/search-service";
|
||||||
|
import { logger } from "../globals";
|
||||||
|
|
||||||
export const search = async (req: Request, res: Response) => {
|
export const search = async (req: Request, res: Response) => {
|
||||||
const { query, channelId } = req.query;
|
const { query, channelId } = req.query;
|
||||||
|
@ -7,5 +8,6 @@ export const search = async (req: Request, res: Response) => {
|
||||||
return res.status(400).json({ error: 'Query is required' });
|
return res.status(400).json({ error: 'Query is required' });
|
||||||
}
|
}
|
||||||
const results = await SearchService.search(query as string, channelId as string);
|
const results = await SearchService.search(query as string, channelId as string);
|
||||||
|
logger.info(`Searched for ${query}`);
|
||||||
res.json({ results });
|
res.json({ results });
|
||||||
}
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
import type { Message } from "../../types";
|
||||||
import { events, logger } from "../globals"
|
import { events, logger } from "../globals"
|
||||||
import { describeImage } from "../services/image-description";
|
import { describeImage } from "../services/image-description";
|
||||||
import { getMessage, updateMessage } from "../services/message-service";
|
import { getMessage, updateMessage } from "../services/message-service";
|
||||||
|
|
|
@ -10,7 +10,7 @@ export const createChannel = async (name: string) => {
|
||||||
|
|
||||||
export const deleteChannel = async (id: string) => {
|
export const deleteChannel = async (id: string) => {
|
||||||
const query = db.query(`DELETE FROM channels WHERE id = ($channelId)`);
|
const query = db.query(`DELETE FROM channels WHERE id = ($channelId)`);
|
||||||
const result = db.run(id);
|
const result = query.run({$channelId: id});
|
||||||
// No need to manually delete messages and files as they are set to cascade on delete in the schema
|
// No need to manually delete messages and files as they are set to cascade on delete in the schema
|
||||||
events .emit('channel-deleted', id);
|
events .emit('channel-deleted', id);
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { events } from "../globals";
|
||||||
|
|
||||||
export const uploadFile = async (channelId: string, messageId: string, filePath: string, fileType: string, fileSize: number, originalName: string) => {
|
export const uploadFile = async (channelId: string, messageId: string, filePath: string, fileType: string, fileSize: number, originalName: string) => {
|
||||||
const query = db.query(`INSERT INTO files (channelId, filePath, fileType, fileSize, originalName) VALUES ($channelId, $filePath, $fileType, $fileSize, $originalName)`);
|
const query = db.query(`INSERT INTO files (channelId, filePath, fileType, fileSize, originalName) VALUES ($channelId, $filePath, $fileType, $fileSize, $originalName)`);
|
||||||
const result = query.run({ $channelId: channelId, $filePath: filePath, $fileType: fileType, $fileSize: fileSize, $originalName: originalName } as any);
|
const result = query.run({ $channelId: channelId, $filePath: filePath, $fileType: fileType, $fileSize: fileSize, $originalName: originalName });
|
||||||
|
|
||||||
const fileId = result.lastInsertRowid;
|
const fileId = result.lastInsertRowid;
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,7 @@ export const API = {
|
||||||
},
|
},
|
||||||
|
|
||||||
async mergeChannels(channelId: string, targetChannelId: string) {
|
async mergeChannels(channelId: string, targetChannelId: string) {
|
||||||
await API.request("POST", "merge-channels", { channelId, targetChannelId });
|
await API.request("PUT", `channels/${channelId}/merge`, { targetChannelId });
|
||||||
},
|
},
|
||||||
|
|
||||||
async search(query: string, channelId?: string) {
|
async search(query: string, channelId?: string) {
|
||||||
|
|
|
@ -3,8 +3,10 @@ import { showToast } from "../speech";
|
||||||
import { state } from "../state";
|
import { state } from "../state";
|
||||||
import { Button, TextInput } from "../ui";
|
import { Button, TextInput } from "../ui";
|
||||||
import { Dialog } from "../ui/dialog";
|
import { Dialog } from "../ui/dialog";
|
||||||
|
import { MergeDialog } from "./merge-dialog";
|
||||||
|
import { RemoveDialog } from "./remove-dialog";
|
||||||
|
|
||||||
export class ChannelDialog extends Dialog<IChannel> {
|
export class ChannelDialog extends Dialog<IChannel | null> {
|
||||||
private channel: IChannel;
|
private channel: IChannel;
|
||||||
private nameField: TextInput;
|
private nameField: TextInput;
|
||||||
private makeDefault: Button;
|
private makeDefault: Button;
|
||||||
|
@ -26,12 +28,15 @@ export class ChannelDialog extends Dialog<IChannel> {
|
||||||
this.mergeButton = new Button("Merge");
|
this.mergeButton = new Button("Merge");
|
||||||
this.mergeButton.setPosition(40, 70, 10, 10);
|
this.mergeButton.setPosition(40, 70, 10, 10);
|
||||||
this.mergeButton.onClick(() => {
|
this.mergeButton.onClick(() => {
|
||||||
showToast("Merge not implemented.");
|
this.mergeChannel();
|
||||||
});
|
});
|
||||||
|
if (state.channelList.channels.length === 1) {
|
||||||
|
this.mergeButton.setDisabled(true);
|
||||||
|
}
|
||||||
this.deleteButton = new Button("Delete");
|
this.deleteButton = new Button("Delete");
|
||||||
this.deleteButton.setPosition(60, 70, 10, 10);
|
this.deleteButton.setPosition(60, 70, 10, 10);
|
||||||
this.deleteButton.onClick(() => {
|
this.deleteButton.onClick(() => {
|
||||||
showToast("Delete not implemented.");
|
this.deleteChannel();
|
||||||
});
|
});
|
||||||
this.add(this.nameField);
|
this.add(this.nameField);
|
||||||
this.add(this.makeDefault);
|
this.add(this.makeDefault);
|
||||||
|
@ -42,4 +47,22 @@ export class ChannelDialog extends Dialog<IChannel> {
|
||||||
return this.channel;
|
return this.channel;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async mergeChannel() {
|
||||||
|
const res = await new MergeDialog().open();
|
||||||
|
if (res) {
|
||||||
|
this.choose(this.channel);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async deleteChannel() {
|
||||||
|
const res = await new RemoveDialog(this.channel.id.toString()).open();
|
||||||
|
if (res) {
|
||||||
|
this.choose(null);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -13,5 +13,10 @@ export class CreateChannelDialog extends Dialog<string> {
|
||||||
this.setOkAction(() => {
|
this.setOkAction(() => {
|
||||||
return this.nameField.getValue();
|
return this.nameField.getValue();
|
||||||
});
|
});
|
||||||
|
this.nameField.onKeyDown((key) => {
|
||||||
|
if (key === "Enter") {
|
||||||
|
this.choose(this.nameField.getValue());
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { Button } from "../ui";
|
||||||
|
import { Dialog } from "../ui/dialog";
|
||||||
|
import { API } from "../api";
|
||||||
|
import { Dropdown } from "../ui/dropdown";
|
||||||
|
import { state } from "../state";
|
||||||
|
import { showToast } from "../speech";
|
||||||
|
|
||||||
|
export class MergeDialog extends Dialog<boolean> {
|
||||||
|
private channelList: Dropdown;
|
||||||
|
private mergeButton: Button;
|
||||||
|
private cancelButton: Button;
|
||||||
|
|
||||||
|
public constructor() {
|
||||||
|
super("Merge channels", false);
|
||||||
|
this.channelList = new Dropdown("Target channel", []);
|
||||||
|
this.channelList.setPosition(10, 10, 80, 20);
|
||||||
|
this.mergeButton = new Button("Merge");
|
||||||
|
this.mergeButton.setPosition(30, 30, 40, 30);
|
||||||
|
this.mergeButton.onClick(() => this.merge());
|
||||||
|
this.cancelButton = new Button("Cancel");
|
||||||
|
this.cancelButton.setPosition(30, 70, 40, 30);
|
||||||
|
this.cancelButton.onClick(() => this.cancel());
|
||||||
|
this.add(this.channelList);
|
||||||
|
this.add(this.mergeButton);
|
||||||
|
this.add(this.cancelButton);
|
||||||
|
this.setupChannelList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private setupChannelList() {
|
||||||
|
this.channelList.clearOptions();
|
||||||
|
state.channelList.getChannels().forEach((channel) => {
|
||||||
|
if (channel.id !== state.currentChannel!.id) this.channelList.addOption(channel.id.toString(), channel.name);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
private async merge() {
|
||||||
|
const currentChannel = state.currentChannel;
|
||||||
|
const target = this.channelList.getSelectedValue();
|
||||||
|
const targetChannel = state.getChannelById(parseInt(target));
|
||||||
|
console.log(currentChannel, targetChannel);
|
||||||
|
if (!targetChannel || !currentChannel) this.cancel();
|
||||||
|
try {
|
||||||
|
const res = await API.mergeChannels(currentChannel!.id.toString(), target);
|
||||||
|
currentChannel!.messages = [];
|
||||||
|
showToast("Channels were merged.");
|
||||||
|
this.choose(true);
|
||||||
|
} catch (e) {
|
||||||
|
showToast("Failed to merge channels: " + e);
|
||||||
|
this.choose(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { Button } from "../ui";
|
||||||
|
import { Dialog } from "../ui/dialog";
|
||||||
|
import { Text } from "../ui";
|
||||||
|
import { API } from "../api";
|
||||||
|
import { state } from "../state";
|
||||||
|
import { showToast } from "../speech";
|
||||||
|
|
||||||
|
export class RemoveDialog extends Dialog<boolean> {
|
||||||
|
private content: Text;
|
||||||
|
private confirmButton: Button;
|
||||||
|
private cancelButton: Button;
|
||||||
|
|
||||||
|
public constructor(channelId: string) {
|
||||||
|
super("Remove channel", false);
|
||||||
|
this.content = new Text("Are you sure you want to remove this channel?");
|
||||||
|
this.confirmButton = new Button("Remove");
|
||||||
|
this.confirmButton.setPosition(30, 30, 40, 30);
|
||||||
|
this.confirmButton.onClick(() => this.remove());
|
||||||
|
this.cancelButton = new Button("Cancel");
|
||||||
|
this.cancelButton.setPosition(30, 70, 40, 30);
|
||||||
|
this.cancelButton.onClick(() => this.cancel());
|
||||||
|
this.add(this.content);
|
||||||
|
this.add(this.confirmButton);
|
||||||
|
this.add(this.cancelButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async remove() {
|
||||||
|
try {
|
||||||
|
const res = await API.deleteChannel(state.currentChannel!.id.toString());
|
||||||
|
state.removeChannel(state.currentChannel!);
|
||||||
|
showToast("Channel was removed.");
|
||||||
|
this.choose(true);
|
||||||
|
} catch (e) {
|
||||||
|
showToast("Failed to remove channel: " + e);
|
||||||
|
|
||||||
|
this.choose(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,11 @@ export class SearchDialog extends Dialog<{channelId: number, messageId: number}>
|
||||||
super("Search for message", false);
|
super("Search for message", false);
|
||||||
this.searchField = new TextInput("Search query");
|
this.searchField = new TextInput("Search query");
|
||||||
this.searchField.setPosition(5, 5, 80, 20);
|
this.searchField.setPosition(5, 5, 80, 20);
|
||||||
|
this.searchField.onKeyDown((key) => {
|
||||||
|
if (key === "Enter") {
|
||||||
|
this.searchButton.click();
|
||||||
|
}
|
||||||
|
});
|
||||||
this.searchButton = new Button("Search");
|
this.searchButton = new Button("Search");
|
||||||
this.searchButton.setPosition(85, 5, 10, 20);
|
this.searchButton.setPosition(85, 5, 10, 20);
|
||||||
this.searchButton.onClick(async () => {
|
this.searchButton.onClick(async () => {
|
||||||
|
@ -38,5 +43,6 @@ export class SearchDialog extends Dialog<{channelId: number, messageId: number}>
|
||||||
itm.onClick(() => this.choose({ messageId: message.id, channelId: message.channelId! }));
|
itm.onClick(() => this.choose({ messageId: message.id, channelId: message.channelId! }));
|
||||||
this.resultsList.add(itm);
|
this.resultsList.add(itm);
|
||||||
});
|
});
|
||||||
|
this.resultsList.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -140,6 +140,21 @@ export class MainView extends View {
|
||||||
}
|
}
|
||||||
const d = new ChannelDialog(state.currentChannel!);
|
const d = new ChannelDialog(state.currentChannel!);
|
||||||
d.open().then((chan) => {
|
d.open().then((chan) => {
|
||||||
|
if (!chan) {
|
||||||
|
state.removeChannel(state.currentChannel!);
|
||||||
|
state.currentChannel = null;
|
||||||
|
this.updateChannelList();
|
||||||
|
state.save();
|
||||||
|
if (state.channelList.channels.length > 0) {
|
||||||
|
return this.switchChannel(state.channelList.channels[0].id.toString());
|
||||||
|
} else {
|
||||||
|
return this.createNewChannel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chan.messages.length < 1) {
|
||||||
|
this.renderInitialMessageList(true);
|
||||||
|
this.syncMessages();
|
||||||
|
}
|
||||||
state.save();
|
state.save();
|
||||||
this.updateChannelList();
|
this.updateChannelList();
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue