diff --git a/api/migrations/55-discord-account-invitation-link.sql b/api/migrations/55-discord-account-invitation-link.sql index 29d28ab5..46f7e6ad 100644 --- a/api/migrations/55-discord-account-invitation-link.sql +++ b/api/migrations/55-discord-account-invitation-link.sql @@ -1,5 +1,5 @@ ALTER TABLE ctfnote_private.invitation_link - ADD COLUMN "discord_id" TEXT DEFAULT NULL; + ADD COLUMN "discord_id" TEXT UNIQUE DEFAULT NULL; DROP FUNCTION ctfnote.create_invitation_link ("role" ctfnote.role); CREATE OR REPLACE FUNCTION ctfnote.create_invitation_link ("role" ctfnote.role, "discord_id" text default null) diff --git a/api/src/discord/commands/register.ts b/api/src/discord/commands/register.ts index 7fbeb79a..ad905b8f 100644 --- a/api/src/discord/commands/register.ts +++ b/api/src/discord/commands/register.ts @@ -32,6 +32,24 @@ async function createAccountLogic( return; } + const existing_invitation_code = await getInvitationTokenForDiscordId( + interaction.user.id + ); + if (existing_invitation_code != null) { + const invitation_url = await getInvitationUrl(existing_invitation_code); + if (invitation_url == "") { + await interaction.editReply({ + content: "Something went wrong.", // TODO: Meaningful error messages? + }); + return; + } + + await interaction.editReply({ + content: `Your personal invitation url: ${invitation_url}. If you already have a CTFNote account you should link it using the /link command instead.`, + }); + return; + } + await interaction.editReply({ content: "Generating private invitation url... If you already have a CTFNote account you should link it using the /link command instead.", diff --git a/api/src/discord/database/users.ts b/api/src/discord/database/users.ts index 8b4a79ad..dcf384ea 100644 --- a/api/src/discord/database/users.ts +++ b/api/src/discord/database/users.ts @@ -52,8 +52,27 @@ type AllowedRoles = | "user_manager" | "user_admin"; -export async function createInvitationURLForDiscordId( - role: AllowedRoles, +export async function getInvitationTokenForDiscordId( + discordId: string, + pgClient: PoolClient | null = null +): Promise { + const useRequestClient = pgClient != null; + if (pgClient == null) pgClient = await connectToDatabase(); + + try { + const query = + "SELECT token FROM ctfnote_private.invitation_link WHERE discord_id = $1"; + const values = [discordId]; + const queryResult = await pgClient.query(query, values); + + return queryResult.rows[0].token as string; + } catch (error) { + return null; + } finally { + if (!useRequestClient) pgClient.release(); + } +} + discordId: string, pgClient: PoolClient | null = null ): Promise {