From b9cac2524ffdb1dc1f1a513810260edfc078f946 Mon Sep 17 00:00:00 2001 From: Peter Beverloo Date: Fri, 26 Jan 2024 21:57:00 +0000 Subject: [PATCH] Remove issueServerAction(), migrate remaining users to callApi() --- app/admin/events/EventCreate.tsx | 18 +++--- .../[team]/applications/CreateApplication.tsx | 4 +- .../[volunteer]/VolunteerHeader.tsx | 21 +++---- .../[volunteer]/VolunteerIdentity.tsx | 12 ++-- .../[slug]/hotels/HotelConfiguration.tsx | 19 +++--- .../events/[slug]/refunds/RefundsHeader.tsx | 29 ++++----- .../events/[slug]/settings/EventSettings.tsx | 33 +++++----- .../events/[slug]/settings/SettingsHeader.tsx | 11 ++-- .../events/[slug]/settings/TeamSettings.tsx | 5 +- .../[slug]/training/TrainingConfiguration.tsx | 12 ++-- .../[slug]/training/TrainingExternal.tsx | 35 +++++------ app/admin/system/integrations/AnimeCon.tsx | 5 +- app/admin/system/integrations/Email.tsx | 8 +-- app/admin/system/integrations/Google.tsx | 5 +- .../system/integrations/StatusHeader.tsx | 4 +- app/admin/system/integrations/VertexAI.tsx | 8 +-- app/admin/system/logs/LogsDataTable.tsx | 5 +- app/admin/volunteers/[id]/Header.tsx | 39 +++++------- app/admin/volunteers/[id]/Information.tsx | 26 ++++---- .../volunteers/[id]/VolunteerPrivileges.tsx | 15 ++--- app/admin/volunteers/teams/Roles.tsx | 4 +- app/admin/volunteers/teams/Team.tsx | 5 +- app/components/RemoteContent.tsx | 6 +- app/lib/callApi.ts | 62 ++++++++++++++++++- app/lib/issueServerAction.ts | 31 ---------- app/registration/AuthenticationFlow.tsx | 58 +++++++++-------- .../authentication/IdentityDialog.tsx | 11 ++-- .../LostPasswordResetDialog.tsx | 5 +- .../authentication/RegisterCompleteDialog.tsx | 6 +- 29 files changed, 236 insertions(+), 266 deletions(-) delete mode 100644 app/lib/issueServerAction.ts diff --git a/app/admin/events/EventCreate.tsx b/app/admin/events/EventCreate.tsx index 81443088..7cb0c10c 100644 --- a/app/admin/events/EventCreate.tsx +++ b/app/admin/events/EventCreate.tsx @@ -12,11 +12,10 @@ import Alert from '@mui/material/Alert'; import Paper from '@mui/material/Paper'; import Typography from '@mui/material/Typography'; -import type { CreateEventDefinition } from '@app/api/admin/createEvent'; import { EventSettingsForm } from './[slug]/settings/EventSettingsForm'; import { SubmitCollapse } from '../components/SubmitCollapse'; -import { issueServerAction } from '@lib/issueServerAction'; import { dayjs } from '@lib/DateTime'; +import { callApi } from '@lib/callApi'; /** * The component enables certain volunteers to create new events on the fly. While @@ -34,14 +33,13 @@ export function EventCreate() { setError(undefined); setLoading(true); try { - const response = await issueServerAction( - '/api/admin/create-event', { - name: data.name, - shortName: data.shortName, - slug: data.slug, - startTime: dayjs(data.startTime).utc().toISOString(), - endTime: dayjs(data.endTime).utc().toISOString(), - }); + const response = await callApi('post', '/api/admin/create-event', { + name: data.name, + shortName: data.shortName, + slug: data.slug, + startTime: dayjs(data.startTime).utc().toISOString(), + endTime: dayjs(data.endTime).utc().toISOString(), + }); setError(response.error); if (response.slug) diff --git a/app/admin/events/[slug]/[team]/applications/CreateApplication.tsx b/app/admin/events/[slug]/[team]/applications/CreateApplication.tsx index ea8add95..cc4c8222 100644 --- a/app/admin/events/[slug]/[team]/applications/CreateApplication.tsx +++ b/app/admin/events/[slug]/[team]/applications/CreateApplication.tsx @@ -22,11 +22,9 @@ import Typography from '@mui/material/Typography'; import type { PageInfoWithTeam } from '@app/admin/events/verifyAccessAndFetchPageInfo'; import type { User } from '@lib/auth/User'; -import type { VolunteerListDefinition } from '@app/api/admin/volunteerList'; import { ApplicationAvailabilityForm, ApplicationParticipationForm } from '@app/registration/[slug]/application/ApplicationParticipation'; import { callApi } from '@lib/callApi'; -import { issueServerAction } from '@lib/issueServerAction'; /** * Props accepted by the component. @@ -69,7 +67,7 @@ function VolunteerAutocompleteTextField(props: VolunteerAutocompleteTextFieldPro setFetching(true); - issueServerAction('/api/admin/volunteer-list', { + callApi('post', '/api/admin/volunteer-list', { excludeEventId }).then(({ volunteers }) => { const removeDuplicateMap = new Map(); diff --git a/app/admin/events/[slug]/[team]/volunteers/[volunteer]/VolunteerHeader.tsx b/app/admin/events/[slug]/[team]/volunteers/[volunteer]/VolunteerHeader.tsx index 66d02e11..b4c8328d 100644 --- a/app/admin/events/[slug]/[team]/volunteers/[volunteer]/VolunteerHeader.tsx +++ b/app/admin/events/[slug]/[team]/volunteers/[volunteer]/VolunteerHeader.tsx @@ -38,7 +38,6 @@ import { ContrastBox } from '@app/admin/components/ContrastBox'; import { Privilege, can } from '@lib/auth/Privileges'; import { RegistrationStatus } from '@lib/database/Types'; import { SettingDialog } from '@app/admin/components/SettingDialog'; -import { issueServerAction } from '@lib/issueServerAction'; import { callApi } from '@lib/callApi'; type TeamsForVolunteer = VolunteerTeamsDefinition['response']['teams']; @@ -94,13 +93,12 @@ function ChangeRoleDialog(props: ChangeRoleDialogProps) { const handleChange = useCallback((value: any) => setSelectedRole(value), [ setSelectedRole ]); const handleSubmit = useCallback(async (data: FieldValues) => { - const response = await issueServerAction( - '/api/admin/volunteer-roles', { - eventId, - roleId: data.role, - teamId, - userId: volunteer.userId, - }); + const response = await callApi('post', '/api/admin/volunteer-roles', { + eventId, + roleId: data.role, + teamId, + userId: volunteer.userId, + }); if (response.success) return { success: `${volunteer.firstName}'s role has been successfully updated.` }; @@ -450,10 +448,9 @@ export function VolunteerHeader(props: VolunteerHeaderProps) { if (!roles) { setRolesLoading(true); try { - const response = await issueServerAction( - '/api/admin/volunteer-roles', { - teamId: team.id, - }); + const response = await callApi('post', '/api/admin/volunteer-roles', { + teamId: team.id, + }); setRoles(response.roles); } finally { diff --git a/app/admin/events/[slug]/[team]/volunteers/[volunteer]/VolunteerIdentity.tsx b/app/admin/events/[slug]/[team]/volunteers/[volunteer]/VolunteerIdentity.tsx index f82030aa..c853fcbb 100644 --- a/app/admin/events/[slug]/[team]/volunteers/[volunteer]/VolunteerIdentity.tsx +++ b/app/admin/events/[slug]/[team]/volunteers/[volunteer]/VolunteerIdentity.tsx @@ -22,10 +22,8 @@ import Typography from '@mui/material/Typography'; import WarningAmberIcon from '@mui/icons-material/WarningAmber'; import { RegistrationStatus } from '@lib/database/Types'; -import type { VolunteerContactInfoDefinition } from '@app/api/admin/volunteerContactInfo'; import { Avatar } from '@components/Avatar'; import { callApi } from '@lib/callApi'; -import { issueServerAction } from '@lib/issueServerAction'; /** * Displays a loading skeleton to inform the user that contact information has not been loaded yet. @@ -125,11 +123,11 @@ export function VolunteerIdentity(props: VolunteerIdentityProps) { return; // contact information hasn't been requested setContactInfoLoading(true); - issueServerAction( - '/api/admin/volunteer-contact-info', { event, teamId, userId }).then(response => - { - setContactInfo(response); - }); + callApi('post', '/api/admin/volunteer-contact-info', { event, teamId, userId }).then( + response => + { + setContactInfo(response); + }); }, [ contactInfo, contactInfoLoading, emailAnchorEl, event, phoneNumberAnchorEl, teamId, userId ]) diff --git a/app/admin/events/[slug]/hotels/HotelConfiguration.tsx b/app/admin/events/[slug]/hotels/HotelConfiguration.tsx index f0b20f82..52a4c2d8 100644 --- a/app/admin/events/[slug]/hotels/HotelConfiguration.tsx +++ b/app/admin/events/[slug]/hotels/HotelConfiguration.tsx @@ -10,12 +10,10 @@ import type { GridRenderCellParams, GridValidRowModel } from '@mui/x-data-grid'; import Paper from '@mui/material/Paper'; import Typography from '@mui/material/Typography'; -import type { HotelDefinition } from '@app/api/admin/hotel'; import type { PageInfo } from '@app/admin/events/verifyAccessAndFetchPageInfo'; -import type { UpdatePublicationDefinition } from '@app/api/admin/updatePublication'; import { type DataTableColumn, OLD_DataTable } from '@app/admin/DataTable'; import { PublishAlert } from '@app/admin/components/PublishAlert'; -import { issueServerAction } from '@lib/issueServerAction'; +import { callApi } from '@lib/callApi'; /** * Helper function for formatting prices in the configuration data table. @@ -80,7 +78,7 @@ export function HotelConfiguration(props: HotelConfigurationProps) { const { event } = props; async function commitAdd(): Promise { - const response = await issueServerAction('/api/admin/hotel', { + const response = await callApi('post', '/api/admin/hotel', { event: event.slug, create: { /* empty payload */ } }); @@ -99,7 +97,7 @@ export function HotelConfiguration(props: HotelConfigurationProps) { } async function commitDelete(oldRow: GridValidRowModel) { - await issueServerAction('/api/admin/hotel', { + await callApi('post', '/api/admin/hotel', { event: event.slug, delete: { id: oldRow.id, @@ -108,7 +106,7 @@ export function HotelConfiguration(props: HotelConfigurationProps) { } async function commitEdit(newRow: GridValidRowModel, oldRow: GridValidRowModel) { - const response = await issueServerAction('/api/admin/hotel', { + const response = await callApi('post', '/api/admin/hotel', { event: event.slug, update: { id: oldRow.id, @@ -126,11 +124,10 @@ export function HotelConfiguration(props: HotelConfigurationProps) { const router = useRouter(); const onPublish = useCallback(async (domEvent: unknown, publish: boolean) => { - const response = await issueServerAction( - '/api/admin/update-publication', { - event: event.slug, - publishHotels: !!publish, - }); + const response = await callApi('post', '/api/admin/update-publication', { + event: event.slug, + publishHotels: !!publish, + }); if (response.success) router.refresh(); diff --git a/app/admin/events/[slug]/refunds/RefundsHeader.tsx b/app/admin/events/[slug]/refunds/RefundsHeader.tsx index 5fb8d0a6..fe6bd7cb 100644 --- a/app/admin/events/[slug]/refunds/RefundsHeader.tsx +++ b/app/admin/events/[slug]/refunds/RefundsHeader.tsx @@ -15,12 +15,10 @@ import Grid from '@mui/material/Unstable_Grid2'; import Paper from '@mui/material/Paper'; import type { PageInfo } from '@app/admin/events/verifyAccessAndFetchPageInfo'; -import type { UpdateEventDefinition } from '@app/api/admin/updateEvent'; -import type { UpdatePublicationDefinition } from '@app/api/admin/updatePublication'; import { PaperHeader } from '@app/admin/components/PaperHeader'; import { PublishAlert } from '@app/admin/components/PublishAlert'; import { SubmitCollapse } from '@app/admin/components/SubmitCollapse'; -import { issueServerAction } from '@lib/issueServerAction'; +import { callApi } from '@lib/callApi'; import { dayjs } from '@lib/DateTime'; /** @@ -65,15 +63,13 @@ export function RefundsHeader(props: RefundsHeaderProps) { data.refundsEndTime ? dayjs(data.refundsEndTime).utc().toISOString() : undefined; - const response = await issueServerAction( - '/api/admin/update-event', - { - event: event.slug, - eventRefunds: { - refundsStartTime, - refundsEndTime, - }, - }); + const response = await callApi('post', '/api/admin/update-event', { + event: event.slug, + eventRefunds: { + refundsStartTime, + refundsEndTime, + }, + }); if (response.success) { setInvalidated(false); @@ -90,11 +86,10 @@ export function RefundsHeader(props: RefundsHeaderProps) { const [ published, setPublished ] = useState(event.publishRefunds); const handleRefundPublicationChange = useCallback(async () => { - const response = await issueServerAction( - '/api/admin/update-publication', { - event: event.slug, - publishRefunds: !published, - }); + const response = await callApi('post', '/api/admin/update-publication', { + event: event.slug, + publishRefunds: !published, + }); if (response.success) { setPublished(published => !published); diff --git a/app/admin/events/[slug]/settings/EventSettings.tsx b/app/admin/events/[slug]/settings/EventSettings.tsx index 9f2f319c..8fcb7f40 100644 --- a/app/admin/events/[slug]/settings/EventSettings.tsx +++ b/app/admin/events/[slug]/settings/EventSettings.tsx @@ -14,11 +14,10 @@ import Paper from '@mui/material/Paper'; import Typography from '@mui/material/Typography'; import type { PageInfo } from '@app/admin/events/verifyAccessAndFetchPageInfo'; -import type { UpdateEventDefinition } from '@app/api/admin/updateEvent'; import { EventAvailabilityStatus } from '@lib/database/Types'; import { EventSettingsForm } from './EventSettingsForm'; import { SubmitCollapse } from '@app/admin/components/SubmitCollapse'; -import { issueServerAction } from '@lib/issueServerAction'; +import { callApi } from '@lib/callApi'; import { dayjs } from '@lib/DateTime'; /** @@ -66,22 +65,20 @@ export function EventSettings(props: EventSettingsProps) { const handleSubmit = useCallback(async (data: FieldValues) => { setLoading(true); try { - const response = await issueServerAction( - '/api/admin/update-event', - { - event: event.slug, - eventSettings: { - name: data.name, - shortName: data.shortName, - timezone: data.timezone, - startTime: dayjs(data.startTime).utc().toISOString(), - endTime: dayjs(data.endTime).utc().toISOString(), - availabilityStatus: data.availabilityStatus, - location: data.location, - festivalId: data.festivalId, - hotelRoomForm: data.hotelRoomForm, - }, - }); + const response = await callApi('post', '/api/admin/update-event', { + event: event.slug, + eventSettings: { + name: data.name, + shortName: data.shortName, + timezone: data.timezone, + startTime: dayjs(data.startTime).utc().toISOString(), + endTime: dayjs(data.endTime).utc().toISOString(), + availabilityStatus: data.availabilityStatus, + location: data.location, + festivalId: data.festivalId, + hotelRoomForm: data.hotelRoomForm, + }, + }); if (response.success) { setInvalidated(false); diff --git a/app/admin/events/[slug]/settings/SettingsHeader.tsx b/app/admin/events/[slug]/settings/SettingsHeader.tsx index c684bfe0..b9ae72c3 100644 --- a/app/admin/events/[slug]/settings/SettingsHeader.tsx +++ b/app/admin/events/[slug]/settings/SettingsHeader.tsx @@ -19,12 +19,11 @@ import StopCircleIcon from '@mui/icons-material/StopCircle'; import Typography from '@mui/material/Typography'; import type { PageInfo } from '@app/admin/events/verifyAccessAndFetchPageInfo'; -import type { UpdateEventDefinition } from '@app/api/admin/updateEvent'; import { ContrastBox } from '@app/admin/components/ContrastBox'; import { LazyAvatarEditor } from '@components/LazyAvatarEditor'; import { SettingDialog } from '@app/admin/components/SettingDialog'; import { TransitionAlert } from '@app/admin/components/TransitionAlert'; -import { issueServerAction } from '@lib/issueServerAction'; +import { callApi } from '@lib/callApi'; /** * Description to use for the event publication dialog. @@ -86,7 +85,7 @@ export function SettingsHeader(props: SettingsHeaderProps) { const openPublish = useCallback(() => setPublishOpen(true), [ /* no deps */ ]); const handlePublish = useCallback(async () => { - const response = await issueServerAction('/api/admin/update-event', { + const response = await callApi('post', '/api/admin/update-event', { event: event.slug, eventHidden: /* publish= */ false, }); @@ -97,7 +96,7 @@ export function SettingsHeader(props: SettingsHeaderProps) { const openSuspend = useCallback(() => setSuspendOpen(true), [ /* no deps */ ]); const handleSuspend = useCallback(async () => { - const response = await issueServerAction('/api/admin/update-event', { + const response = await callApi('post', '/api/admin/update-event', { event: event.slug, eventHidden: /* suspend= */ true, }); @@ -118,7 +117,7 @@ export function SettingsHeader(props: SettingsHeaderProps) { reader.readAsDataURL(avatar); }); - const response = await issueServerAction('/api/admin/update-event', { + const response = await callApi('post', '/api/admin/update-event', { event: event.slug, eventIdentity: base64Avatar as string, }); @@ -133,7 +132,7 @@ export function SettingsHeader(props: SettingsHeaderProps) { const openSlug = useCallback(() => setSlugOpen(true), [ /* no deps */ ]); const handleSlug = useCallback(async (data: any) => { - const response = await issueServerAction('/api/admin/update-event', { + const response = await callApi('post', '/api/admin/update-event', { event: event.slug, eventSlug: data.slug, }); diff --git a/app/admin/events/[slug]/settings/TeamSettings.tsx b/app/admin/events/[slug]/settings/TeamSettings.tsx index 2bc7d433..6265162e 100644 --- a/app/admin/events/[slug]/settings/TeamSettings.tsx +++ b/app/admin/events/[slug]/settings/TeamSettings.tsx @@ -15,9 +15,8 @@ import Typography from '@mui/material/Typography'; import type { DataTableColumn } from '@app/admin/DataTable'; import type { PageInfo } from '@app/admin/events/verifyAccessAndFetchPageInfo'; -import type { UpdateEventDefinition } from '@app/api/admin/updateEvent'; import { OLD_DataTable } from '@app/admin/DataTable'; -import { issueServerAction } from '@lib/issueServerAction'; +import { callApi } from '@lib/callApi'; /** * Settings related to an individual team whose settings can be updated on this page. @@ -175,7 +174,7 @@ export function TeamSettings(props: TeamSettingsProps) { const router = useRouter(); const handleEdit = useCallback(async (newRow: GridValidRowModel, oldRow: GridValidRowModel) => { - const response = await issueServerAction('/api/admin/update-event', { + const response = await callApi('post', '/api/admin/update-event', { event: event.slug, team: { id: oldRow.id, diff --git a/app/admin/events/[slug]/training/TrainingConfiguration.tsx b/app/admin/events/[slug]/training/TrainingConfiguration.tsx index 187e03c5..37bb23b1 100644 --- a/app/admin/events/[slug]/training/TrainingConfiguration.tsx +++ b/app/admin/events/[slug]/training/TrainingConfiguration.tsx @@ -11,11 +11,10 @@ import Typography from '@mui/material/Typography'; import type { PageInfo } from '@app/admin/events/verifyAccessAndFetchPageInfo'; import type { TrainingsRowModel } from '@app/api/admin/trainings/[[...id]]/route'; -import type { UpdatePublicationDefinition } from '@app/api/admin/updatePublication'; import { PublishAlert } from '@app/admin/components/PublishAlert'; import { RemoteDataTable, type RemoteDataTableColumn } from '@app/admin/components/RemoteDataTable'; +import { callApi } from '@lib/callApi'; import { dayjs, fromLocalDate, toLocalDate } from '@lib/DateTime'; -import { issueServerAction } from '@lib/issueServerAction'; /** * Props accepted by the component. @@ -37,11 +36,10 @@ export function TrainingConfiguration(props: TrainingConfigurationProps) { const router = useRouter(); const onPublish = useCallback(async (domEvent: unknown, publish: boolean) => { - const response = await issueServerAction( - '/api/admin/update-publication', { - event: event.slug, - publishTrainings: !!publish, - }); + const response = await callApi('post', '/api/admin/update-publication', { + event: event.slug, + publishTrainings: !!publish, + }); if (response.success) router.refresh(); diff --git a/app/admin/events/[slug]/training/TrainingExternal.tsx b/app/admin/events/[slug]/training/TrainingExternal.tsx index bc3aac59..f3b1492e 100644 --- a/app/admin/events/[slug]/training/TrainingExternal.tsx +++ b/app/admin/events/[slug]/training/TrainingExternal.tsx @@ -13,10 +13,9 @@ import Tooltip from '@mui/material/Tooltip'; import Typography from '@mui/material/Typography'; import type { PageInfo } from '@app/admin/events/verifyAccessAndFetchPageInfo'; -import type { TrainingExtraDefinition } from '@app/api/admin/trainingExtra'; import { type DataTableColumn, OLD_DataTable } from '@app/admin/DataTable'; import { dayjs } from '@lib/DateTime'; -import { issueServerAction } from '@lib/issueServerAction'; +import { callApi } from '@lib/callApi'; /** * Configuration options available for extra training participants. @@ -81,11 +80,10 @@ export function TrainingExternal(props: TrainingExternalProps) { const { event } = props; async function commitAdd(): Promise { - const response = await issueServerAction( - '/api/admin/training-extra', { - event: event.slug, - create: { /* empty payload */ } - }); + const response = await callApi('post', '/api/admin/training-extra', { + event: event.slug, + create: { /* empty payload */ } + }); if (!response.id) throw new Error('The server was unable to create a new participant.'); @@ -101,7 +99,7 @@ export function TrainingExternal(props: TrainingExternalProps) { } async function commitDelete(oldRow: GridValidRowModel) { - await issueServerAction('/api/admin/training-extra', { + await callApi('post', '/api/admin/training-extra', { event: event.slug, delete: { id: oldRow.id, @@ -110,17 +108,16 @@ export function TrainingExternal(props: TrainingExternalProps) { } const commitEdit = useCallback(async (newRow: GridValidRowModel, oldRow: GridValidRowModel) => { - const response = await issueServerAction( - '/api/admin/training-extra', { - event: event.slug, - update: { - id: oldRow.id, - trainingExtraName: newRow.trainingExtraName, - trainingExtraEmail: newRow.trainingExtraEmail, - trainingExtraBirthdate: newRow.trainingExtraBirthdate, - preferenceTrainingId: newRow.preferenceTrainingId, - } - }); + const response = await callApi('post', '/api/admin/training-extra', { + event: event.slug, + update: { + id: oldRow.id, + trainingExtraName: newRow.trainingExtraName, + trainingExtraEmail: newRow.trainingExtraEmail, + trainingExtraBirthdate: newRow.trainingExtraBirthdate, + preferenceTrainingId: newRow.preferenceTrainingId, + } + }); if (!response.success) return oldRow; diff --git a/app/admin/system/integrations/AnimeCon.tsx b/app/admin/system/integrations/AnimeCon.tsx index b73dca1d..037deaa5 100644 --- a/app/admin/system/integrations/AnimeCon.tsx +++ b/app/admin/system/integrations/AnimeCon.tsx @@ -17,9 +17,8 @@ import Paper from '@mui/material/Paper'; import Typography from '@mui/material/Typography'; import type { AnimeConClientSettings } from '@lib/integrations/animecon/AnimeConClient'; -import type { UpdateIntegrationDefinition } from '@app/api/admin/updateIntegration'; import { SubmitCollapse } from '../../components/SubmitCollapse'; -import { issueServerAction } from '@lib/issueServerAction'; +import { callApi } from '@lib/callApi'; /** * Settings applicable to the component that can be edited through this component. We @@ -59,7 +58,7 @@ export function AnimeCon(props: GoogleProps) { setLoading(true); setError(undefined); try { - await issueServerAction('/api/admin/update-integration', { + await callApi('post', '/api/admin/update-integration', { animecon: { ...data as any }, }); setInvalidated(false); diff --git a/app/admin/system/integrations/Email.tsx b/app/admin/system/integrations/Email.tsx index 8e795d13..fe7ca7d0 100644 --- a/app/admin/system/integrations/Email.tsx +++ b/app/admin/system/integrations/Email.tsx @@ -5,8 +5,7 @@ import { useCallback, useState } from 'react'; -import { type FieldValues, FormContainer, SelectElement, TextFieldElement } - from 'react-hook-form-mui'; +import { type FieldValues, FormContainer, TextFieldElement } from 'react-hook-form-mui'; import Alert from '@mui/material/Alert'; import Grid from '@mui/material/Unstable_Grid2'; @@ -14,9 +13,8 @@ import Paper from '@mui/material/Paper'; import Typography from '@mui/material/Typography'; import type { EmailClientSettings } from '@lib/integrations/email/EmailClient'; -import type { UpdateIntegrationDefinition } from '@app/api/admin/updateIntegration'; import { SubmitCollapse } from '../../components/SubmitCollapse'; -import { issueServerAction } from '@lib/issueServerAction'; +import { callApi } from '@lib/callApi'; export type EmailSettings = EmailClientSettings; @@ -46,7 +44,7 @@ export function Email(props: EmailProps) { setLoading(true); setError(undefined); try { - await issueServerAction('/api/admin/update-integration', { + await callApi('post', '/api/admin/update-integration', { email: { hostname: data.hostname, port: parseInt(data.port, 10), diff --git a/app/admin/system/integrations/Google.tsx b/app/admin/system/integrations/Google.tsx index 99db509b..139d3563 100644 --- a/app/admin/system/integrations/Google.tsx +++ b/app/admin/system/integrations/Google.tsx @@ -13,9 +13,8 @@ import Grid from '@mui/material/Unstable_Grid2'; import Paper from '@mui/material/Paper'; import Typography from '@mui/material/Typography'; -import type { UpdateIntegrationDefinition } from '@app/api/admin/updateIntegration'; import { SubmitCollapse } from '../../components/SubmitCollapse'; -import { issueServerAction } from '@lib/issueServerAction'; +import { callApi } from '@lib/callApi'; /** * Google endpoint locations that are available in the Volunteer Manager. @@ -80,7 +79,7 @@ export function Google(props: GoogleProps) { setLoading(true); setError(undefined); try { - await issueServerAction('/api/admin/update-integration', { + await callApi('post', '/api/admin/update-integration', { google: { apiKey: data.apiKey, credential: data.credential, diff --git a/app/admin/system/integrations/StatusHeader.tsx b/app/admin/system/integrations/StatusHeader.tsx index 8dff92e2..4f09439a 100644 --- a/app/admin/system/integrations/StatusHeader.tsx +++ b/app/admin/system/integrations/StatusHeader.tsx @@ -22,7 +22,7 @@ import Typography from '@mui/material/Typography'; import type { ServiceHealthDefinition } from '@app/api/admin/serviceHealth'; import { ContrastBox } from '../../components/ContrastBox'; -import { issueServerAction } from '@lib/issueServerAction'; +import { callApi } from '@lib/callApi'; type ServiceHealthRequest = ServiceHealthDefinition['request']; type ServiceHealthResponse = ServiceHealthDefinition['response']; @@ -35,7 +35,7 @@ async function determineServiceStatus(service: ServiceHealthRequest['service']) : Promise { try { - return issueServerAction('/api/admin/service-health', { + return callApi('post', '/api/admin/service-health', { service, }); } catch (error: any) { diff --git a/app/admin/system/integrations/VertexAI.tsx b/app/admin/system/integrations/VertexAI.tsx index 87e2aa50..20fdfccd 100644 --- a/app/admin/system/integrations/VertexAI.tsx +++ b/app/admin/system/integrations/VertexAI.tsx @@ -19,13 +19,11 @@ import SmartToyIcon from '@mui/icons-material/SmartToy'; import Stack from '@mui/material/Stack'; import Typography from '@mui/material/Typography'; -import type { UpdateIntegrationDefinition } from '@app/api/admin/updateIntegration'; -import type { VertexAiDefinition } from '@app/api/admin/vertexAi'; import type { VertexAISettings } from '@lib/integrations/vertexai/VertexAIClient'; import { PlaceholderPaper } from '../../components/PlaceholderPaper'; import { SubmitCollapse } from '../../components/SubmitCollapse'; import { VertexSupportedModels } from '@lib/integrations/vertexai/VertexSupportedModels'; -import { issueServerAction } from '@lib/issueServerAction'; +import { callApi } from '@lib/callApi'; export type { VertexAISettings }; @@ -272,7 +270,7 @@ export function VertexAI(props: VertexAIProps) { setResponse(undefined); try { - const response = await issueServerAction('/api/admin/vertex-ai', { + const response = await callApi('post', '/api/admin/vertex-ai', { prompt, settings, }); @@ -291,7 +289,7 @@ export function VertexAI(props: VertexAIProps) { }, [ setSettings ]); const saveSettings = useCallback(async () => { - await issueServerAction('/api/admin/update-integration', { + await callApi('post', '/api/admin/update-integration', { vertexAi: settings, }); }, [ settings ]); diff --git a/app/admin/system/logs/LogsDataTable.tsx b/app/admin/system/logs/LogsDataTable.tsx index 7fc27e24..fd6e5b36 100644 --- a/app/admin/system/logs/LogsDataTable.tsx +++ b/app/admin/system/logs/LogsDataTable.tsx @@ -16,10 +16,9 @@ import WarningOutlinedIcon from '@mui/icons-material/WarningOutlined'; import type { DataTableBaseProps, DataTableColumn } from '@app/admin/DataTable'; import type { DataTableRowRequest } from '@app/admin/DataTable'; -import type { LogsDefinition } from '@app/api/admin/logs'; import { OLD_DataTable } from '../../DataTable'; +import { callApi } from '@lib/callApi'; import { dayjs } from '@lib/DateTime'; -import { issueServerAction } from '@lib/issueServerAction'; /** * Props made available to the component. @@ -114,7 +113,7 @@ export function LogsDataTable(props: LogsDataTableProps) { ]), [ /* no deps */ ]); const onRequestRows = useCallback(async (request: DataTableRowRequest) => { - const response = await issueServerAction('/api/admin/logs', { + const response = await callApi('post', '/api/admin/logs', { filters: { sourceOrTargetUserId: filters?.sourceOrTargetUserId, }, diff --git a/app/admin/volunteers/[id]/Header.tsx b/app/admin/volunteers/[id]/Header.tsx index 51153881..6ba5e802 100644 --- a/app/admin/volunteers/[id]/Header.tsx +++ b/app/admin/volunteers/[id]/Header.tsx @@ -19,13 +19,9 @@ import Stack from '@mui/material/Stack'; import Typography from '@mui/material/Typography'; import UnpublishedIcon from '@mui/icons-material/Unpublished'; -import type { ResetAccessCodeDefinition } from '@app/api/admin/resetAccessCode'; -import type { ResetPasswordLinkDefinition } from '@app/api/admin/resetPasswordLink'; -import type { UpdateActivationDefinition } from '@app/api/admin/updateActivation'; import type { VolunteerInfo } from './page'; import { ContrastBox } from '@app/admin/components/ContrastBox'; import { SettingDialog } from '@app/admin/components/SettingDialog'; -import { issueServerAction } from '@lib/issueServerAction'; import { callApi } from '@lib/callApi'; /** @@ -56,10 +52,9 @@ function AccessCodeDialog(props: DialogProps) { const { account, onClose, open } = props; const handleSubmit = useCallback(async() => { - const response = await issueServerAction( - '/api/admin/reset-access-code', { - userId: account.userId - }); + const response = await callApi('post', '/api/admin/reset-access-code', { + userId: account.userId + }); if (!response.accessCode) return { error: `Unable to retrieve ${account.firstName}'s access code right now` }; @@ -89,12 +84,10 @@ function ActivateDialog(props: DialogProps) { const { account, onClose, open } = props; const handleSubmit = useCallback(async() => { - const response = await issueServerAction( - '/api/admin/update-activation', - { - userId: account.userId, - activated: true, - }); + const response = await callApi('post', '/api/admin/update-activation', { + userId: account.userId, + activated: true, + }); if (response.success) return { success: `${account.firstName}'s account has been activated` }; @@ -123,12 +116,10 @@ function DeactivateDialog(props: DialogProps) { const { account, onClose, open } = props; const handleSubmit = useCallback(async() => { - const response = await issueServerAction( - '/api/admin/update-activation', - { - userId: account.userId, - activated: false, - }); + const response = await callApi('post', '/api/admin/update-activation', { + userId: account.userId, + activated: false, + }); if (response.success) return { success: `${account.firstName}'s account has been deactivated` }; @@ -157,11 +148,9 @@ function PasswordResetDialog(props: DialogProps) { const { account, onClose, open } = props; const handleSubmit = useCallback(async() => { - const response = await issueServerAction( - '/api/admin/reset-password-link', - { - userId: account.userId - }); + const response = await callApi('post', '/api/admin/reset-password-link', { + userId: account.userId + }); if (!response.link) return { error: 'The password reset link could not be created just now' }; diff --git a/app/admin/volunteers/[id]/Information.tsx b/app/admin/volunteers/[id]/Information.tsx index aad084ba..11190a66 100644 --- a/app/admin/volunteers/[id]/Information.tsx +++ b/app/admin/volunteers/[id]/Information.tsx @@ -14,10 +14,10 @@ import Grid from '@mui/material/Unstable_Grid2'; import Paper from '@mui/material/Paper'; import Typography from '@mui/material/Typography'; -import type { UpdateVolunteerDefinition } from '@app/api/admin/updateVolunteer'; import type { VolunteerInfo } from './page'; import { SubmitCollapse } from '../../components/SubmitCollapse'; -import { issueServerAction } from '@lib/issueServerAction'; +import { callApi } from '@lib/callApi'; + import { kGenderOptions } from '@app/registration/authentication/RegisterForm'; /** @@ -50,18 +50,16 @@ export function Information(props: InformationProps) { try { const birthdate = dayjs(data.rawBirthdate); - const response = await issueServerAction( - '/api/admin/update-volunteer', - { - userId: account.userId, - firstName: data.firstName, - lastName: data.lastName, - username: data.username ?? undefined, - gender: data.gender, - birthdate: birthdate.isValid() ? birthdate.format('YYYY-MM-DD') - : undefined, - phoneNumber: data.phoneNumber ?? undefined, - }); + const response = await callApi('post', '/api/admin/update-volunteer', { + userId: account.userId, + firstName: data.firstName, + lastName: data.lastName, + username: data.username ?? undefined, + gender: data.gender, + birthdate: birthdate.isValid() ? birthdate.format('YYYY-MM-DD') + : undefined, + phoneNumber: data.phoneNumber ?? undefined, + }); if (response.success) { setInvalidated(false); diff --git a/app/admin/volunteers/[id]/VolunteerPrivileges.tsx b/app/admin/volunteers/[id]/VolunteerPrivileges.tsx index e50dbc66..5ba86107 100644 --- a/app/admin/volunteers/[id]/VolunteerPrivileges.tsx +++ b/app/admin/volunteers/[id]/VolunteerPrivileges.tsx @@ -20,10 +20,7 @@ import Typography from '@mui/material/Typography'; import WarningIcon from '@mui/icons-material/Warning'; import { red } from '@mui/material/colors'; -import type { UpdatePermissionsDefinition } from '@app/api/admin/updatePermissions'; -import { SubmitCollapse } from '@app/admin/components/SubmitCollapse'; -import { issueServerAction } from '@lib/issueServerAction'; - +import { callApi } from '@lib/callApi'; import { PrivilegeGroups, PrivilegeNames, PrivilegeWarnings, Privilege } from '@lib/auth/Privileges'; @@ -85,12 +82,10 @@ export function VolunteerPrivileges(props: VolunteerPrivilegesProps) { const save = useCallback(async () => { setLoading(true); try { - const response = await issueServerAction( - '/api/admin/update-permissions', - { - userId: props.userId, - privileges: privileges.toString(), - }); + const response = await callApi('post', '/api/admin/update-permissions', { + userId: props.userId, + privileges: privileges.toString(), + }); if (!response.success) { console.warn('Unable to update permissions for this user.'); diff --git a/app/admin/volunteers/teams/Roles.tsx b/app/admin/volunteers/teams/Roles.tsx index 510a0037..65a6c2ff 100644 --- a/app/admin/volunteers/teams/Roles.tsx +++ b/app/admin/volunteers/teams/Roles.tsx @@ -17,7 +17,7 @@ import type { DataTableColumn } from '@app/admin/DataTable'; import type { UpdateRoleDefinition } from '@app/api/admin/updateRole'; import { OLD_DataTable } from '@app/admin/DataTable'; import { VolunteerBadge, VolunteerBadgeVariant } from '@components/VolunteerBadge'; -import { issueServerAction } from '@lib/issueServerAction'; +import { callApi } from '@lib/callApi'; /** * Interface representation of a role in the Volunteer Manager. @@ -131,7 +131,7 @@ export function Roles(props: RolesProps) { const router = useRouter(); const commitEdit = useCallback(async (newRow: GridValidRowModel, oldRow: GridValidRowModel) => { - const response = await issueServerAction('/api/admin/update-role', { + const response = await callApi('post', '/api/admin/update-role', { ...newRow as Role, roleBadge: newRow.roleBadge !== '(none)' ? newRow.roleBadge : undefined, }); diff --git a/app/admin/volunteers/teams/Team.tsx b/app/admin/volunteers/teams/Team.tsx index 081a9309..60da1265 100644 --- a/app/admin/volunteers/teams/Team.tsx +++ b/app/admin/volunteers/teams/Team.tsx @@ -12,7 +12,6 @@ import { import type { SxProps, Theme } from '@mui/system'; import Box from '@mui/material/Box'; -import Chip from '@mui/material/Chip'; import Grid from '@mui/material/Unstable_Grid2'; import Paper from '@mui/material/Paper'; import Stack from '@mui/material/Stack'; @@ -22,7 +21,7 @@ import { deepmerge } from '@mui/utils'; import type { Role } from './Roles'; import type { UpdateTeamDefinition } from '@app/api/admin/updateTeam'; import { SubmitCollapse } from '@app/admin/components/SubmitCollapse'; -import { issueServerAction } from '@lib/issueServerAction'; +import { callApi } from '@lib/callApi'; /** * Custom styles applied to the & related components. @@ -120,7 +119,7 @@ export function Team(props: TeamProps) { const handleSubmit = useCallback(async (data: FieldValues) => { setLoading(true); try { - const result = await issueServerAction('/api/admin/update-team', { + const result = await callApi('post', '/api/admin/update-team', { ...data as UpdateTeamDefinition['request'], teamDefaultRole: data.teamDefaultRole, diff --git a/app/components/RemoteContent.tsx b/app/components/RemoteContent.tsx index 3ecde3ce..3ae0fdfe 100644 --- a/app/components/RemoteContent.tsx +++ b/app/components/RemoteContent.tsx @@ -21,7 +21,7 @@ import { alpha, darken, lighten } from '@mui/system'; import type { HotelsDefinition } from '@app/api/event/hotels'; import type { TrainingsDefinition } from '@app/api/event/trainings'; import { Markdown } from './Markdown'; -import { issueServerAction } from '@lib/issueServerAction'; +import { callApi } from '@lib/callApi'; /** * Mechanism for formatting hotel room prices in euros. Prefer Intl.NumberFormat, but fall back to @@ -80,7 +80,7 @@ function RemoteContentHotels(props: Omit) { if (!props.event) return; - issueServerAction('/api/event/hotels', { + callApi('post', '/api/event/hotels', { event: props.event }).then(response => setHotels(response)); @@ -152,7 +152,7 @@ function RemoteContentTrainings(props: Omit) { if (!props.event) return; - issueServerAction('/api/event/trainings', { + callApi('post', '/api/event/trainings', { event: props.event }).then(response => setTrainings(response)); diff --git a/app/lib/callApi.ts b/app/lib/callApi.ts index 787de5ed..ae2f5306 100644 --- a/app/lib/callApi.ts +++ b/app/lib/callApi.ts @@ -3,6 +3,7 @@ import type { ApplicationDefinition } from '@app/api/event/application'; import type { AvailabilityPreferencesDefinition } from '@app/api/event/availabilityPreferences'; +import type { ConfirmIdentityDefinition } from '@app/api/auth/confirmIdentity'; import type { CreateBookingDefinition } from '@app/api/admin/hotel-bookings/createBooking'; import type { CreateChallengeDefinition } from '@app/api/auth/passkeys/createChallenge'; import type { CreateEventDefinition } from '@app/api/admin/createEvent'; @@ -11,21 +12,50 @@ import type { DeletePasskeyDefinition } from '@app/api/auth/passkeys/deletePassk import type { ExportsDefinition } from '@app/api/exports/route'; import type { GeneratePromptDefinition } from '@app/api/ai/generatePrompt'; import type { GetOutboxDefinition } from '@app/api/admin/outbox/getOutbox'; +import type { HotelDefinition } from '@app/api/admin/hotel'; import type { HotelPreferencesDefinition } from '@app/api/event/hotelPreferences'; import type { HotelsDefinition } from '@app/api/event/hotels'; import type { ListOutboxDefinition } from '@app/api/admin/outbox/listOutbox'; import type { ListPasskeysDefinition } from '@app/api/auth/passkeys/listPasskeys'; +import type { LogsDefinition } from '@app/api/admin/logs'; +import type { PasswordChangeDefinition } from '@app/api/auth/passwordChange'; +import type { PasswordResetDefinition } from '@app/api/auth/passwordReset'; +import type { PasswordResetRequestDefinition } from '@app/api/auth/passwordResetRequest'; +import type { PasswordResetVerifyDefinition } from '@app/api/auth/passwordResetVerify'; import type { RefundRequestDefinition } from '@app/api/event/refundRequest'; +import type { RegisterActivateDefinition } from '@app/api/auth/registerActivate'; +import type { RegisterDefinition } from '@app/api/auth/register'; import type { RegisterPasskeyDefinition } from '@app/api/auth/passkeys/registerPasskey'; +import type { ResetAccessCodeDefinition } from '@app/api/admin/resetAccessCode'; +import type { ResetPasswordLinkDefinition } from '@app/api/admin/resetPasswordLink'; import type { ScheduleTaskDefinition } from '@app/api/admin/scheduler/scheduleTask'; +import type { ServiceHealthDefinition } from '@app/api/admin/serviceHealth'; import type { SignInImpersonateDefinition } from '@app/api/auth/signInImpersonate'; +import type { SignInPasskeyDefinition } from '@app/api/auth/signInPasskey'; +import type { SignInPasswordDefinition } from '@app/api/auth/signInPassword'; +import type { SignInPasswordUpdateDefinition } from '@app/api/auth/signInPasswordUpdate'; +import type { SignOutDefinition } from '@app/api/auth/signOut'; import type { TrainingDefinition } from '@app/api/admin/training'; +import type { TrainingExtraDefinition } from '@app/api/admin/trainingExtra'; import type { TrainingPreferencesDefinition } from '@app/api/event/trainingPreferences'; +import type { TrainingsDefinition } from '@app/api/event/trainings'; import type { UpdateAccountDefinition } from '@app/api/auth/updateAccount'; +import type { UpdateActivationDefinition } from '@app/api/admin/updateActivation'; import type { UpdateApplicationDefinition } from '@app/api/application/updateApplication'; import type { UpdateAvatarDefinition } from '@app/api/auth/updateAvatar'; import type { UpdateBookingDefinition } from '@app/api/admin/hotel-bookings/updateBooking'; +import type { UpdateEventDefinition } from '@app/api/admin/updateEvent'; +import type { UpdateIntegrationDefinition } from '@app/api/admin/updateIntegration'; +import type { UpdatePermissionsDefinition } from '@app/api/admin/updatePermissions'; +import type { UpdatePublicationDefinition } from '@app/api/admin/updatePublication'; +import type { UpdateRoleDefinition } from '@app/api/admin/updateRole'; import type { UpdateSettingsDefinition } from '@app/api/ai/updateSettings'; +import type { UpdateTeamDefinition } from '@app/api/admin/updateTeam'; +import type { UpdateVolunteerDefinition } from '@app/api/admin/updateVolunteer'; +import type { VertexAiDefinition } from '@app/api/admin/vertexAi'; +import type { VolunteerContactInfoDefinition } from '@app/api/admin/volunteerContactInfo'; +import type { VolunteerListDefinition } from '@app/api/admin/volunteerList'; +import type { VolunteerRolesDefinition } from '@app/api/admin/volunteerRoles'; import type { VolunteerTeamsDefinition } from '@app/api/admin/volunteerTeams'; import type { ContentEndpoints } from '@app/api/admin/content/[[...id]]/route'; @@ -69,24 +99,54 @@ export type ApiEndpoints = { '/api/admin/content': ContentEndpoints['create'], '/api/admin/create-event': CreateEventDefinition, '/api/admin/exports': ExportsEndpoints['create'], + '/api/admin/hotel': HotelDefinition, '/api/admin/hotel-bookings/:slug': CreateBookingDefinition, + '/api/admin/logs': LogsDefinition, + '/api/admin/reset-access-code': ResetAccessCodeDefinition, + '/api/admin/reset-password-link': ResetPasswordLinkDefinition, '/api/admin/scheduler': ScheduleTaskDefinition, + '/api/admin/service-health': ServiceHealthDefinition, + '/api/admin/training-extra': TrainingExtraDefinition, '/api/admin/training': TrainingDefinition, '/api/admin/trainings': TrainingsEndpoints['create'], + '/api/admin/update-activation': UpdateActivationDefinition, + '/api/admin/update-event': UpdateEventDefinition, + '/api/admin/update-integration': UpdateIntegrationDefinition, + '/api/admin/update-permissions': UpdatePermissionsDefinition, + '/api/admin/update-publication': UpdatePublicationDefinition, + '/api/admin/update-role': UpdateRoleDefinition, + '/api/admin/update-team': UpdateTeamDefinition, + '/api/admin/update-volunteer': UpdateVolunteerDefinition, '/api/admin/vendors': VendorEndpoints['create'], + '/api/admin/vertex-ai': VertexAiDefinition, + '/api/admin/volunteer-contact-info': VolunteerContactInfoDefinition, + '/api/admin/volunteer-list': VolunteerListDefinition, + '/api/admin/volunteer-roles': VolunteerRolesDefinition, '/api/admin/volunteer-teams': VolunteerTeamsDefinition, '/api/ai/generate/:type': GeneratePromptDefinition, + '/api/auth/confirm-identity': ConfirmIdentityDefinition, '/api/auth/passkeys/create-challenge': CreateChallengeDefinition, '/api/auth/passkeys/register': RegisterPasskeyDefinition, + '/api/auth/password-change': PasswordChangeDefinition, + '/api/auth/password-reset-request': PasswordResetRequestDefinition, + '/api/auth/password-reset-verify': PasswordResetVerifyDefinition, + '/api/auth/password-reset': PasswordResetDefinition, + '/api/auth/register-activate': RegisterActivateDefinition, + '/api/auth/register': RegisterDefinition, '/api/auth/sign-in-impersonate': SignInImpersonateDefinition, + '/api/auth/sign-in-passkey': SignInPasskeyDefinition, + '/api/auth/sign-in-password': SignInPasswordDefinition, + '/api/auth/sign-in-password-update': SignInPasswordUpdateDefinition, + '/api/auth/sign-out': SignOutDefinition, '/api/auth/update-account': UpdateAccountDefinition, '/api/auth/update-avatar': UpdateAvatarDefinition, '/api/event/application': ApplicationDefinition, '/api/event/availability-preferences': AvailabilityPreferencesDefinition, '/api/event/hotel-preferences': HotelPreferencesDefinition, - '/api/event/hotels': HotelsDefinition, + '/api/event/hotels': HotelsDefinition, // FIXME: move to GET? '/api/event/refund-request': RefundRequestDefinition, '/api/event/training-preferences': TrainingPreferencesDefinition, + '/api/event/trainings': TrainingsDefinition, // FIXME: move to GET? '/api/exports': ExportsDefinition, '/api/nardo': NardoEndpoints['create'], diff --git a/app/lib/issueServerAction.ts b/app/lib/issueServerAction.ts deleted file mode 100644 index ec79ff12..00000000 --- a/app/lib/issueServerAction.ts +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2023 Peter Beverloo & AnimeCon. All rights reserved. -// Use of this source code is governed by a MIT license that can be found in the LICENSE file. - -type ServerCallDefinition = { request: object; response: object; }; - -/** - * Issues a call to the server at the given `endpoint` with the given `request` information. When - * successful, will return an object conforming to the response type, or otherwise throw an Error - * that should be caught by the user interface. - * - * @param endpoint The endpoint to which the call should be made. - * @param request Request information that should be included in the request. - * @returns Response from the server, unverified but assumed to be correct for now. - */ -export async function issueServerAction( - endpoint: string, request: T['request']) - : Promise -{ - try { - const response = await fetch(endpoint, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(request), - }); - - return await response.json(); - - } catch (cause) { - throw new Error(`The server ran into an issue, please try again later (${cause}).`); - } -} diff --git a/app/registration/AuthenticationFlow.tsx b/app/registration/AuthenticationFlow.tsx index 86ece723..a6d7d817 100644 --- a/app/registration/AuthenticationFlow.tsx +++ b/app/registration/AuthenticationFlow.tsx @@ -23,7 +23,6 @@ import { RegisterDialog, type PartialRegistrationRequest } from './authenticatio import { RegisterCompleteDialog } from './authentication/RegisterCompleteDialog'; import { RegisterConfirmDialog } from './authentication/RegisterConfirmDialog'; import { UsernameDialog } from './authentication/UsernameDialog'; -import { issueServerAction } from '@lib/issueServerAction'; import { validatePassword } from './authentication/PasswordField'; import type { ConfirmIdentityDefinition } from '@app/api/auth/confirmIdentity'; @@ -35,6 +34,7 @@ import type { SignInPasskeyDefinition } from '@app/api/auth/signInPasskey'; import type { SignInPasswordDefinition } from '@app/api/auth/signInPassword'; import type { SignInPasswordUpdateDefinition } from '@app/api/auth/signInPasswordUpdate'; import type { SignOutDefinition } from '@app/api/auth/signOut'; +import { callApi } from '@lib/callApi'; /** * Styles used by the various components that make up the authentication flow. @@ -189,8 +189,7 @@ export function AuthenticationFlow(props: AuthenticationFlowProps) { // Supporting callbacks for the 'username' state: // --------------------------------------------------------------------------------------------- const onSubmitUsername = useCallback(async (username: string) => { - const response = await issueServerAction( - '/api/auth/confirm-identity', { username }); + const response = await callApi('post', '/api/auth/confirm-identity', { username }); setUsername(username); @@ -198,8 +197,10 @@ export function AuthenticationFlow(props: AuthenticationFlowProps) { if (response.authenticationOptions && browserSupportsWebAuthn()) { try { const result = await startAuthentication(response.authenticationOptions); - const verification = await issueServerAction( - '/api/auth/sign-in-passkey', { username, verification: result }); + const verification = await callApi('post', '/api/auth/sign-in-passkey', { + username, + verification: result + }); if (verification.success) { router.refresh(); @@ -225,11 +226,10 @@ export function AuthenticationFlow(props: AuthenticationFlowProps) { const onLostPassword = useCallback(() => setAuthFlowState('lost-password'), [ /* no deps */ ]); const onSubmitPassword = useCallback(async (plaintextPassword: string) => { - const response = await issueServerAction( - '/api/auth/sign-in-password', { - username: username!, - password: await SHA256HashPassword(plaintextPassword), - }); + const response = await callApi('post', '/api/auth/sign-in-password', { + username: username!, + password: await SHA256HashPassword(plaintextPassword), + }); if (!response.success) throw new Error('That is not the password we\'ve got on file. Try again?'); @@ -248,12 +248,11 @@ export function AuthenticationFlow(props: AuthenticationFlowProps) { }, [ onRequestClose, router, username ]); const onSubmitUpdatePassword = useCallback(async (plaintextPassword: string) => { - const response = await issueServerAction( - '/api/auth/sign-in-password-update', { - username: username!, - password: await SHA256HashPassword(plaintextPassword), - passwordResetRequest: passwordUpdateToken!, - }); + const response = await callApi('post', '/api/auth/sign-in-password-update', { + username: username!, + password: await SHA256HashPassword(plaintextPassword), + passwordResetRequest: passwordUpdateToken!, + }); if (!response.success) throw new Error('Your password could not be updated. Try again?'); @@ -267,8 +266,9 @@ export function AuthenticationFlow(props: AuthenticationFlowProps) { // Supporting callbacks for the 'lost-password' and 'lost-password-reset' states: // --------------------------------------------------------------------------------------------- const onRequestPasswordReset = useCallback(async () => { - const response = await issueServerAction( - '/api/auth/password-reset-request', { username: username! }); + const response = await callApi('post', '/api/auth/password-reset-request', { + username: username! + }); return response.success; @@ -277,11 +277,10 @@ export function AuthenticationFlow(props: AuthenticationFlowProps) { const onPasswordReset = useCallback(async (request: string, plaintextPassword: string) => { validatePassword(plaintextPassword, /* throwOnFailure= */ true); - const response = await issueServerAction( - '/api/auth/password-reset', { - password: await SHA256HashPassword(plaintextPassword), - request, - }); + const response = await callApi('post', '/api/auth/password-reset', { + password: await SHA256HashPassword(plaintextPassword), + request, + }); if (!response.success) throw new Error('The server was not able to save the new password. Try again?'); @@ -299,7 +298,7 @@ export function AuthenticationFlow(props: AuthenticationFlowProps) { = useCallback(async (plaintextPassword: string, request: PartialRegistrationRequest) => { validatePassword(plaintextPassword, /* throwOnFailure= */ true); - const response = await issueServerAction('/api/auth/register', { + const response = await callApi('post', '/api/auth/register', { ...request, username: username!, @@ -327,17 +326,16 @@ export function AuthenticationFlow(props: AuthenticationFlowProps) { const onRequestPasswordChange = useCallback( async (currentPlaintextPassword: string, newPlaintextPassword: string) => { validatePassword(newPlaintextPassword, /* throwOnFailure= */ true); - const response = await issueServerAction( - '/api/auth/password-change', { - currentPassword: await SHA256HashPassword(currentPlaintextPassword), - newPassword: await SHA256HashPassword(newPlaintextPassword), - }); + const response = await callApi('post', '/api/auth/password-change', { + currentPassword: await SHA256HashPassword(currentPlaintextPassword), + newPassword: await SHA256HashPassword(newPlaintextPassword), + }); return !!response.success; }, [ /* no dependencies */ ]); const onRequestSignOut = useCallback(async () => { - const response = await issueServerAction('/api/auth/sign-out', { }); + const response = await callApi('post', '/api/auth/sign-out', { /* no params */ }); router.refresh(); if (response.returnUrl) diff --git a/app/registration/authentication/IdentityDialog.tsx b/app/registration/authentication/IdentityDialog.tsx index e8ef71df..c188b84e 100644 --- a/app/registration/authentication/IdentityDialog.tsx +++ b/app/registration/authentication/IdentityDialog.tsx @@ -16,10 +16,9 @@ import LoadingButton from '@mui/lab/LoadingButton'; import PasswordIcon from '@mui/icons-material/Password'; import Typography from '@mui/material/Typography'; -import type { UpdateAvatarDefinition } from '@app/api/auth/updateAvatar'; import type { User } from '@lib/auth/User'; import { Avatar } from '@components/Avatar'; -import { issueServerAction } from '@lib/issueServerAction'; +import { callApi } from '@lib/callApi'; /** * Props accepted by the component. @@ -90,11 +89,9 @@ export function IdentityDialog(props: IdentityDialogProps) { reader.readAsDataURL(avatar); }); - const response = await issueServerAction( - '/api/auth/update-avatar', - { - avatar: base64Avatar as string, - }); + const response = await callApi('post', '/api/auth/update-avatar', { + avatar: base64Avatar as string, + }); if (response.success) router.refresh(); diff --git a/app/registration/authentication/LostPasswordResetDialog.tsx b/app/registration/authentication/LostPasswordResetDialog.tsx index 91e2fe44..ed4ad329 100644 --- a/app/registration/authentication/LostPasswordResetDialog.tsx +++ b/app/registration/authentication/LostPasswordResetDialog.tsx @@ -15,9 +15,8 @@ import DialogTitle from '@mui/material/DialogTitle'; import LoadingButton from '@mui/lab/LoadingButton'; import Skeleton from '@mui/material/Skeleton'; -import type { PasswordResetVerifyDefinition } from '@app/api/auth/passwordResetVerify'; import { PasswordField } from './PasswordField'; -import { issueServerAction } from '@lib/issueServerAction'; +import { callApi } from '@lib/callApi'; /** * Props accepted by the component. @@ -53,7 +52,7 @@ export function LostPasswordResetDialog(props: LostPasswordResetDialogProps) { // Verify the password reset request through the authentication endpoint, which will also tell // us about the user's first name to display in the dialog. useEffect(() => { - issueServerAction('/api/auth/password-reset-verify', { + callApi('post', '/api/auth/password-reset-verify', { request: passwordResetRequest, }).then(response => { setTimeout(() => { diff --git a/app/registration/authentication/RegisterCompleteDialog.tsx b/app/registration/authentication/RegisterCompleteDialog.tsx index 1de027f7..fac07bf8 100644 --- a/app/registration/authentication/RegisterCompleteDialog.tsx +++ b/app/registration/authentication/RegisterCompleteDialog.tsx @@ -12,8 +12,8 @@ import DialogContentText from '@mui/material/DialogContentText'; import DialogTitle from '@mui/material/DialogTitle'; import Skeleton from '@mui/material/Skeleton'; -import type { RegisterActivateDefinition } from '@app/api/auth/registerActivate'; -import { issueServerAction } from '@lib/issueServerAction'; +import { callApi } from '@lib/callApi'; + /** * Props accepted by the component. @@ -53,7 +53,7 @@ export function RegisterCompleteDialog(props: RegisterCompleteDialogProps) { // Verify the registration request through the authentication endpoint, which will also tell // us about the user's first name to display in the dialog and signs the user in. useEffect(() => { - issueServerAction('/api/auth/register-activate', { + callApi('post', '/api/auth/register-activate', { registrationRequest, }).then(response => { setTimeout(() => {