diff --git a/api/src/routes/guilds/index.ts b/api/src/routes/guilds/index.ts
index 48aab092..7b676211 100644
--- a/api/src/routes/guilds/index.ts
+++ b/api/src/routes/guilds/index.ts
@@ -29,80 +29,17 @@ router.post("/", route({ body: "GuildCreateSchema" }), async (req: Request, res:
throw DiscordApiErrors.MAXIMUM_GUILDS.withParams(maxGuilds);
}
- const guild_id = Snowflake.generate();
+ const guild = await Guild.createGuild({ ...body, owner_id: req.user_id });
- await new Guild({
- name: body.name,
- icon: await handleFile(`/icons/${guild_id}`, body.icon as string),
- region: Config.get().regions.default,
- owner_id: req.user_id,
- afk_timeout: 300,
- default_message_notifications: 0,
- explicit_content_filter: 0,
- features: [],
- id: guild_id,
- max_members: 250000,
- max_presences: 250000,
- max_video_channel_users: 25,
- presence_count: 0,
- member_count: 0, // will automatically be increased by addMember()
- mfa_level: 0,
- preferred_locale: "en-US",
- premium_subscription_count: 0,
- premium_tier: 0,
- system_channel_flags: 0,
- unavailable: false,
- nsfw: false,
- nsfw_level: 0,
- verification_level: 0,
- welcome_screen: {
- enabled: false,
- description: "No description",
- welcome_channels: []
- },
- widget_enabled: false
- }).save();
-
- // we have to create the role _after_ the guild because else we would get a "SQLITE_CONSTRAINT: FOREIGN KEY constraint failed" error
- await new Role({
- id: guild_id,
- guild_id: guild_id,
- color: 0,
- hoist: false,
- managed: false,
- mentionable: false,
- name: "@everyone",
- permissions: String("2251804225"),
- position: 0
- }).save();
-
- if (!body.channels || !body.channels.length) body.channels = [{ id: "01", type: 0, name: "general" }];
-
- const ids = new Map();
-
- body.channels.forEach((x) => {
- if (x.id) {
- ids.set(x.id, Snowflake.generate());
- }
- });
-
- for (const channel of body.channels?.sort((a, b) => (a.parent_id ? 1 : -1))) {
- var id = ids.get(channel.id) || Snowflake.generate();
-
- // TODO: should we abort if parent_id is a category? (to disallow sub category channels)
- var parent_id = ids.get(channel.parent_id);
-
- await Channel.createChannel({ ...channel, guild_id, id, parent_id }, req.user_id, {
- keepId: true,
- skipExistsCheck: true,
- skipPermissionCheck: true,
- skipEventEmit: true
- });
+ const { autoJoin } = Config.get().guild;
+ if (autoJoin.enabled && !autoJoin.guilds?.length) {
+ // @ts-ignore
+ await Config.set({ guild: { autoJoin: { guilds: [guild.id] } } });
}
- await Member.addToGuild(req.user_id, guild_id);
+ await Member.addToGuild(req.user_id, guild.id);
- res.status(201).json({ id: guild_id });
+ res.status(201).json({ id: guild.id });
});
export default router;
diff --git a/api/src/routes/users/@me/guilds.ts b/api/src/routes/users/@me/guilds.ts
index 4ba03cec..22a2c04c 100644
--- a/api/src/routes/users/@me/guilds.ts
+++ b/api/src/routes/users/@me/guilds.ts
@@ -1,5 +1,5 @@
import { Router, Request, Response } from "express";
-import { Guild, Member, User, GuildDeleteEvent, GuildMemberRemoveEvent, emitEvent } from "@fosscord/util";
+import { Guild, Member, User, GuildDeleteEvent, GuildMemberRemoveEvent, emitEvent, Config } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { route } from "@fosscord/api";
@@ -12,12 +12,16 @@ router.get("/", route({}), async (req: Request, res: Response) => {
});
// user send to leave a certain guild
-router.delete("/:id", route({}), async (req: Request, res: Response) => {
- const guild_id = req.params.id;
+router.delete("/:guild_id", route({}), async (req: Request, res: Response) => {
+ const { autoJoin } = Config.get().guild;
+ const { guild_id } = req.params;
const guild = await Guild.findOneOrFail({ where: { id: guild_id }, select: ["owner_id"] });
if (!guild) throw new HTTPError("Guild doesn't exist", 404);
if (guild.owner_id === req.user_id) throw new HTTPError("You can't leave your own guild", 400);
+ if (autoJoin.enabled && autoJoin.guilds.includes(guild_id) && !autoJoin.canLeave) {
+ throw new HTTPError("You can't leave instance auto join guilds", 400);
+ }
await Promise.all([
Member.delete({ id: req.user_id, guild_id: guild_id }),
diff --git a/api/src/util/Instance.ts b/api/src/util/Instance.ts
new file mode 100644
index 00000000..a7b3205a
--- /dev/null
+++ b/api/src/util/Instance.ts
@@ -0,0 +1,18 @@
+import { Config, Guild } from "@fosscord/util";
+
+export async function initInstance() {
+ // TODO: clean up database and delete tombstone data
+ // TODO: set first user as instance administrator/or generate one if none exists and output it in the terminal
+
+ // create default guild and add it to auto join
+ // TODO: check if any current user is not part of autoJoinGuilds
+ const { autoJoin } = Config.get().guild;
+
+ if (autoJoin.enabled && autoJoin.guilds?.length) {
+ let guild = await Guild.findOne({});
+ if (!guild) guild = await Guild.createGuild({});
+
+ // @ts-ignore
+ await Config.set({ guild: { autoJoin: { guilds: [guild.id] } } });
+ }
+}
diff --git a/util/src/entities/Config.ts b/util/src/entities/Config.ts
index 28926233..921a12c2 100644
--- a/util/src/entities/Config.ts
+++ b/util/src/entities/Config.ts
@@ -144,9 +144,13 @@ export interface ConfigValue {
useDefaultAsOptimal: boolean;
available: Region[];
};
-
guild: {
showAllGuildsInDiscovery: boolean;
+ autoJoin: {
+ enabled: boolean;
+ guilds: string[];
+ canLeave: boolean;
+ };
};
rabbitmq: {
host: string | null;
@@ -302,6 +306,11 @@ export const DefaultConfigOptions: ConfigValue = {
guild: {
showAllGuildsInDiscovery: false,
+ autoJoin: {
+ enabled: true,
+ canLeave: true,
+ guilds: [],
+ },
},
rabbitmq: {
host: null,
diff --git a/util/src/entities/Guild.ts b/util/src/entities/Guild.ts
index e107937d..35595191 100644
--- a/util/src/entities/Guild.ts
+++ b/util/src/entities/Guild.ts
@@ -1,4 +1,5 @@
import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, OneToMany, OneToOne, RelationId } from "typeorm";
+import { Config, handleFile, Snowflake } from "..";
import { Ban } from "./Ban";
import { BaseClass } from "./BaseClass";
import { Channel } from "./Channel";
@@ -295,4 +296,84 @@ export class Guild extends BaseClass {
@Column({ nullable: true })
nsfw?: boolean;
+
+ static async createGuild(body: {
+ name?: string;
+ icon?: string | null;
+ owner_id?: string;
+ channels?: Partial<Channel>[];
+ }) {
+ const guild_id = Snowflake.generate();
+
+ const guild = await new Guild({
+ name: body.name || "Fosscord",
+ icon: await handleFile(`/icons/${guild_id}`, body.icon as string),
+ region: Config.get().regions.default,
+ owner_id: body.owner_id,
+ afk_timeout: 300,
+ default_message_notifications: 0,
+ explicit_content_filter: 0,
+ features: [],
+ id: guild_id,
+ max_members: 250000,
+ max_presences: 250000,
+ max_video_channel_users: 25,
+ presence_count: 0,
+ member_count: 0, // will automatically be increased by addMember()
+ mfa_level: 0,
+ preferred_locale: "en-US",
+ premium_subscription_count: 0,
+ premium_tier: 0,
+ system_channel_flags: 0,
+ unavailable: false,
+ nsfw: false,
+ nsfw_level: 0,
+ verification_level: 0,
+ welcome_screen: {
+ enabled: false,
+ description: "No description",
+ welcome_channels: [],
+ },
+ widget_enabled: false,
+ }).save();
+
+ // we have to create the role _after_ the guild because else we would get a "SQLITE_CONSTRAINT: FOREIGN KEY constraint failed" error
+ await new Role({
+ id: guild_id,
+ guild_id: guild_id,
+ color: 0,
+ hoist: false,
+ managed: false,
+ mentionable: false,
+ name: "@everyone",
+ permissions: String("2251804225"),
+ position: 0,
+ }).save();
+
+ if (!body.channels || !body.channels.length) body.channels = [{ id: "01", type: 0, name: "general" }];
+
+ const ids = new Map();
+
+ body.channels.forEach((x) => {
+ if (x.id) {
+ ids.set(x.id, Snowflake.generate());
+ }
+ });
+
+ for (const channel of body.channels?.sort((a, b) => (a.parent_id ? 1 : -1))) {
+ var id = ids.get(channel.id) || Snowflake.generate();
+
+ // TODO: should we abort if parent_id is a category? (to disallow sub category channels)
+ var parent_id = ids.get(channel.parent_id);
+
+ await Channel.createChannel({ ...channel, guild_id, id, parent_id }, body.owner_id, {
+ keepId: true,
+ skipExistsCheck: true,
+ skipPermissionCheck: true,
+ skipEventEmit: true,
+ });
+ }
+
+ return guild;
+ }
}
|