diff --git a/api/migrations/54-add-discord-integration-enabled.sql b/api/migrations/54-add-discord-integration-enabled.sql new file mode 100644 index 000000000..e94c5db8d --- /dev/null +++ b/api/migrations/54-add-discord-integration-enabled.sql @@ -0,0 +1,8 @@ +ALTER TABLE ctfnote.settings + ADD COLUMN "discord_integration_enabled" boolean NOT NULL DEFAULT FALSE; + +GRANT SELECT ("discord_integration_enabled") ON ctfnote.settings TO user_anonymous; +REVOKE UPDATE ON ctfnote.settings FROM user_admin; +GRANT UPDATE (unique_id, registration_allowed, registration_password_allowed, registration_password, registration_default_role, style, ical_password) ON ctfnote.settings TO user_admin; +GRANT UPDATE ("discord_integration_enabled") ON ctfnote.settings TO user_postgraphile; + diff --git a/api/src/index.ts b/api/src/index.ts index 8d92cc41e..b28461238 100644 --- a/api/src/index.ts +++ b/api/src/index.ts @@ -22,6 +22,7 @@ import discordHooks from "./plugins/discordHooks"; import { getDiscordClient } from "./discord"; import PgManyToManyPlugin from "@graphile-contrib/pg-many-to-many"; import ProfileSubscriptionPlugin from "./plugins/ProfileSubscriptionPlugin"; +import { connectToDatabase } from "./discord/database/database"; function getDbUrl(role: "user" | "admin") { const login = config.db[role].login; @@ -154,6 +155,22 @@ async function main() { getDiscordClient(); + const pgClient = await connectToDatabase(); //? maybe we should not keep this dependency in the discord folder? + + try { + const query = + "UPDATE ctfnote.settings SET discord_integration_enabled = $1"; + const values = [config.discord.use.toLowerCase() !== "false"]; + await pgClient.query(query, values); + } catch (error) { + console.error( + "Failed to set discord_integration_enabled flag in the database:", + error + ); + } finally { + pgClient.release(); + } + app.listen(config.web.port, () => { //sendMessageToDiscord("CTFNote API started"); console.log(`Listening on :${config.web.port}`); diff --git a/front/graphql.schema.json b/front/graphql.schema.json index 9f91ea6af..dd9db3579 100644 --- a/front/graphql.schema.json +++ b/front/graphql.schema.json @@ -12122,6 +12122,22 @@ "name": "Setting", "description": null, "fields": [ + { + "name": "discordIntegrationEnabled", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "icalPassword", "description": null, @@ -12248,6 +12264,18 @@ "description": "Represents an update to a `Setting`. Fields that are set will be updated.", "fields": null, "inputFields": [ + { + "name": "discordIntegrationEnabled", + "description": null, + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "icalPassword", "description": null, diff --git a/front/src/components/CTF/Guests.vue b/front/src/components/CTF/Guests.vue index 86f38a2d4..8ec832d75 100644 --- a/front/src/components/CTF/Guests.vue +++ b/front/src/components/CTF/Guests.vue @@ -33,7 +33,12 @@
No guests found.
Sync with Discord event
-
+
+
+ The Discord integration is currently disabled. +
+
+
@@ -55,10 +60,12 @@ export default defineComponent({ setup() { const team = ctfnote.profiles.injectTeam(); const now = ref(new Date()); + const settings = ctfnote.settings.injectSettings(); return { now, team, + settings, inviteUserToCtf: ctfnote.ctfs.useInviteUserToCtf(), uninviteUserToCtf: ctfnote.ctfs.useUninviteUserToCtf(), }; diff --git a/front/src/ctfnote/models.ts b/front/src/ctfnote/models.ts index f505310c8..00debfa7a 100644 --- a/front/src/ctfnote/models.ts +++ b/front/src/ctfnote/models.ts @@ -102,6 +102,7 @@ export type Settings = { registrationAllowed: boolean; registrationPasswordAllowed: boolean; style: SettingsColorMap; + discordIntegrationEnabled: boolean; }; export type AdminSettings = Settings & { diff --git a/front/src/ctfnote/settings.ts b/front/src/ctfnote/settings.ts index 04c6fd746..5096a1d1c 100644 --- a/front/src/ctfnote/settings.ts +++ b/front/src/ctfnote/settings.ts @@ -40,6 +40,7 @@ export function buildSettings( registrationAllowed: fragment.registrationAllowed ?? false, registrationPasswordAllowed: fragment.registrationPasswordAllowed ?? false, style: parseStyle(fragment.style ?? '{}'), + discordIntegrationEnabled: fragment.discordIntegrationEnabled ?? false, }; } diff --git a/front/src/generated/graphql.ts b/front/src/generated/graphql.ts index a2d6744e1..8f374beab 100644 --- a/front/src/generated/graphql.ts +++ b/front/src/generated/graphql.ts @@ -2350,6 +2350,7 @@ export type SetDiscordEventLinkPayload = { export type Setting = Node & { __typename?: 'Setting'; + discordIntegrationEnabled: Scalars['Boolean']; icalPassword?: Maybe; /** A globally unique identifier. Can be used in various places throughout the system to identify this single value. */ nodeId: Scalars['ID']; @@ -2362,6 +2363,7 @@ export type Setting = Node & { /** Represents an update to a `Setting`. Fields that are set will be updated. */ export type SettingPatch = { + discordIntegrationEnabled?: InputMaybe; icalPassword?: InputMaybe; registrationAllowed?: InputMaybe; registrationDefaultRole?: InputMaybe; @@ -3661,14 +3663,14 @@ export type UpdateCredentialsForCtfIdMutationVariables = Exact<{ export type UpdateCredentialsForCtfIdMutation = { __typename?: 'Mutation', updateCtfSecret?: { __typename?: 'UpdateCtfSecretPayload', ctfSecret?: { __typename?: 'CtfSecret', nodeId: string, credentials?: string | null } | null } | null }; -export type SettingsInfoFragment = { __typename?: 'Setting', nodeId: string, registrationAllowed: boolean, registrationPasswordAllowed: boolean, style: string }; +export type SettingsInfoFragment = { __typename?: 'Setting', nodeId: string, registrationAllowed: boolean, registrationPasswordAllowed: boolean, style: string, discordIntegrationEnabled: boolean }; -export type AdminSettingsInfoFragment = { __typename?: 'Setting', nodeId: string, registrationPassword: string, registrationDefaultRole: Role, icalPassword?: string | null, registrationAllowed: boolean, registrationPasswordAllowed: boolean, style: string }; +export type AdminSettingsInfoFragment = { __typename?: 'Setting', nodeId: string, registrationPassword: string, registrationDefaultRole: Role, icalPassword?: string | null, registrationAllowed: boolean, registrationPasswordAllowed: boolean, style: string, discordIntegrationEnabled: boolean }; export type GetSettingsQueryVariables = Exact<{ [key: string]: never; }>; -export type GetSettingsQuery = { __typename?: 'Query', settings?: { __typename?: 'SettingsConnection', nodes: Array<{ __typename?: 'Setting', nodeId: string, registrationAllowed: boolean, registrationPasswordAllowed: boolean, style: string }> } | null }; +export type GetSettingsQuery = { __typename?: 'Query', settings?: { __typename?: 'SettingsConnection', nodes: Array<{ __typename?: 'Setting', nodeId: string, registrationAllowed: boolean, registrationPasswordAllowed: boolean, style: string, discordIntegrationEnabled: boolean }> } | null }; export type GetIcalPasswordQueryVariables = Exact<{ [key: string]: never; }>; @@ -3678,7 +3680,7 @@ export type GetIcalPasswordQuery = { __typename?: 'Query', settings?: { __typena export type GetAdminSettingsQueryVariables = Exact<{ [key: string]: never; }>; -export type GetAdminSettingsQuery = { __typename?: 'Query', settings?: { __typename?: 'SettingsConnection', nodes: Array<{ __typename?: 'Setting', nodeId: string, registrationPassword: string, registrationDefaultRole: Role, icalPassword?: string | null, registrationAllowed: boolean, registrationPasswordAllowed: boolean, style: string }> } | null }; +export type GetAdminSettingsQuery = { __typename?: 'Query', settings?: { __typename?: 'SettingsConnection', nodes: Array<{ __typename?: 'Setting', nodeId: string, registrationPassword: string, registrationDefaultRole: Role, icalPassword?: string | null, registrationAllowed: boolean, registrationPasswordAllowed: boolean, style: string, discordIntegrationEnabled: boolean }> } | null }; export type UpdateSettingsMutationVariables = Exact<{ nodeId: Scalars['ID']; @@ -3686,7 +3688,7 @@ export type UpdateSettingsMutationVariables = Exact<{ }>; -export type UpdateSettingsMutation = { __typename?: 'Mutation', updateSettingByNodeId?: { __typename?: 'UpdateSettingPayload', setting?: { __typename?: 'Setting', nodeId: string, registrationPassword: string, registrationDefaultRole: Role, icalPassword?: string | null, registrationAllowed: boolean, registrationPasswordAllowed: boolean, style: string } | null } | null }; +export type UpdateSettingsMutation = { __typename?: 'Mutation', updateSettingByNodeId?: { __typename?: 'UpdateSettingPayload', setting?: { __typename?: 'Setting', nodeId: string, registrationPassword: string, registrationDefaultRole: Role, icalPassword?: string | null, registrationAllowed: boolean, registrationPasswordAllowed: boolean, style: string, discordIntegrationEnabled: boolean } | null } | null }; export type TagFragment = { __typename?: 'Tag', nodeId: string, id: number, tag: string }; @@ -3955,6 +3957,7 @@ export const SettingsInfoFragmentDoc = gql` registrationAllowed registrationPasswordAllowed style + discordIntegrationEnabled } `; export const AdminSettingsInfoFragmentDoc = gql` @@ -6127,6 +6130,7 @@ export const SettingsInfo = gql` registrationAllowed registrationPasswordAllowed style + discordIntegrationEnabled } `; export const AdminSettingsInfo = gql` diff --git a/front/src/graphql/Settings.graphql b/front/src/graphql/Settings.graphql index 75fbb4932..c3cc15a32 100644 --- a/front/src/graphql/Settings.graphql +++ b/front/src/graphql/Settings.graphql @@ -3,6 +3,7 @@ fragment SettingsInfo on Setting { registrationAllowed registrationPasswordAllowed style + discordIntegrationEnabled } fragment AdminSettingsInfo on Setting { diff --git a/front/src/pages/Settings.vue b/front/src/pages/Settings.vue index df98a3392..d289a2504 100644 --- a/front/src/pages/Settings.vue +++ b/front/src/pages/Settings.vue @@ -138,7 +138,17 @@
Link your Discord account
- + +
The Discord integration is currently disabled.
+
+ +
Your CTFNote account is not linked to your Discord account.
@@ -162,7 +172,11 @@
- + = ref(''); const { result: profileTokenResult } = ctfnote.me.getProfileToken(); + const settings = ctfnote.settings.injectSettings(); + watch( profileTokenResult, (s) => { @@ -242,6 +258,7 @@ export default defineComponent({ username, description, me, + settings, systemNotificationEnabled, askForNotificationPrivilege, disableSystemNotification,