mirror of
https://github.com/spacebarchat/server.git
synced 2026-08-27 13:19:55 +00:00
Merge remote-tracking branch 'origin/dev/threads'
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -35,7 +35,7 @@ import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import multer from "multer";
|
||||
import { handleMessage, postHandleMessage, route } from "../../../../../util";
|
||||
import { MessageCreateAttachment, MessageCreateCloudAttachment, MessageCreateSchema, MessageEditSchema } from "@spacebar/schemas";
|
||||
import { MessageCreateAttachment, MessageCreateCloudAttachment, MessageCreateSchema, MessageEditSchema, ChannelType } from "@spacebar/schemas";
|
||||
|
||||
const router = Router({ mergeParams: true });
|
||||
// TODO: message content/embed string length limit
|
||||
@@ -293,6 +293,10 @@ router.delete(
|
||||
const channel = await Channel.findOneOrFail({
|
||||
where: { id: channel_id },
|
||||
});
|
||||
if (channel.type === ChannelType.GUILD_PUBLIC_THREAD) {
|
||||
if (channel.message_count !== undefined) channel.message_count--;
|
||||
channel.save(); //Save async, it's fine
|
||||
}
|
||||
const message = await Message.findOneOrFail({
|
||||
where: { id: message_id },
|
||||
});
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
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 { route, sendMessage } from "@spacebar/api";
|
||||
import { Message, Channel, emitEvent, User, MessageUpdateEvent, Recipient } from "@spacebar/util";
|
||||
import { MessageThreadCreationSchema, ChannelType, MessageType } from "@spacebar/schemas";
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
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(
|
||||
"/",
|
||||
route({
|
||||
requestBody: "MessageThreadCreationSchema",
|
||||
responses: {
|
||||
200: {},
|
||||
403: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { message_id, channel_id } = req.params;
|
||||
const body = req.body as MessageThreadCreationSchema;
|
||||
const message = await Message.findOneOrFail({
|
||||
where: { id: message_id, channel_id },
|
||||
relations: ["guild"],
|
||||
});
|
||||
const channel = await Channel.findOneOrFail({
|
||||
where: { id: channel_id },
|
||||
});
|
||||
const user = await User.findOneOrFail({ where: { id: req.user_id } });
|
||||
const recipient = Recipient.create({ channel_id: message.id, user });
|
||||
|
||||
const thread = await Channel.createChannel(
|
||||
{
|
||||
id: message.id,
|
||||
owner: user,
|
||||
parent: channel,
|
||||
guild: channel.guild,
|
||||
member_count: 1,
|
||||
message_count: 1,
|
||||
total_message_sent: 1,
|
||||
name: body.name,
|
||||
guild_id: channel.guild_id,
|
||||
rate_limit_per_user: body.rate_limit_per_user,
|
||||
type: ChannelType.GUILD_PUBLIC_THREAD,
|
||||
recipients: [],
|
||||
thread_metadata: {
|
||||
archived: false,
|
||||
auto_archive_duration: body.auto_archive_duration || channel.default_auto_archive_duration || 4320,
|
||||
archive_timestamp: new Date().toISOString(),
|
||||
locked: false,
|
||||
create_timestamp: new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
void 0,
|
||||
{ skipPermissionCheck: true, keepId: true, skipEventEmit: true },
|
||||
);
|
||||
|
||||
recipient.save();
|
||||
|
||||
message.thread = thread;
|
||||
message.flags ||= 1 << 5;
|
||||
await sendMessage({
|
||||
channel_id: thread.id,
|
||||
type: MessageType.THREAD_STARTER_MESSAGE,
|
||||
message_reference: {
|
||||
message_id: message.id,
|
||||
channel_id: channel.id,
|
||||
guild_id: channel.guild_id,
|
||||
},
|
||||
author_id: user.id,
|
||||
});
|
||||
await Promise.all([
|
||||
emitEvent({
|
||||
event: "THREAD_CREATE",
|
||||
channel_id,
|
||||
data: {
|
||||
...thread.toJSON(),
|
||||
newly_created: true,
|
||||
},
|
||||
}),
|
||||
message.save(),
|
||||
emitEvent({
|
||||
event: "MESSAGE_UPDATE",
|
||||
channel_id: message.channel_id,
|
||||
data: message.toJSON(),
|
||||
} as MessageUpdateEvent),
|
||||
]);
|
||||
|
||||
return res.json(thread.toJSON());
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
@@ -41,6 +41,7 @@ import {
|
||||
stringGlobToRegexp,
|
||||
uploadFile,
|
||||
User,
|
||||
Recipient,
|
||||
} from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
@@ -59,6 +60,7 @@ import {
|
||||
Reaction,
|
||||
ReadStateType,
|
||||
RelationshipType,
|
||||
ChannelType,
|
||||
} from "@spacebar/schemas";
|
||||
|
||||
const router: Router = Router({ mergeParams: true });
|
||||
@@ -137,6 +139,11 @@ router.get(
|
||||
sticker_items: true,
|
||||
attachments: true,
|
||||
},
|
||||
thread: {
|
||||
recipients: {
|
||||
user: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -246,6 +253,7 @@ router.get(
|
||||
|
||||
return x;
|
||||
});
|
||||
//console.log(ret);
|
||||
|
||||
await Promise.all(
|
||||
ret
|
||||
@@ -409,8 +417,6 @@ router.post(
|
||||
//@ts-ignore dont care2
|
||||
message.edited_timestamp = null;
|
||||
|
||||
channel.last_message_id = message.id;
|
||||
|
||||
if (channel.isDm()) {
|
||||
const channel_dto = await DmChannelDTO.from(channel);
|
||||
|
||||
@@ -464,11 +470,20 @@ router.post(
|
||||
data: message,
|
||||
} as MessageCreateEvent),
|
||||
message.guild_id ? Member.update({ id: req.user_id, guild_id: message.guild_id }, { last_message_id: message.id }) : null,
|
||||
channel.save(),
|
||||
]);
|
||||
|
||||
// no await as it shouldnt block the message send function and silently catch error
|
||||
postHandleMessage(message).catch((e) => console.error("[Message] post-message handler failed", e));
|
||||
if (channel.type === ChannelType.GUILD_PUBLIC_THREAD && channel.recipients && !channel.recipients.find((_) => _.user.id === req.user_id)) {
|
||||
const rec = Recipient.create({
|
||||
channel_id: channel.id,
|
||||
user_id: req.user_id,
|
||||
});
|
||||
channel.recipients.push(rec);
|
||||
rec.save().then(() => {
|
||||
channel.save();
|
||||
});
|
||||
}
|
||||
|
||||
return res.json(
|
||||
message.withSignedAttachments(
|
||||
|
||||
@@ -106,6 +106,15 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
||||
components: opts.components ?? undefined, // Fix Discord-Go?
|
||||
});
|
||||
const ephermal = (message.flags & (1 << 6)) !== 0;
|
||||
if (!ephermal && channel.type === ChannelType.GUILD_PUBLIC_THREAD) {
|
||||
const rep = Channel.getRepository();
|
||||
console.log(channel.id);
|
||||
await rep.increment({ id: channel.id }, "message_count", 1);
|
||||
await rep.increment({ id: channel.id }, "total_message_sent", 1);
|
||||
}
|
||||
if (!ephermal) {
|
||||
channel.last_message_id = message.id;
|
||||
}
|
||||
|
||||
if (cloudAttachments && cloudAttachments.length > 0) {
|
||||
console.log("[Message] Processing attachments for message", message.id, ":", message.attachments);
|
||||
@@ -234,7 +243,8 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
||||
|
||||
if (opts.message_reference.type != 1) {
|
||||
if (opts.message_reference.guild_id !== channel.guild_id) throw new HTTPError("You can only reference messages from this guild");
|
||||
if (opts.message_reference.channel_id !== opts.channel_id) throw new HTTPError("You can only reference messages from this channel");
|
||||
if (opts.message_reference.channel_id !== opts.channel_id && opts.type !== MessageType.THREAD_STARTER_MESSAGE)
|
||||
throw new HTTPError("You can only reference messages from this channel");
|
||||
}
|
||||
|
||||
message.message_reference = opts.message_reference;
|
||||
@@ -254,14 +264,22 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
||||
},
|
||||
});
|
||||
|
||||
if (message.referenced_message.channel_id && message.referenced_message.channel_id !== opts.message_reference.channel_id)
|
||||
if (
|
||||
message.referenced_message.channel_id &&
|
||||
message.referenced_message.channel_id !== opts.message_reference.channel_id &&
|
||||
opts.type !== MessageType.THREAD_STARTER_MESSAGE
|
||||
)
|
||||
throw new HTTPError("Referenced message not found in the specified channel", 404);
|
||||
if (message.referenced_message.guild_id && message.referenced_message.guild_id !== opts.message_reference.guild_id)
|
||||
if (
|
||||
message.referenced_message.guild_id &&
|
||||
message.referenced_message.guild_id !== opts.message_reference.guild_id &&
|
||||
opts.type !== MessageType.THREAD_STARTER_MESSAGE
|
||||
)
|
||||
throw new HTTPError("Referenced message not found in the specified channel", 404);
|
||||
}
|
||||
/** Q: should be checked if the referenced message exists? ANSWER: NO
|
||||
otherwise backfilling won't work **/
|
||||
message.type = MessageType.REPLY;
|
||||
if (MessageType.THREAD_STARTER_MESSAGE !== message.type) message.type = MessageType.REPLY;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,7 +292,8 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
||||
!opts.sticker_ids?.length &&
|
||||
!opts.poll &&
|
||||
!opts.components?.length &&
|
||||
opts.message_reference?.type != 1
|
||||
opts.message_reference?.type != 1 &&
|
||||
opts.type !== MessageType.THREAD_STARTER_MESSAGE
|
||||
) {
|
||||
console.log("[Message] Rejecting empty message:", opts, message);
|
||||
throw new HTTPError("Empty messages are not allowed", 50006);
|
||||
|
||||
@@ -296,7 +296,10 @@ export async function onIdentify(this: WebSocket, data: Payload) {
|
||||
] = await Promise.all([
|
||||
timePromise(() =>
|
||||
Channel.find({
|
||||
where: { guild_id: In(guildIds) },
|
||||
where: {
|
||||
guild_id: In(guildIds),
|
||||
type: Not(ChannelType.GUILD_PUBLIC_THREAD),
|
||||
},
|
||||
order: { guild_id: "ASC" },
|
||||
}),
|
||||
),
|
||||
|
||||
@@ -36,6 +36,14 @@ export enum ChannelPermissionOverwriteType {
|
||||
member = 1,
|
||||
group = 2,
|
||||
}
|
||||
export interface threadMetadata {
|
||||
archived: boolean;
|
||||
auto_archive_duration: number;
|
||||
archive_timestamp: string;
|
||||
locked: boolean;
|
||||
invitable?: boolean;
|
||||
create_timestamp: string; //Discord docs say this is optional, but it's only for after a certain date so it's not
|
||||
}
|
||||
|
||||
export interface DMChannel extends Omit<Channel, "type" | "recipients"> {
|
||||
type: ChannelType.DM | ChannelType.GROUP_DM;
|
||||
|
||||
@@ -39,6 +39,7 @@ export enum MessageType {
|
||||
ENCRYPTED = 16,
|
||||
REPLY = 19,
|
||||
APPLICATION_COMMAND = 20, // application command or self command invocation
|
||||
THREAD_STARTER_MESSAGE = 21,
|
||||
ROUTE_ADDED = 41, // custom message routing: new route affecting that channel
|
||||
ROUTE_DISABLED = 42, // custom message routing: given route no longer affecting that channel
|
||||
SELF_COMMAND_SCRIPT = 43, // self command scripts
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
export interface MessageThreadCreationSchema {
|
||||
auto_archive_duration?: number;
|
||||
rate_limit_per_user?: number;
|
||||
name: string;
|
||||
type?: number;
|
||||
location?: string; //0 clue what this means lol
|
||||
}
|
||||
@@ -91,3 +91,4 @@ export * from "./WebhookCreateSchema";
|
||||
export * from "./WebhookExecuteSchema";
|
||||
export * from "./WebhookUpdateSchema";
|
||||
export * from "./WidgetModifySchema";
|
||||
export * from "./MessageThreadCreationSchema";
|
||||
|
||||
@@ -31,7 +31,7 @@ import { User } from "./User";
|
||||
import { VoiceState } from "./VoiceState";
|
||||
import { Webhook } from "./Webhook";
|
||||
import { Member } from "./Member";
|
||||
import { ChannelPermissionOverwrite, ChannelPermissionOverwriteType, ChannelType, PublicUserProjection } from "@spacebar/schemas";
|
||||
import { ChannelPermissionOverwrite, ChannelPermissionOverwriteType, ChannelType, PublicUserProjection, threadMetadata } from "@spacebar/schemas";
|
||||
|
||||
@Entity({
|
||||
name: "channels",
|
||||
@@ -152,6 +152,18 @@ export class Channel extends BaseClass {
|
||||
@Column({ nullable: true })
|
||||
default_thread_rate_limit_per_user?: number = 0;
|
||||
|
||||
@Column({ type: "simple-json", nullable: true })
|
||||
thread_metadata?: threadMetadata;
|
||||
|
||||
@Column({ nullable: true })
|
||||
member_count?: number;
|
||||
|
||||
@Column({ nullable: true })
|
||||
message_count?: number;
|
||||
|
||||
@Column({ nullable: true })
|
||||
total_message_sent?: number;
|
||||
|
||||
/** Must be calculated Channel.calculatePosition */
|
||||
position: number;
|
||||
|
||||
@@ -203,6 +215,7 @@ export class Channel extends BaseClass {
|
||||
}
|
||||
|
||||
switch (channel.type) {
|
||||
case ChannelType.GUILD_PUBLIC_THREAD:
|
||||
case ChannelType.GUILD_TEXT:
|
||||
case ChannelType.GUILD_NEWS:
|
||||
case ChannelType.GUILD_VOICE:
|
||||
@@ -532,6 +545,7 @@ export class Channel extends BaseClass {
|
||||
user_limit: this.user_limit || undefined,
|
||||
rate_limit_per_user: this.rate_limit_per_user || undefined,
|
||||
owner_id: this.owner_id || undefined,
|
||||
...(this.type === ChannelType.GUILD_PUBLIC_THREAD && this.recipients ? { member_ids_preview: this.recipients.map((_) => _.user.id) } : {}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,16 @@ export class Message extends BaseClass {
|
||||
})
|
||||
channel: Channel;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((message: Message) => message.thread)
|
||||
thread_id?: string;
|
||||
|
||||
@JoinColumn({ name: "thread_id" })
|
||||
@ManyToOne(() => Channel, {
|
||||
onDelete: "CASCADE",
|
||||
})
|
||||
thread?: Channel;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((message: Message) => message.guild)
|
||||
guild_id?: string;
|
||||
@@ -234,6 +244,7 @@ export class Message extends BaseClass {
|
||||
poll: this.poll ?? undefined,
|
||||
content: this.content ?? "",
|
||||
pinned: this.pinned,
|
||||
thread: this.thread ? this.thread.toJSON() : this.thread,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -775,6 +775,7 @@ export type EVENT =
|
||||
| "RELATIONSHIP_UPDATE"
|
||||
| "SESSIONS_REPLACE"
|
||||
| "USER_SETTINGS_PROTO_UPDATE"
|
||||
| "THREAD_CREATE"
|
||||
| CUSTOMEVENTS;
|
||||
|
||||
export type CUSTOMEVENTS = "INVALIDATED" | "RATELIMIT";
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class Threads1764612754204 implements MigrationInterface {
|
||||
name = "Threads1764612754204";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "messages" ADD "thread_id" character varying`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" ADD "thread_metadata" text`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" ADD "member_count" integer`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" ADD "message_count" integer`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" ADD "total_message_sent" integer`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "channels" DROP COLUMN "total_message_sent"`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" DROP COLUMN "message_count"`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" DROP COLUMN "member_count"`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" DROP COLUMN "thread_metadata"`);
|
||||
await queryRunner.query(`ALTER TABLE "messages" DROP COLUMN "thread_id"`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class ThreadGoof1764622231800 implements MigrationInterface {
|
||||
name = "ThreadGoof1764622231800";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "messages" ADD CONSTRAINT "FK_bb3af7f695d50083e6523290d41" FOREIGN KEY ("thread_id") REFERENCES "channels"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "messages" DROP CONSTRAINT "FK_bb3af7f695d50083e6523290d41"`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class Threads1764609924756 implements MigrationInterface {
|
||||
name = "Threads1764609924756";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "messages" DROP CONSTRAINT "FK_bb3af7f695d50083e6523290d41"`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" DROP COLUMN "total_message_sent"`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" ADD "total_messsage_sent" integer`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" ALTER COLUMN "thread_metadata" DROP NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" ALTER COLUMN "member_count" DROP NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" ALTER COLUMN "message_count" DROP NOT NULL`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "channels" ALTER COLUMN "message_count" SET NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" ALTER COLUMN "member_count" SET NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" ALTER COLUMN "thread_metadata" SET NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" DROP COLUMN "total_messsage_sent"`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" ADD "total_message_sent" integer NOT NULL`);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "messages" ADD CONSTRAINT "FK_bb3af7f695d50083e6523290d41" FOREIGN KEY ("thread_id") REFERENCES "channels"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class Threadtypo1764610059742 implements MigrationInterface {
|
||||
name = "Threadtypo1764610059742";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "messages" DROP CONSTRAINT "FK_bb3af7f695d50083e6523290d41"`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" DROP COLUMN "total_message_sent"`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" ADD "total_messsage_sent" integer`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" ALTER COLUMN "thread_metadata" DROP NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" ALTER COLUMN "member_count" DROP NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" ALTER COLUMN "message_count" DROP NOT NULL`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "channels" ALTER COLUMN "message_count" SET NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" ALTER COLUMN "member_count" SET NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" ALTER COLUMN "thread_metadata" SET NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" DROP COLUMN "total_messsage_sent"`);
|
||||
await queryRunner.query(`ALTER TABLE "channels" ADD "total_message_sent" integer NOT NULL`);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "messages" ADD CONSTRAINT "FK_bb3af7f695d50083e6523290d41" FOREIGN KEY ("thread_id") REFERENCES "channels"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user