Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Discord integration checks to frontend. #296

Merged
merged 10 commits into from
Jul 18, 2024
8 changes: 8 additions & 0 deletions api/migrations/54-add-discord-integration-enabled.sql
Original file line number Diff line number Diff line change
@@ -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;

17 changes: 17 additions & 0 deletions api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -154,6 +155,22 @@ async function main() {

getDiscordClient();

const pgClient = await connectToDatabase(); //? maybe we should not keep this dependency in the discord folder?
daanbreur marked this conversation as resolved.
Show resolved Hide resolved

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}`);
Expand Down
28 changes: 28 additions & 0 deletions front/graphql.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
9 changes: 8 additions & 1 deletion front/src/components/CTF/Guests.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@
<div v-else class="q-ml-xs"><i>No guests found.</i></div>
</div>
<div class="row text-h6">Sync with Discord event</div>
<div class="row q-mt-sm">
<div v-if="!settings.discordIntegrationEnabled" class="row q-mt-sm">
<div style="width: 590px">
The Discord integration is currently disabled.
</div>
</div>
<div v-else class="row q-mt-sm">
<div style="width: 590px">
<discord-event-link-sync :ctf="ctf" class="col" />
</div>
Expand All @@ -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(),
};
Expand Down
1 change: 1 addition & 0 deletions front/src/ctfnote/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export type Settings = {
registrationAllowed: boolean;
registrationPasswordAllowed: boolean;
style: SettingsColorMap;
discordIntegrationEnabled: boolean;
};

export type AdminSettings = Settings & {
Expand Down
1 change: 1 addition & 0 deletions front/src/ctfnote/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export function buildSettings(
registrationAllowed: fragment.registrationAllowed ?? false,
registrationPasswordAllowed: fragment.registrationPasswordAllowed ?? false,
style: parseStyle(fragment.style ?? '{}'),
discordIntegrationEnabled: fragment.discordIntegrationEnabled ?? false,
};
}

Expand Down
14 changes: 9 additions & 5 deletions front/src/generated/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2350,6 +2350,7 @@ export type SetDiscordEventLinkPayload = {

export type Setting = Node & {
__typename?: 'Setting';
discordIntegrationEnabled: Scalars['Boolean'];
icalPassword?: Maybe<Scalars['String']>;
/** A globally unique identifier. Can be used in various places throughout the system to identify this single value. */
nodeId: Scalars['ID'];
Expand All @@ -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<Scalars['Boolean']>;
icalPassword?: InputMaybe<Scalars['String']>;
registrationAllowed?: InputMaybe<Scalars['Boolean']>;
registrationDefaultRole?: InputMaybe<Role>;
Expand Down Expand Up @@ -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; }>;

Expand All @@ -3678,15 +3680,15 @@ 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'];
patch: SettingPatch;
}>;


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 };

Expand Down Expand Up @@ -3955,6 +3957,7 @@ export const SettingsInfoFragmentDoc = gql`
registrationAllowed
registrationPasswordAllowed
style
discordIntegrationEnabled
}
`;
export const AdminSettingsInfoFragmentDoc = gql`
Expand Down Expand Up @@ -6127,6 +6130,7 @@ export const SettingsInfo = gql`
registrationAllowed
registrationPasswordAllowed
style
discordIntegrationEnabled
}
`;
export const AdminSettingsInfo = gql`
Expand Down
1 change: 1 addition & 0 deletions front/src/graphql/Settings.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ fragment SettingsInfo on Setting {
registrationAllowed
registrationPasswordAllowed
style
discordIntegrationEnabled
}

fragment AdminSettingsInfo on Setting {
Expand Down
21 changes: 19 additions & 2 deletions front/src/pages/Settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,17 @@
<div class="text-h6">Link your Discord account</div>
</q-card-section>

<q-card-section class="q-pt-none q-gutter-md">
<q-card-section
v-if="!settings.discordIntegrationEnabled"
class="q-pt-none q-gutter-md"
>
<div>The Discord integration is currently disabled.</div>
</q-card-section>

<q-card-section
v-if="settings.discordIntegrationEnabled"
class="q-pt-none q-gutter-md"
>
<div v-if="me?.profile.discordId == null">
Your CTFNote account is not linked to your Discord account.
</div>
Expand All @@ -162,7 +172,11 @@
</password-input>
</q-card-section>

<q-card-actions align="right" class="q-px-md q-pb-md q-pt-none">
<q-card-actions
v-if="settings.discordIntegrationEnabled"
daanbreur marked this conversation as resolved.
Show resolved Hide resolved
align="right"
class="q-px-md q-pb-md q-pt-none"
>
<q-btn
v-if="me?.profile.discordId != null"
label="Unlink Discord"
Expand Down Expand Up @@ -205,6 +219,8 @@ export default defineComponent({
const profileToken: Ref<string> = ref('');
const { result: profileTokenResult } = ctfnote.me.getProfileToken();

const settings = ctfnote.settings.injectSettings();

watch(
profileTokenResult,
(s) => {
Expand Down Expand Up @@ -242,6 +258,7 @@ export default defineComponent({
username,
description,
me,
settings,
systemNotificationEnabled,
askForNotificationPrivilege,
disableSystemNotification,
Expand Down