diff --git a/src/actions/signup.ts b/src/actions/signup.ts index a7d4754..b638d41 100644 --- a/src/actions/signup.ts +++ b/src/actions/signup.ts @@ -17,19 +17,33 @@ export const signup = { .max(Date.now() - 1000 * 60 * 60 * 24 * 365 * 6), phone: z.string().trim().nullable(), username: z.string().trim(), - edition: z.enum(['java', 'bedrock']) + edition: z.enum(['java', 'bedrock']), + hash: z.string().nullish() }), handler: async (input) => { + // check if hash given and if so, if it's valid and nobody has already used it + if (input.hash) { + const directSignup = await db.getDirectSignupByHash({ hash: input.hash }); + + if (!directSignup) { + throw new ActionError({ + code: 'NOT_FOUND' + }); + } else if (directSignup.user) { + throw new ActionError({ + code: 'CONFLICT' + }); + } + } // check if signup is allowed - if (!(await getSetting(db, SettingKey.SignupEnabled))) { + else if (!(await getSetting(db, SettingKey.SignupEnabled))) { throw new ActionError({ code: 'FORBIDDEN', message: 'Die Anmeldung ist derzeit deaktiviert' }); } - // check if the user were already signed up - if (await db.getUserByUsername({ username: input.username })) { + else if (await db.getUserByUsername({ username: input.username })) { throw new ActionError({ code: 'CONFLICT', message: 'Du hast dich bereits registriert' @@ -57,14 +71,20 @@ export const signup = { } } - await db.addUser({ - firstname: input.firstname, - lastname: input.lastname, - birthday: input.birthday, - telephone: input.phone, - username: input.username, - edition: input.edition, - uuid: uuid + await db.transaction(async (tx) => { + const user = await tx.addUser({ + firstname: input.firstname, + lastname: input.lastname, + birthday: input.birthday, + telephone: input.phone, + username: input.username, + edition: input.edition, + uuid: uuid + }); + + if (input.hash) { + await tx.setDirectSignupUser({ hash: input.hash, userId: user.id }); + } }); sendWebhook(WebhookAction.Signup, { diff --git a/src/actions/user.ts b/src/actions/user.ts index 6d8b36b..2d374ec 100644 --- a/src/actions/user.ts +++ b/src/actions/user.ts @@ -144,5 +144,31 @@ export const user = { blocked: await db.getBlockedUsers({}) }; } + }), + addDirectInvitation: defineAction({ + handler: async (_, context) => { + Session.actionSessionFromCookies(context.cookies, Permissions.Users); + + return await db.addDirectSignup({}); + } + }), + deleteDirectInvitation: defineAction({ + input: z.object({ + hash: z.string() + }), + handler: async (input, context) => { + Session.actionSessionFromCookies(context.cookies, Permissions.Users); + + await db.deleteDirectSignup({ hash: input.hash }); + } + }), + directInvitations: defineAction({ + handler: async (_, context) => { + Session.actionSessionFromCookies(context.cookies, Permissions.Users); + + return { + directInvitations: await db.getDirectSignups({}) + }; + } }) }; diff --git a/src/app/admin/directInvitations/DirectInvitations.svelte b/src/app/admin/directInvitations/DirectInvitations.svelte new file mode 100644 index 0000000..2aff3ba --- /dev/null +++ b/src/app/admin/directInvitations/DirectInvitations.svelte @@ -0,0 +1,28 @@ + + + diff --git a/src/app/admin/directInvitations/SidebarActions.svelte b/src/app/admin/directInvitations/SidebarActions.svelte new file mode 100644 index 0000000..abbeb61 --- /dev/null +++ b/src/app/admin/directInvitations/SidebarActions.svelte @@ -0,0 +1,32 @@ + + +
+ +
+ + diff --git a/src/app/admin/directInvitations/directInvitations.ts b/src/app/admin/directInvitations/directInvitations.ts new file mode 100644 index 0000000..3e69edf --- /dev/null +++ b/src/app/admin/directInvitations/directInvitations.ts @@ -0,0 +1,45 @@ +import { type ActionReturnType, actions } from 'astro:actions'; +import { writable } from 'svelte/store'; +import { actionErrorPopup } from '@util/action.ts'; +import { addToWritableArray, deleteFromWritableArray } from '@util/state.ts'; + +// types +export type DirectInvitations = Exclude< + ActionReturnType['data'], + undefined +>['directInvitations']; +export type DirectInvitation = DirectInvitations[0]; + +// state +export const directInvitations = writable([]); + +// actions +export async function fetchDirectInvitations() { + const { data, error } = await actions.user.directInvitations(); + if (error) { + actionErrorPopup(error); + return; + } + + directInvitations.set(data.directInvitations); +} + +export async function addDirectInvitation() { + const { data, error } = await actions.user.addDirectInvitation(); + if (error) { + actionErrorPopup(error); + return; + } + + addToWritableArray(directInvitations, data); +} + +export async function deleteDirectInvitation(directInvitation: DirectInvitation) { + const { error } = await actions.user.deleteDirectInvitation(directInvitation); + if (error) { + actionErrorPopup(error); + return; + } + + deleteFromWritableArray(directInvitations, (i) => i.hash === directInvitation.hash); +} diff --git a/src/app/website/signup/RegisteredPopup.svelte b/src/app/website/signup/RegisteredPopup.svelte index d6b2f6b..e7d341b 100644 --- a/src/app/website/signup/RegisteredPopup.svelte +++ b/src/app/website/signup/RegisteredPopup.svelte @@ -8,9 +8,10 @@ paypalLink: string; teamspeakLink: string; startDate: string; + newRegistrationAllowed: boolean; } - let { discordLink, paypalLink, teamspeakLink, startDate }: Props = $props(); + let { discordLink, paypalLink, teamspeakLink, startDate, newRegistrationAllowed }: Props = $props(); let skin: string | null = $state(null); @@ -87,10 +88,12 @@ {/if} -
-
- -
+ {#if newRegistrationAllowed} +
+
+ +
+ {/if}
diff --git a/src/pages/signup.astro b/src/app/website/signup/Signup.astro similarity index 90% rename from src/pages/signup.astro rename to src/app/website/signup/Signup.astro index e438c16..4fa1f99 100644 --- a/src/pages/signup.astro +++ b/src/app/website/signup/Signup.astro @@ -6,28 +6,27 @@ import Select from '@components/input/Select.svelte'; import RulesPopup from '@app/website/signup/RulesPopup.svelte'; import Popup from '@components/popup/Popup.svelte'; import RegisteredPopup from '@app/website/signup/RegisteredPopup.svelte'; -import { getSettings, SettingKey } from '@util/settings'; -import { db } from '@db/database.ts'; import { DISCORD_LINK, PAYPAL_LINK, START_DATE, TEAMSPEAK_LINK } from 'astro:env/server'; -const signupSetting = await getSettings(db, [ - SettingKey.SignupEnabled, - SettingKey.SignupDisabledMessage, - SettingKey.SignupDisabledSubMessage -]); -const signupEnabled = signupSetting[SettingKey.SignupEnabled] ?? false; -const signupDisabledMessage = signupSetting[SettingKey.SignupDisabledMessage] ?? 'Anmeldung deaktiviert'; -const signupDisabledSubMessage = signupSetting[SettingKey.SignupDisabledSubMessage] ?? ''; +interface Props { + signupDisabled?: { + message: string; + subMessage?: string; + }; + signupHash?: string; +} + +const { signupDisabled, signupHash } = Astro.props; ---

Anmeldung

-
+
Persönliche Angaben
{ - !signupEnabled && ( + signupDisabled && (
-

{signupDisabledMessage}

-

{signupDisabledSubMessage}

+

{signupDisabled.message}

+

{signupDisabled.subMessage}

) }