mirror of
https://github.com/spacebarchat/server.git
synced 2026-10-22 13:12:19 +00:00
more forum fixes
This commit is contained in:
1 parent
2b37cd2a5e
commit
f1f253e515
5 files changed
+228
-17
No files matched your search
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { handleMessage, postHandleMessage, route, sendMessage } from "@spacebar/api";
|
||||
import { Channel, emitEvent, User, uploadFile, Attachment, Member, ReadState, MessageCreateEvent, FieldErrors, getPermission, ThreadMember, Message } from "@spacebar/util";
|
||||
import { ChannelType, MessageType, ThreadCreationSchema, MessageCreateAttachment, MessageCreateCloudAttachment, PostDataSchema } from "@spacebar/schemas";
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { messageUpload } from "./messages";
|
||||
import { HTTPError } from "#util/util/lambert-server";
|
||||
import { FindManyOptions, FindOptionsOrder, In, Like } from "typeorm";
|
||||
|
||||
const router = Router({ mergeParams: true });
|
||||
|
||||
// TODO: public read receipts & privacy scoping
|
||||
// TODO: send read state event to all channel members
|
||||
// TODO: advance-only notification cursor
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
messageUpload.any(),
|
||||
(req, res, next) => {
|
||||
if (req.body.payload_json) {
|
||||
req.body = JSON.parse(req.body.payload_json);
|
||||
}
|
||||
|
||||
next();
|
||||
},
|
||||
route({
|
||||
requestBody: "PostDataSchema",
|
||||
permission: "VIEW_CHANNEL",
|
||||
responses: {
|
||||
200: {},
|
||||
403: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const body = (req.body as PostDataSchema).thread_ids;
|
||||
const threads = await Channel.find({
|
||||
where: {
|
||||
id: In(body),
|
||||
},
|
||||
});
|
||||
const [messages, members] = await Promise.all([
|
||||
Message.find({
|
||||
where: {
|
||||
id: In(threads.map(({ id }) => id)),
|
||||
},
|
||||
}),
|
||||
Member.find({
|
||||
where: {
|
||||
id: In(threads.map(({ owner_id }) => owner_id)),
|
||||
},
|
||||
}),
|
||||
]);
|
||||
const objRet: { threads: Record<string, { first_message: null | Message; owner: null | Member }> } = { threads: {} };
|
||||
for (const thread of threads) {
|
||||
const owner = members.find(({ id }) => id === thread.owner_id)?.toJSON() || null;
|
||||
const first_message = messages.find(({ channel_id }) => channel_id === thread.id)?.toJSON() || null;
|
||||
objRet.threads[thread.id] = {
|
||||
owner,
|
||||
first_message,
|
||||
};
|
||||
}
|
||||
return res.json(objRet);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
@@ -17,11 +17,13 @@
|
||||
*/
|
||||
|
||||
import { handleMessage, postHandleMessage, route, sendMessage } from "@spacebar/api";
|
||||
import { Channel, emitEvent, User, uploadFile, Attachment, Member, ReadState, MessageCreateEvent } from "@spacebar/util";
|
||||
import { Channel, emitEvent, User, uploadFile, Attachment, Member, ReadState, MessageCreateEvent, FieldErrors, getPermission, ThreadMember, Message } from "@spacebar/util";
|
||||
import { ChannelType, MessageType, ThreadCreationSchema, MessageCreateAttachment, MessageCreateCloudAttachment } from "@spacebar/schemas";
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { messageUpload } from "./messages";
|
||||
import { HTTPError } from "#util/util/lambert-server";
|
||||
import { FindManyOptions, FindOptionsOrder, In, Like } from "typeorm";
|
||||
|
||||
const router = Router({ mergeParams: true });
|
||||
|
||||
@@ -168,4 +170,101 @@ router.post(
|
||||
},
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/search",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "GuildMessagesSearchResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
422: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { name, slop, tag, tag_setting, archived, sort_by, sort_order, limit, offset, max_id, min_id } = req.query as Record<string, string>;
|
||||
const { channel_id } = req.params as Record<string, string>;
|
||||
|
||||
const parsedLimit = Number(limit) || 25;
|
||||
if (parsedLimit < 1 || parsedLimit > 25) throw new HTTPError("limit must be between 1 and 25", 422);
|
||||
|
||||
let order: FindOptionsOrder<Channel>;
|
||||
switch (sort_by) {
|
||||
case undefined:
|
||||
case "creation_time":
|
||||
order = {
|
||||
created_at: sort_order === "asc" ? "ASC" : "DESC",
|
||||
};
|
||||
break;
|
||||
case "last_message_time":
|
||||
order = {
|
||||
last_message_id: sort_order === "asc" ? "ASC" : "DESC",
|
||||
};
|
||||
break;
|
||||
default:
|
||||
throw FieldErrors({
|
||||
sort_by: {
|
||||
message: "Value must be one of ('last_message_time', 'archive_time', 'relevance', 'creation_time').",
|
||||
code: "BASE_TYPE_CHOICES",
|
||||
},
|
||||
}); // todo this is wrong
|
||||
}
|
||||
const channel = await Channel.findOneOrFail({
|
||||
where: {
|
||||
id: channel_id,
|
||||
},
|
||||
});
|
||||
|
||||
const permissions = await getPermission(req.user_id, channel.guild_id, channel);
|
||||
permissions.hasThrow("VIEW_CHANNEL");
|
||||
if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json({ threads: [], total_results: 0, members: [], has_more: false, first_messages: [] });
|
||||
const member = await Member.findOneOrFail({ where: { guild_id: channel.guild_id, id: req.user_id } });
|
||||
|
||||
const query: FindManyOptions<Channel> = {
|
||||
order,
|
||||
where: {
|
||||
parent_id: channel_id,
|
||||
...(name ? { name: Like(`%${name}%`) } : {}),
|
||||
...(archived
|
||||
? {
|
||||
thread_metadata: {
|
||||
archived: archived === "true" ? true : false,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
relations: {},
|
||||
};
|
||||
|
||||
const threads: Channel[] = await Channel.find({ ...query, take: parsedLimit || 0, skip: offset ? Number(offset) : 0 });
|
||||
const total_results = await Channel.count(query);
|
||||
|
||||
const members = ThreadMember.find({
|
||||
where: {
|
||||
member_idx: member.index,
|
||||
id: In(threads.map(({ id }) => id)),
|
||||
},
|
||||
});
|
||||
|
||||
const messages = Message.find({
|
||||
where: {
|
||||
id: In(threads.map(({ id }) => id)),
|
||||
},
|
||||
});
|
||||
|
||||
const left = total_results - threads.length - +offset;
|
||||
return res.json({
|
||||
threads: threads.map((_) => _.toJSON()),
|
||||
members: (await members).map((_) => _.toJSON()),
|
||||
messages: (await messages).map((_) => _.toJSON()),
|
||||
total_results,
|
||||
has_more: left > 0,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
Reference in new issue
Block a user