diff --git a/src/client/src/app/+state/events/actions/commit-event.action.ts b/src/client/src/app/+state/events/actions/commit-event.action.ts deleted file mode 100644 index c500234..0000000 --- a/src/client/src/app/+state/events/actions/commit-event.action.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { inject } from '@angular/core'; -import { on } from '@ngrx/store'; -import { produce } from 'immer'; -import { switchMap } from 'rxjs'; - -import { EventAdministrationService } from '../../../api/services'; -import { createHttpAction, handleHttpAction, onHttpAction, toHttpAction } from '../../action-state'; -import { createFunctionalEffect } from '../../functional-effect'; -import { Effects, Reducers } from '../../utils'; -import { EVENTS_ACTION_SCOPE } from '../consts'; -import { selectEventsActionState } from '../events.selectors'; -import { EventsFeatureState, eventEntityAdapter } from '../events.state'; - -export const commitEventAction = createHttpAction<{ eventId: string; commit: boolean }>()( - EVENTS_ACTION_SCOPE, - 'Commit Event' -); - -export const commitEventReducers: Reducers = [ - on(commitEventAction.success, (state, { props }) => - eventEntityAdapter.mapOne( - { - id: props.eventId, - map: produce(draft => { - draft.staged = !props.commit; - }), - }, - state - ) - ), - handleHttpAction('commit', commitEventAction), -]; - -export const commitEventEffects: Effects = { - commitEvent$: createFunctionalEffect.dispatching((api = inject(EventAdministrationService)) => - onHttpAction(commitEventAction, selectEventsActionState('commit')).pipe( - switchMap(({ props }) => toHttpAction(commitEvent(api, props), commitEventAction, props)) - ) - ), -}; - -async function commitEvent( - api: EventAdministrationService, - props: ReturnType['props'] -) { - const response = await api.updateEvent({ - eventId: props.eventId, - body: { commit: true }, - }); - return response.ok - ? commitEventAction.success(props, undefined) - : commitEventAction.error(props, response); -} diff --git a/src/client/src/app/+state/events/actions/update-event.action.ts b/src/client/src/app/+state/events/actions/update-event.action.ts new file mode 100644 index 0000000..5042ade --- /dev/null +++ b/src/client/src/app/+state/events/actions/update-event.action.ts @@ -0,0 +1,58 @@ +import { inject } from '@angular/core'; +import { on } from '@ngrx/store'; +import { produce } from 'immer'; +import { switchMap } from 'rxjs'; + +import { EventAdministrationService } from '../../../api/services'; +import { createHttpAction, handleHttpAction, onHttpAction, toHttpAction } from '../../action-state'; +import { createFunctionalEffect } from '../../functional-effect'; +import { Effects, Reducers } from '../../utils'; +import { EVENTS_ACTION_SCOPE } from '../consts'; +import { selectEventsActionState } from '../events.selectors'; +import { EventsFeatureState, eventEntityAdapter } from '../events.state'; + +export const updateEventAction = createHttpAction<{ + eventId: string; + commit?: boolean; + externalUri?: string; +}>()(EVENTS_ACTION_SCOPE, 'Update Event'); + +export const updateEventReducers: Reducers = [ + on(updateEventAction.success, (state, { props }) => + eventEntityAdapter.mapOne( + { + id: props.eventId, + map: produce(draft => { + draft.staged = props.commit ? props.commit : undefined; + draft.externalUri = props.externalUri != undefined ? props.externalUri : undefined; + }), + }, + state + ) + ), + handleHttpAction('update', updateEventAction), +]; + +export const updateEventEffects: Effects = { + updateEvent$: createFunctionalEffect.dispatching((api = inject(EventAdministrationService)) => + onHttpAction(updateEventAction, selectEventsActionState('update')).pipe( + switchMap(({ props }) => toHttpAction(updateEvent(api, props), updateEventAction, props)) + ) + ), +}; + +async function updateEvent( + api: EventAdministrationService, + props: ReturnType['props'] +) { + const response = await api.updateEvent({ + eventId: props.eventId, + body: { + commit: props.commit ? props.commit : undefined, + externalUri: props.externalUri ?? undefined, + }, + }); + return response.ok + ? updateEventAction.success(props, undefined) + : updateEventAction.error(props, response); +} diff --git a/src/client/src/app/+state/events/events.actions.ts b/src/client/src/app/+state/events/events.actions.ts index 449e847..d4424e1 100644 --- a/src/client/src/app/+state/events/events.actions.ts +++ b/src/client/src/app/+state/events/events.actions.ts @@ -1,7 +1,7 @@ export { addEventPreconfigAction } from './actions/add-event-preconfig.action'; export { addEventTimeslotAction } from './actions/add-event-timeslot.action'; export { addEventAction } from './actions/add-event.action'; -export { commitEventAction } from './actions/commit-event.action'; +export { updateEventAction } from './actions/update-event.action'; export { eventTimeslotRegistrationChangedAction } from './actions/event-timeslot-registration-changed.action'; export { addPlayerToEventPreconfigurationAction } from './actions/add-player-to-preconfig.action'; export { buildEventInstancesAction } from './actions/build-event-instances.action'; diff --git a/src/client/src/app/+state/events/events.effects.ts b/src/client/src/app/+state/events/events.effects.ts index 0e4502b..b7e068c 100644 --- a/src/client/src/app/+state/events/events.effects.ts +++ b/src/client/src/app/+state/events/events.effects.ts @@ -6,7 +6,6 @@ import { addEventTimeslotEffects } from './actions/add-event-timeslot.action'; import { addEventEffects } from './actions/add-event.action'; import { addPlayerToEventPreconfigurationEffects } from './actions/add-player-to-preconfig.action'; import { buildEventInstancesEffects } from './actions/build-event-instances.action'; -import { commitEventEffects } from './actions/commit-event.action'; import { loadEventAction, loadEventEffects } from './actions/load-event.action'; import { loadEventsEffects } from './actions/load-events.action'; import { removeEventPreconfigEffects } from './actions/remove-event-preconfig.action'; @@ -15,6 +14,7 @@ import { removeEventAction, removeEventEffects } from './actions/remove-event.ac import { removePlayerFromPreconfigEffects } from './actions/remove-player-from-preconfig.action'; import { startEventEffects } from './actions/start-event.action'; import { updateEventTimeslotEffects } from './actions/update-event-timeslot.action'; +import { updateEventEffects } from './actions/update-event.action'; import { eventTimeslotRegistrationChangedAction, resetEventsActionStateAction, @@ -36,7 +36,7 @@ export const eventsFeatureEffects: Effects[] = [ removeEventEffects, removePlayerFromPreconfigEffects, startEventEffects, - commitEventEffects, + updateEventEffects, updateEventTimeslotEffects, { eventUpdated$: createFunctionalEffect.dispatching((events = inject(RealtimeEventsService)) => diff --git a/src/client/src/app/+state/events/events.reducer.ts b/src/client/src/app/+state/events/events.reducer.ts index fc116ed..2e679f1 100644 --- a/src/client/src/app/+state/events/events.reducer.ts +++ b/src/client/src/app/+state/events/events.reducer.ts @@ -5,7 +5,6 @@ import { addEventTimeslotReducers } from './actions/add-event-timeslot.action'; import { addEventReducers } from './actions/add-event.action'; import { addPlayerToEventPreconfigurationReducers } from './actions/add-player-to-preconfig.action'; import { buildEventInstancesReducers } from './actions/build-event-instances.action'; -import { commitEventReducers } from './actions/commit-event.action'; import { eventTimeslotRegistrationChangedReducers } from './actions/event-timeslot-registration-changed.action'; import { loadEventReducers } from './actions/load-event.action'; import { loadEventsReducers } from './actions/load-events.action'; @@ -16,6 +15,7 @@ import { removePlayerFromPreconfigReducers } from './actions/remove-player-from- import { resetEventsActionStateReducers } from './actions/reset-events-action-state.action'; import { startEventReducers } from './actions/start-event.action'; import { updateEventTimeslotReducers } from './actions/update-event-timeslot.action'; +import { updateEventReducers } from './actions/update-event.action'; import { EventsFeatureState, initialEventsFeatureState } from './events.state'; export const eventsReducer = createReducer( @@ -26,7 +26,7 @@ export const eventsReducer = createReducer( ...addEventReducers, ...addPlayerToEventPreconfigurationReducers, ...buildEventInstancesReducers, - ...commitEventReducers, + ...updateEventReducers, ...eventTimeslotRegistrationChangedReducers, ...loadEventReducers, ...loadEventsReducers, diff --git a/src/client/src/app/+state/events/events.state.ts b/src/client/src/app/+state/events/events.state.ts index 860d5bc..b36c097 100644 --- a/src/client/src/app/+state/events/events.state.ts +++ b/src/client/src/app/+state/events/events.state.ts @@ -11,7 +11,7 @@ export type EventsFeatureState = EntityState & { add: ActionState; remove: ActionState; start: ActionState; - commit: ActionState; + update: ActionState; addTimeslot: ActionState; removeTimeslot: ActionState; buildInstances: ActionState; @@ -36,7 +36,7 @@ export const initialEventsFeatureState: EventsFeatureState = eventEntityAdapter. add: initialActionState, remove: initialActionState, start: initialActionState, - commit: initialActionState, + update: initialActionState, addTimeslot: initialActionState, removeTimeslot: initialActionState, buildInstances: initialActionState, diff --git a/src/client/src/app/components/events/event-details/event-details.component.html b/src/client/src/app/components/events/event-details/event-details.component.html index 87a3c4d..84160be 100644 --- a/src/client/src/app/components/events/event-details/event-details.component.html +++ b/src/client/src/app/components/events/event-details/event-details.component.html @@ -71,6 +71,37 @@ +
+

{{ translations.events_facebookLink() }}

+
+ + + + +
+
+

{{ translations.events_timeslots() }}

} + } @else {
diff --git a/src/client/src/app/components/events/event-details/event-details.component.ts b/src/client/src/app/components/events/event-details/event-details.component.ts index c33369d..c595adb 100644 --- a/src/client/src/app/components/events/event-details/event-details.component.ts +++ b/src/client/src/app/components/events/event-details/event-details.component.ts @@ -1,6 +1,7 @@ import { CommonModule, formatDate } from '@angular/common'; import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core'; import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop'; +import { FormsModule } from '@angular/forms'; import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { InterpolatePipe, interpolate } from '@ngneers/signal-translate'; import { Actions, ofType } from '@ngrx/effects'; @@ -9,11 +10,15 @@ import { AccordionModule } from 'primeng/accordion'; import { ConfirmationService } from 'primeng/api'; import { ButtonModule } from 'primeng/button'; import { CardModule } from 'primeng/card'; +import { InputGroupModule } from 'primeng/inputgroup'; +import { InputGroupAddonModule } from 'primeng/inputgroupaddon'; +import { InputTextModule } from 'primeng/inputtext'; import { MessagesModule } from 'primeng/messages'; import { ProgressSpinnerModule } from 'primeng/progressspinner'; import { TooltipModule } from 'primeng/tooltip'; import { filter, map, timer } from 'rxjs'; +import { ModifyExternalUriDialogComponent } from './modify-external-uri-dialog/modify-external-uri-dialog.component'; import { hasActionFailed, isActionBusy } from '../../../+state/action-state'; import { buildEventInstancesAction, @@ -21,7 +26,7 @@ import { selectEvent, selectEventsActionState, startEventAction, - commitEventAction, + updateEventAction, } from '../../../+state/events'; import { keepEventLoaded } from '../../../+state/events/events.utils'; import { mapSelectors } from '../../../+state/maps'; @@ -44,8 +49,13 @@ import { EventTimeslotDialogComponent } from '../event-timeslot-dialog/event-tim CommonModule, EventFormComponent, EventTimeslotDialogComponent, + FormsModule, + InputGroupAddonModule, + InputGroupModule, + InputTextModule, InterpolatePipe, MessagesModule, + ModifyExternalUriDialogComponent, ProgressSpinnerModule, RouterLink, TooltipModule, @@ -77,6 +87,8 @@ export class EventDetailsComponent { protected readonly isStartBusy = computed(() => isActionBusy(this.startActionState())); protected readonly isBuildBusy = computed(() => isActionBusy(this.buildActionState())); protected readonly event = selectSignal(computed(() => selectEvent(this.eventId()))); + protected readonly externalUri = computed(() => this.event()?.externalUri); + // protected readonly externalUriLoaded = computed(() => ) protected readonly timeslots = computed(() => [...(this.event()?.timeslots ?? [])].sort((a, b) => compareTimes(a.time, b.time)) ); @@ -198,7 +210,7 @@ export class EventDetailsComponent { rejectButtonStyleClass: 'p-button-text', accept: () => { this._store.dispatch( - commitEventAction({ + updateEventAction({ eventId: event.id, commit: true, }) @@ -207,6 +219,26 @@ export class EventDetailsComponent { }); } + protected updateExternalUri() { + const event = this.event(); + if (!event) return; + + this._store.dispatch( + updateEventAction({ + eventId: event.id, + externalUri: + this.externalUri() ?? undefined, + }) + ); + } + + protected openExternalUri() { + const url = this.externalUri(); + if (url) { + window.location.href = url; + } + } + protected buildInstances() { this._store.dispatch(buildEventInstancesAction({ eventId: this.eventId() })); } diff --git a/src/client/src/app/components/events/event-details/modify-external-uri-dialog/modify-external-uri-dialog.component.html b/src/client/src/app/components/events/event-details/modify-external-uri-dialog/modify-external-uri-dialog.component.html new file mode 100644 index 0000000..b5180e0 --- /dev/null +++ b/src/client/src/app/components/events/event-details/modify-external-uri-dialog/modify-external-uri-dialog.component.html @@ -0,0 +1,24 @@ + + + + + + + + + + + diff --git a/src/client/src/app/components/events/event-details/modify-external-uri-dialog/modify-external-uri-dialog.component.ts b/src/client/src/app/components/events/event-details/modify-external-uri-dialog/modify-external-uri-dialog.component.ts new file mode 100644 index 0000000..a14f559 --- /dev/null +++ b/src/client/src/app/components/events/event-details/modify-external-uri-dialog/modify-external-uri-dialog.component.ts @@ -0,0 +1,70 @@ +import { + ChangeDetectionStrategy, + Component, + inject, + input, + signal, + ElementRef, + viewChild, +} from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { ButtonModule } from 'primeng/button'; +import { DialogModule } from 'primeng/dialog'; +import { InputGroupModule } from 'primeng/inputgroup'; +import { InputMaskModule } from 'primeng/inputmask'; +import { InputTextModule } from 'primeng/inputtext'; + +import { updateEventAction } from '../../../../+state/events'; +import { Event } from '../../../../models/parsed-models'; +import { TranslateService } from '../../../../services/translate.service'; + +@Component({ + selector: 'app-modify-external-uri-dialog', + standalone: true, + imports: [ + ButtonModule, + DialogModule, + FormsModule, + InputGroupModule, + InputMaskModule, + InputTextModule, + ], + templateUrl: './modify-external-uri-dialog.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ModifyExternalUriDialogComponent { + private readonly _store = inject(Store); + private readonly _translateService = inject(TranslateService); + + protected readonly translations = this._translateService.translations; + public readonly event = input.required(); + protected readonly visible = signal(false); + protected readonly externalUri = signal(''); + + private readonly _inputElement = viewChild.required('inputElement', { read: ElementRef }); + + public open(currentValue: string | null | undefined) { + this.visible.set(true); + this.externalUri.set(currentValue != null ? (currentValue as string) : ''); + } + + protected updateExternalUri() { + const event = this.event(); + if (!event) return; + + this._store.dispatch( + updateEventAction({ + eventId: event.id, + externalUri: this.externalUri(), + }) + ); + + this.visible.set(false); + } + + protected clearExternalUri() { + this.externalUri.set(''); + this._inputElement().nativeElement.focus(); + } +} diff --git a/src/client/src/app/components/player-events/player-event-details/player-event-details.component.html b/src/client/src/app/components/player-events/player-event-details/player-event-details.component.html index 11dab2f..d01d883 100644 --- a/src/client/src/app/components/player-events/player-event-details/player-event-details.component.html +++ b/src/client/src/app/components/player-events/player-event-details/player-event-details.component.html @@ -36,6 +36,16 @@
+ @if (externalUri()) { +
+ +
+ } + @if (!canRegister() && !event.isStarted) { diff --git a/src/client/src/app/components/player-events/player-event-details/player-event-details.component.ts b/src/client/src/app/components/player-events/player-event-details/player-event-details.component.ts index 77ee2fb..e32f529 100644 --- a/src/client/src/app/components/player-events/player-event-details/player-event-details.component.ts +++ b/src/client/src/app/components/player-events/player-event-details/player-event-details.component.ts @@ -92,6 +92,7 @@ export class PlayerEventDetailsComponent { isActionBusy(this.registerActionState()) ); protected readonly event = selectSignal(computed(() => selectPlayerEvent(this.eventId()))); + protected readonly externalUri = computed(() => this.event()?.externalUri); protected readonly timeslots = computed(() => [...(this.event()?.timeslots ?? [])].sort((a, b) => compareTimes(a.time, b.time)) ); @@ -207,4 +208,11 @@ export class PlayerEventDetailsComponent { } return true; } + + protected openExternalUri() { + const url = this.externalUri(); + if (url) { + window.location.href = url; + } + } } diff --git a/src/client/src/app/i18n/de.json b/src/client/src/app/i18n/de.json index 2fd014f..943f5db 100644 --- a/src/client/src/app/i18n/de.json +++ b/src/client/src/app/i18n/de.json @@ -83,6 +83,8 @@ "rebuildGroups": "Gruppen neu bilden", "commit": "Event freigeben", "staged": "vorbereitet", + "facebookLink": "Facebook link", + "visitFacebookLink": "Öffne Facebook Event", "timeslot": { "notFound": "Dieser Zeitslot existiert nicht.", "preconfiguredGroups": "Vordefinierte Gruppen", diff --git a/src/client/src/app/i18n/en.json b/src/client/src/app/i18n/en.json index 5ff6c52..52ec1ef 100644 --- a/src/client/src/app/i18n/en.json +++ b/src/client/src/app/i18n/en.json @@ -83,6 +83,8 @@ "rebuildGroups": "Rebuild groups", "commit": "commit event", "staged": "staged", + "facebookLink": "Facebook link", + "visitFacebookLink": "View Facebook Event", "timeslot": { "notFound": "This timeslot does not exist.", "preconfiguredGroups": "Preconfigured groups", diff --git a/src/client/src/app/models/parsed-models.ts b/src/client/src/app/models/parsed-models.ts index 551b4eb..0492147 100644 --- a/src/client/src/app/models/parsed-models.ts +++ b/src/client/src/app/models/parsed-models.ts @@ -64,7 +64,7 @@ export function parseEvent(event: ApiEvent): Event { registrationDeadline: new Date(event.registrationDeadline), timeslots: event.timeslots.map(parseEventTimeslot), startedAt: event.startedAt ? new Date(event.startedAt) : undefined, - staged: event.staged, + staged: event.staged != null ? event.staged : undefined, }; } diff --git a/src/server/data/Entities/EventEntity.cs b/src/server/data/Entities/EventEntity.cs index 4202b1c..dbbf6dc 100644 --- a/src/server/data/Entities/EventEntity.cs +++ b/src/server/data/Entities/EventEntity.cs @@ -1,5 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; +using MinigolfFriday.Domain.Models; namespace MinigolfFriday.Data.Entities; @@ -13,6 +14,8 @@ public class EventEntity public List Timeslots { get; set; } = []; + public string? ExternalUri { get; set; } = null; + public static void Configure(EntityTypeBuilder builder) { builder.ToTable("events"); @@ -27,5 +30,6 @@ public static void Configure(EntityTypeBuilder builder) builder.HasKey(x => x.Id); builder.Property(x => x.Staged).HasColumnName("staged").IsRequired(); + builder.Property(x => x.ExternalUri).HasColumnName("external_uri"); } } diff --git a/src/server/domain/Models/Event.cs b/src/server/domain/Models/Event.cs index 8b70eb0..ec175ea 100644 --- a/src/server/domain/Models/Event.cs +++ b/src/server/domain/Models/Event.cs @@ -16,7 +16,8 @@ public record Event( [property: Required] DateTimeOffset RegistrationDeadline, [property: Required] EventTimeslot[] Timeslots, DateTimeOffset? StartedAt, - [property: Required] bool Staged + bool? Staged, + string? ExternalUri ); /// diff --git a/src/server/domain/Models/PlayerEvent.cs b/src/server/domain/Models/PlayerEvent.cs index 8adbc7f..e1b8f94 100644 --- a/src/server/domain/Models/PlayerEvent.cs +++ b/src/server/domain/Models/PlayerEvent.cs @@ -15,7 +15,8 @@ public record PlayerEvent( [property: Required] DateOnly Date, [property: Required] DateTimeOffset RegistrationDeadline, [property: Required] PlayerEventTimeslot[] Timeslots, - [property: Required] bool IsStarted + [property: Required] bool IsStarted, + string? ExternalUri ); /// diff --git a/src/server/host/Endpoints/Administration/Events/StartEventEndpoint.cs b/src/server/host/Endpoints/Administration/Events/StartEventEndpoint.cs index 2cdd6f5..2912913 100644 --- a/src/server/host/Endpoints/Administration/Events/StartEventEndpoint.cs +++ b/src/server/host/Endpoints/Administration/Events/StartEventEndpoint.cs @@ -31,7 +31,6 @@ public class StartEventEndpoint( DatabaseContext databaseContext, IRealtimeEventsService realtimeEventsService, IIdService idService, - IUserPushSubscriptionMapper userPushSubscriptionMapper, IWebPushService webPushService ) : Endpoint { diff --git a/src/server/host/Endpoints/Administration/Events/UpdateEventEndpoint.cs b/src/server/host/Endpoints/Administration/Events/UpdateEventEndpoint.cs index ff9e9be..b33a8ed 100644 --- a/src/server/host/Endpoints/Administration/Events/UpdateEventEndpoint.cs +++ b/src/server/host/Endpoints/Administration/Events/UpdateEventEndpoint.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using FastEndpoints; using FluentValidation; +using MaSch.Core.Extensions; using Microsoft.EntityFrameworkCore; using MinigolfFriday.Data; using MinigolfFriday.Data.Entities; @@ -17,7 +18,8 @@ namespace MinigolfFriday.Host.Endpoints.Administration.Events; // / set true if you want to commit the event and send a notification to the user public record UpdateEventRequest( [property: Required] string EventId, - [property: Required] bool Commit + bool? Commit, + string? ExternalUri ); public class UpdateEventRequestValidator : Validator @@ -57,7 +59,7 @@ public override async Task HandleAsync(UpdateEventRequest req, CancellationToken return; } - if (!eventInfo.Staged) + if (req.Commit == false && !eventInfo.Staged) { Logger.LogWarning(EndpointErrors.EventNotStaged, eventId); await this.SendErrorAsync( @@ -70,7 +72,15 @@ await this.SendErrorAsync( var updateBuilder = DbUpdateBuilder.Create(eventQuery); - updateBuilder.With(x => x.SetProperty(x => x.Staged, false)); + if (req.Commit == true) + { + updateBuilder.With(x => x.SetProperty(x => x.Staged, false)); + } + + if (req.ExternalUri != null) + { + updateBuilder.With(x => x.SetProperty(x => x.ExternalUri, req.ExternalUri)); + } ThrowIfAnyErrors(); @@ -92,20 +102,23 @@ await realtimeEventsService.SendEventAsync( ct ); - var pushSubscriptions = await databaseContext - .UserPushSubscriptions.Where(x => - x.User.Settings == null - || (x.User.Settings.EnableNotifications && x.User.Settings.NotifyOnEventPublish) - ) - .Select(userPushSubscriptionMapper.MapUserPushSubscriptionExpression) - .ToListAsync(ct); - await webPushService.SendAsync( - pushSubscriptions, - new PushNotificationData.EventPublished( - idService.Event.Encode(eventId), - eventInfo.Date - ), - ct - ); + if (req.Commit == true) + { + var pushSubscriptions = await databaseContext + .UserPushSubscriptions.Where(x => + x.User.Settings == null + || (x.User.Settings.EnableNotifications && x.User.Settings.NotifyOnEventPublish) + ) + .Select(userPushSubscriptionMapper.MapUserPushSubscriptionExpression) + .ToListAsync(ct); + await webPushService.SendAsync( + pushSubscriptions, + new PushNotificationData.EventPublished( + idService.Event.Encode(eventId), + eventInfo.Date + ), + ct + ); + } } } diff --git a/src/server/host/Mappers/EventMapper.cs b/src/server/host/Mappers/EventMapper.cs index c8d4f99..c81f8eb 100644 --- a/src/server/host/Mappers/EventMapper.cs +++ b/src/server/host/Mappers/EventMapper.cs @@ -16,7 +16,8 @@ public Event Map(EventEntity entity) entity.RegistrationDeadline, entity.Timeslots?.Select(Map).ToArray() ?? [], entity.StartedAt, - entity.Staged + entity.Staged, + entity.ExternalUri ); } diff --git a/src/server/host/Mappers/PlayerEventMapper.cs b/src/server/host/Mappers/PlayerEventMapper.cs index 1d65368..afee385 100644 --- a/src/server/host/Mappers/PlayerEventMapper.cs +++ b/src/server/host/Mappers/PlayerEventMapper.cs @@ -18,7 +18,8 @@ public PlayerEvent Map(EventEntity entity, long userId) .Timeslots.OrderBy(x => x.Time) .Select(timeslot => Map(timeslot, userId)) .ToArray(), - entity.StartedAt != null + entity.StartedAt != null, + entity.ExternalUri ); } diff --git a/src/server/migrations/mssql/Migrations/20240709194535_AddEventExternalUri.Designer.cs b/src/server/migrations/mssql/Migrations/20240709194535_AddEventExternalUri.Designer.cs new file mode 100644 index 0000000..b8802ae --- /dev/null +++ b/src/server/migrations/mssql/Migrations/20240709194535_AddEventExternalUri.Designer.cs @@ -0,0 +1,616 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using MinigolfFriday.Data; + +#nullable disable + +namespace MinigolfFriday.Migrations.MsSql.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20240709194535_AddEventExternalUri")] + partial class AddEventExternalUri + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Date") + .HasColumnType("date") + .HasColumnName("date"); + + b.Property("ExternalUri") + .HasColumnType("nvarchar(max)") + .HasColumnName("external_uri"); + + b.Property("RegistrationDeadline") + .HasColumnType("datetimeoffset") + .HasColumnName("registration_deadline"); + + b.Property("Staged") + .HasColumnType("bit") + .HasColumnName("staged"); + + b.Property("StartedAt") + .HasColumnType("datetimeoffset") + .HasColumnName("started_at"); + + b.HasKey("Id"); + + b.ToTable("events", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventInstanceEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("GroupCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)") + .HasColumnName("group_code"); + + b.Property("timeslot_id") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("timeslot_id"); + + b.ToTable("event_instances", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventInstancePreconfigurationEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("event_timeslot_id") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("event_timeslot_id"); + + b.ToTable("event_instance_preconfigurations", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventTimeslotEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("EventId") + .HasColumnType("bigint") + .HasColumnName("event_id"); + + b.Property("IsFallbackAllowed") + .HasColumnType("bit") + .HasColumnName("is_fallback_allowed"); + + b.Property("MapId") + .HasColumnType("bigint") + .HasColumnName("map_id"); + + b.Property("Time") + .HasColumnType("time") + .HasColumnName("time"); + + b.HasKey("Id"); + + b.HasIndex("MapId"); + + b.HasIndex("EventId", "Time") + .IsUnique(); + + b.ToTable("event_timeslots", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventTimeslotRegistrationEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("EventTimeslotId") + .HasColumnType("bigint") + .HasColumnName("event_timeslot_id"); + + b.Property("FallbackEventTimeslotId") + .HasColumnType("bigint") + .HasColumnName("fallback_event_timeslot_id"); + + b.Property("PlayerId") + .HasColumnType("bigint") + .HasColumnName("user_id"); + + b.HasKey("Id"); + + b.HasIndex("EventTimeslotId"); + + b.HasIndex("FallbackEventTimeslotId"); + + b.HasIndex("PlayerId"); + + b.ToTable("event_timeslot_registration", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.MinigolfMapEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("active"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("nvarchar(150)") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.ToTable("maps", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.RoleEntity", b => + { + b.Property("Id") + .HasColumnType("int") + .HasColumnName("id"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.ToTable("roles", (string)null); + + b.HasData( + new + { + Id = 0, + Name = "Player" + }, + new + { + Id = 1, + Name = "Admin" + }, + new + { + Id = 2, + Name = "Developer" + }); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.UserEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Alias") + .HasMaxLength(150) + .HasColumnType("nvarchar(150)") + .HasColumnName("alias"); + + b.Property("LoginToken") + .HasMaxLength(32) + .HasColumnType("nvarchar(32)") + .HasColumnName("login_token"); + + b.Property("SettingsId") + .HasColumnType("bigint") + .HasColumnName("settings_id"); + + b.HasKey("Id"); + + b.HasIndex("LoginToken") + .IsUnique() + .HasFilter("[login_token] IS NOT NULL"); + + b.HasIndex("SettingsId"); + + b.ToTable("users", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.UserPushSubscriptionEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Auth") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("auth"); + + b.Property("Endpoint") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)") + .HasColumnName("endpoint"); + + b.Property("Lang") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)") + .HasColumnName("lang"); + + b.Property("P256DH") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("p256dh"); + + b.Property("UserId") + .HasColumnType("bigint") + .HasColumnName("user_id"); + + b.HasKey("Id"); + + b.HasIndex("Endpoint") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("user_push_subscriptions", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.UserSettingsEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("EnableNotifications") + .HasColumnType("bit") + .HasColumnName("enable_notifications"); + + b.Property("NotifyOnEventPublish") + .HasColumnType("bit") + .HasColumnName("notify_on_event_publish"); + + b.Property("NotifyOnEventStart") + .HasColumnType("bit") + .HasColumnName("notify_on_event_start"); + + b.Property("NotifyOnEventUpdated") + .HasColumnType("bit") + .HasColumnName("notify_on_event_updated"); + + b.Property("NotifyOnTimeslotStart") + .HasColumnType("bit") + .HasColumnName("notify_on_timeslot_start"); + + b.Property("SecondsToNotifyBeforeTimeslotStart") + .HasColumnType("int") + .HasColumnName("seconds_to_notify_before_timeslot_start"); + + b.HasKey("Id"); + + b.ToTable("user_settings", (string)null); + }); + + modelBuilder.Entity("event_instances_to_users", b => + { + b.Property("event_instance_id") + .HasColumnType("bigint"); + + b.Property("user_id") + .HasColumnType("bigint"); + + b.HasKey("event_instance_id", "user_id"); + + b.HasIndex("user_id"); + + b.ToTable("event_instances_to_users"); + }); + + modelBuilder.Entity("users_to_avoided_users", b => + { + b.Property("avoided_user_id") + .HasColumnType("bigint"); + + b.Property("user_id") + .HasColumnType("bigint"); + + b.HasKey("avoided_user_id", "user_id"); + + b.HasIndex("user_id"); + + b.ToTable("users_to_avoided_users"); + }); + + modelBuilder.Entity("users_to_event_instance_preconfigurations", b => + { + b.Property("event_instance_preconfiguration_id") + .HasColumnType("bigint"); + + b.Property("user_id") + .HasColumnType("bigint"); + + b.HasKey("event_instance_preconfiguration_id", "user_id"); + + b.HasIndex("user_id"); + + b.ToTable("users_to_event_instance_preconfigurations"); + }); + + modelBuilder.Entity("users_to_preferred_users", b => + { + b.Property("preferred_user_id") + .HasColumnType("bigint"); + + b.Property("user_id") + .HasColumnType("bigint"); + + b.HasKey("preferred_user_id", "user_id"); + + b.HasIndex("user_id"); + + b.ToTable("users_to_preferred_users"); + }); + + modelBuilder.Entity("users_to_roles", b => + { + b.Property("role_id") + .HasColumnType("int"); + + b.Property("user_id") + .HasColumnType("bigint"); + + b.HasKey("role_id", "user_id"); + + b.HasIndex("user_id"); + + b.ToTable("users_to_roles"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventInstanceEntity", b => + { + b.HasOne("MinigolfFriday.Data.Entities.EventTimeslotEntity", "EventTimeslot") + .WithMany("Instances") + .HasForeignKey("timeslot_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EventTimeslot"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventInstancePreconfigurationEntity", b => + { + b.HasOne("MinigolfFriday.Data.Entities.EventTimeslotEntity", "EventTimeSlot") + .WithMany("Preconfigurations") + .HasForeignKey("event_timeslot_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EventTimeSlot"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventTimeslotEntity", b => + { + b.HasOne("MinigolfFriday.Data.Entities.EventEntity", "Event") + .WithMany("Timeslots") + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MinigolfFriday.Data.Entities.MinigolfMapEntity", "Map") + .WithMany("EventTimeslots") + .HasForeignKey("MapId"); + + b.Navigation("Event"); + + b.Navigation("Map"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventTimeslotRegistrationEntity", b => + { + b.HasOne("MinigolfFriday.Data.Entities.EventTimeslotEntity", "EventTimeslot") + .WithMany("Registrations") + .HasForeignKey("EventTimeslotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MinigolfFriday.Data.Entities.EventTimeslotEntity", "FallbackEventTimeslot") + .WithMany() + .HasForeignKey("FallbackEventTimeslotId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EventTimeslot"); + + b.Navigation("FallbackEventTimeslot"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.UserEntity", b => + { + b.HasOne("MinigolfFriday.Data.Entities.UserSettingsEntity", "Settings") + .WithMany("Users") + .HasForeignKey("SettingsId"); + + b.Navigation("Settings"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.UserPushSubscriptionEntity", b => + { + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", "User") + .WithMany("PushSubscriptions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("event_instances_to_users", b => + { + b.HasOne("MinigolfFriday.Data.Entities.EventInstanceEntity", null) + .WithMany() + .HasForeignKey("event_instance_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", null) + .WithMany() + .HasForeignKey("user_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("users_to_avoided_users", b => + { + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", null) + .WithMany() + .HasForeignKey("avoided_user_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", null) + .WithMany() + .HasForeignKey("user_id") + .OnDelete(DeleteBehavior.ClientCascade) + .IsRequired(); + }); + + modelBuilder.Entity("users_to_event_instance_preconfigurations", b => + { + b.HasOne("MinigolfFriday.Data.Entities.EventInstancePreconfigurationEntity", null) + .WithMany() + .HasForeignKey("event_instance_preconfiguration_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", null) + .WithMany() + .HasForeignKey("user_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("users_to_preferred_users", b => + { + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", null) + .WithMany() + .HasForeignKey("preferred_user_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", null) + .WithMany() + .HasForeignKey("user_id") + .OnDelete(DeleteBehavior.ClientCascade) + .IsRequired(); + }); + + modelBuilder.Entity("users_to_roles", b => + { + b.HasOne("MinigolfFriday.Data.Entities.RoleEntity", null) + .WithMany() + .HasForeignKey("role_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", null) + .WithMany() + .HasForeignKey("user_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventEntity", b => + { + b.Navigation("Timeslots"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventTimeslotEntity", b => + { + b.Navigation("Instances"); + + b.Navigation("Preconfigurations"); + + b.Navigation("Registrations"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.MinigolfMapEntity", b => + { + b.Navigation("EventTimeslots"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.UserEntity", b => + { + b.Navigation("PushSubscriptions"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.UserSettingsEntity", b => + { + b.Navigation("Users"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/server/migrations/mssql/Migrations/20240709194535_AddEventExternalUri.cs b/src/server/migrations/mssql/Migrations/20240709194535_AddEventExternalUri.cs new file mode 100644 index 0000000..fda11eb --- /dev/null +++ b/src/server/migrations/mssql/Migrations/20240709194535_AddEventExternalUri.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MinigolfFriday.Migrations.MsSql.Migrations +{ + /// + public partial class AddEventExternalUri : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "external_uri", + table: "events", + type: "nvarchar(max)", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "external_uri", + table: "events"); + } + } +} diff --git a/src/server/migrations/mssql/Migrations/DatabaseContextModelSnapshot.cs b/src/server/migrations/mssql/Migrations/DatabaseContextModelSnapshot.cs index 228ef5e..c457801 100644 --- a/src/server/migrations/mssql/Migrations/DatabaseContextModelSnapshot.cs +++ b/src/server/migrations/mssql/Migrations/DatabaseContextModelSnapshot.cs @@ -35,6 +35,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("date") .HasColumnName("date"); + b.Property("ExternalUri") + .HasColumnType("nvarchar(max)") + .HasColumnName("external_uri"); + b.Property("RegistrationDeadline") .HasColumnType("datetimeoffset") .HasColumnName("registration_deadline"); diff --git a/src/server/migrations/postgresql/Migrations/20240709194538_AddEventExternalUri.Designer.cs b/src/server/migrations/postgresql/Migrations/20240709194538_AddEventExternalUri.Designer.cs new file mode 100644 index 0000000..340b2b1 --- /dev/null +++ b/src/server/migrations/postgresql/Migrations/20240709194538_AddEventExternalUri.Designer.cs @@ -0,0 +1,615 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using MinigolfFriday.Data; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace MinigolfFriday.Migrations.PostgreSql.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20240709194538_AddEventExternalUri")] + partial class AddEventExternalUri + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Date") + .HasColumnType("date") + .HasColumnName("date"); + + b.Property("ExternalUri") + .HasColumnType("text") + .HasColumnName("external_uri"); + + b.Property("RegistrationDeadline") + .HasColumnType("timestamp with time zone") + .HasColumnName("registration_deadline"); + + b.Property("Staged") + .HasColumnType("boolean") + .HasColumnName("staged"); + + b.Property("StartedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("started_at"); + + b.HasKey("Id"); + + b.ToTable("events", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventInstanceEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("GroupCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasColumnName("group_code"); + + b.Property("timeslot_id") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("timeslot_id"); + + b.ToTable("event_instances", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventInstancePreconfigurationEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("event_timeslot_id") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("event_timeslot_id"); + + b.ToTable("event_instance_preconfigurations", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventTimeslotEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("EventId") + .HasColumnType("bigint") + .HasColumnName("event_id"); + + b.Property("IsFallbackAllowed") + .HasColumnType("boolean") + .HasColumnName("is_fallback_allowed"); + + b.Property("MapId") + .HasColumnType("bigint") + .HasColumnName("map_id"); + + b.Property("Time") + .HasColumnType("time without time zone") + .HasColumnName("time"); + + b.HasKey("Id"); + + b.HasIndex("MapId"); + + b.HasIndex("EventId", "Time") + .IsUnique(); + + b.ToTable("event_timeslots", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventTimeslotRegistrationEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("EventTimeslotId") + .HasColumnType("bigint") + .HasColumnName("event_timeslot_id"); + + b.Property("FallbackEventTimeslotId") + .HasColumnType("bigint") + .HasColumnName("fallback_event_timeslot_id"); + + b.Property("PlayerId") + .HasColumnType("bigint") + .HasColumnName("user_id"); + + b.HasKey("Id"); + + b.HasIndex("EventTimeslotId"); + + b.HasIndex("FallbackEventTimeslotId"); + + b.HasIndex("PlayerId"); + + b.ToTable("event_timeslot_registration", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.MinigolfMapEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasColumnName("active"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("character varying(150)") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.ToTable("maps", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.RoleEntity", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.ToTable("roles", (string)null); + + b.HasData( + new + { + Id = 0, + Name = "Player" + }, + new + { + Id = 1, + Name = "Admin" + }, + new + { + Id = 2, + Name = "Developer" + }); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.UserEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Alias") + .HasMaxLength(150) + .HasColumnType("character varying(150)") + .HasColumnName("alias"); + + b.Property("LoginToken") + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasColumnName("login_token"); + + b.Property("SettingsId") + .HasColumnType("bigint") + .HasColumnName("settings_id"); + + b.HasKey("Id"); + + b.HasIndex("LoginToken") + .IsUnique(); + + b.HasIndex("SettingsId"); + + b.ToTable("users", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.UserPushSubscriptionEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Auth") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("auth"); + + b.Property("Endpoint") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)") + .HasColumnName("endpoint"); + + b.Property("Lang") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)") + .HasColumnName("lang"); + + b.Property("P256DH") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("p256dh"); + + b.Property("UserId") + .HasColumnType("bigint") + .HasColumnName("user_id"); + + b.HasKey("Id"); + + b.HasIndex("Endpoint") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("user_push_subscriptions", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.UserSettingsEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("EnableNotifications") + .HasColumnType("boolean") + .HasColumnName("enable_notifications"); + + b.Property("NotifyOnEventPublish") + .HasColumnType("boolean") + .HasColumnName("notify_on_event_publish"); + + b.Property("NotifyOnEventStart") + .HasColumnType("boolean") + .HasColumnName("notify_on_event_start"); + + b.Property("NotifyOnEventUpdated") + .HasColumnType("boolean") + .HasColumnName("notify_on_event_updated"); + + b.Property("NotifyOnTimeslotStart") + .HasColumnType("boolean") + .HasColumnName("notify_on_timeslot_start"); + + b.Property("SecondsToNotifyBeforeTimeslotStart") + .HasColumnType("integer") + .HasColumnName("seconds_to_notify_before_timeslot_start"); + + b.HasKey("Id"); + + b.ToTable("user_settings", (string)null); + }); + + modelBuilder.Entity("event_instances_to_users", b => + { + b.Property("event_instance_id") + .HasColumnType("bigint"); + + b.Property("user_id") + .HasColumnType("bigint"); + + b.HasKey("event_instance_id", "user_id"); + + b.HasIndex("user_id"); + + b.ToTable("event_instances_to_users"); + }); + + modelBuilder.Entity("users_to_avoided_users", b => + { + b.Property("avoided_user_id") + .HasColumnType("bigint"); + + b.Property("user_id") + .HasColumnType("bigint"); + + b.HasKey("avoided_user_id", "user_id"); + + b.HasIndex("user_id"); + + b.ToTable("users_to_avoided_users"); + }); + + modelBuilder.Entity("users_to_event_instance_preconfigurations", b => + { + b.Property("event_instance_preconfiguration_id") + .HasColumnType("bigint"); + + b.Property("user_id") + .HasColumnType("bigint"); + + b.HasKey("event_instance_preconfiguration_id", "user_id"); + + b.HasIndex("user_id"); + + b.ToTable("users_to_event_instance_preconfigurations"); + }); + + modelBuilder.Entity("users_to_preferred_users", b => + { + b.Property("preferred_user_id") + .HasColumnType("bigint"); + + b.Property("user_id") + .HasColumnType("bigint"); + + b.HasKey("preferred_user_id", "user_id"); + + b.HasIndex("user_id"); + + b.ToTable("users_to_preferred_users"); + }); + + modelBuilder.Entity("users_to_roles", b => + { + b.Property("role_id") + .HasColumnType("integer"); + + b.Property("user_id") + .HasColumnType("bigint"); + + b.HasKey("role_id", "user_id"); + + b.HasIndex("user_id"); + + b.ToTable("users_to_roles"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventInstanceEntity", b => + { + b.HasOne("MinigolfFriday.Data.Entities.EventTimeslotEntity", "EventTimeslot") + .WithMany("Instances") + .HasForeignKey("timeslot_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EventTimeslot"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventInstancePreconfigurationEntity", b => + { + b.HasOne("MinigolfFriday.Data.Entities.EventTimeslotEntity", "EventTimeSlot") + .WithMany("Preconfigurations") + .HasForeignKey("event_timeslot_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EventTimeSlot"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventTimeslotEntity", b => + { + b.HasOne("MinigolfFriday.Data.Entities.EventEntity", "Event") + .WithMany("Timeslots") + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MinigolfFriday.Data.Entities.MinigolfMapEntity", "Map") + .WithMany("EventTimeslots") + .HasForeignKey("MapId"); + + b.Navigation("Event"); + + b.Navigation("Map"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventTimeslotRegistrationEntity", b => + { + b.HasOne("MinigolfFriday.Data.Entities.EventTimeslotEntity", "EventTimeslot") + .WithMany("Registrations") + .HasForeignKey("EventTimeslotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MinigolfFriday.Data.Entities.EventTimeslotEntity", "FallbackEventTimeslot") + .WithMany() + .HasForeignKey("FallbackEventTimeslotId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EventTimeslot"); + + b.Navigation("FallbackEventTimeslot"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.UserEntity", b => + { + b.HasOne("MinigolfFriday.Data.Entities.UserSettingsEntity", "Settings") + .WithMany("Users") + .HasForeignKey("SettingsId"); + + b.Navigation("Settings"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.UserPushSubscriptionEntity", b => + { + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", "User") + .WithMany("PushSubscriptions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("event_instances_to_users", b => + { + b.HasOne("MinigolfFriday.Data.Entities.EventInstanceEntity", null) + .WithMany() + .HasForeignKey("event_instance_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", null) + .WithMany() + .HasForeignKey("user_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("users_to_avoided_users", b => + { + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", null) + .WithMany() + .HasForeignKey("avoided_user_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", null) + .WithMany() + .HasForeignKey("user_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("users_to_event_instance_preconfigurations", b => + { + b.HasOne("MinigolfFriday.Data.Entities.EventInstancePreconfigurationEntity", null) + .WithMany() + .HasForeignKey("event_instance_preconfiguration_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", null) + .WithMany() + .HasForeignKey("user_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("users_to_preferred_users", b => + { + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", null) + .WithMany() + .HasForeignKey("preferred_user_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", null) + .WithMany() + .HasForeignKey("user_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("users_to_roles", b => + { + b.HasOne("MinigolfFriday.Data.Entities.RoleEntity", null) + .WithMany() + .HasForeignKey("role_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", null) + .WithMany() + .HasForeignKey("user_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventEntity", b => + { + b.Navigation("Timeslots"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventTimeslotEntity", b => + { + b.Navigation("Instances"); + + b.Navigation("Preconfigurations"); + + b.Navigation("Registrations"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.MinigolfMapEntity", b => + { + b.Navigation("EventTimeslots"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.UserEntity", b => + { + b.Navigation("PushSubscriptions"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.UserSettingsEntity", b => + { + b.Navigation("Users"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/server/migrations/postgresql/Migrations/20240709194538_AddEventExternalUri.cs b/src/server/migrations/postgresql/Migrations/20240709194538_AddEventExternalUri.cs new file mode 100644 index 0000000..b7307b4 --- /dev/null +++ b/src/server/migrations/postgresql/Migrations/20240709194538_AddEventExternalUri.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MinigolfFriday.Migrations.PostgreSql.Migrations +{ + /// + public partial class AddEventExternalUri : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "external_uri", + table: "events", + type: "text", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "external_uri", + table: "events"); + } + } +} diff --git a/src/server/migrations/postgresql/Migrations/DatabaseContextModelSnapshot.cs b/src/server/migrations/postgresql/Migrations/DatabaseContextModelSnapshot.cs index cb29ae0..521aa94 100644 --- a/src/server/migrations/postgresql/Migrations/DatabaseContextModelSnapshot.cs +++ b/src/server/migrations/postgresql/Migrations/DatabaseContextModelSnapshot.cs @@ -35,6 +35,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("date") .HasColumnName("date"); + b.Property("ExternalUri") + .HasColumnType("text") + .HasColumnName("external_uri"); + b.Property("RegistrationDeadline") .HasColumnType("timestamp with time zone") .HasColumnName("registration_deadline"); diff --git a/src/server/migrations/sqlite/Migrations/20240709194533_AddEventExternalUri.Designer.cs b/src/server/migrations/sqlite/Migrations/20240709194533_AddEventExternalUri.Designer.cs new file mode 100644 index 0000000..2eff244 --- /dev/null +++ b/src/server/migrations/sqlite/Migrations/20240709194533_AddEventExternalUri.Designer.cs @@ -0,0 +1,592 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using MinigolfFriday.Data; + +#nullable disable + +namespace MinigolfFriday.Migrations.Sqlite.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20240709194533_AddEventExternalUri")] + partial class AddEventExternalUri + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("Date") + .HasColumnType("TEXT") + .HasColumnName("date"); + + b.Property("ExternalUri") + .HasColumnType("TEXT") + .HasColumnName("external_uri"); + + b.Property("RegistrationDeadline") + .HasColumnType("TEXT") + .HasColumnName("registration_deadline"); + + b.Property("Staged") + .HasColumnType("INTEGER") + .HasColumnName("staged"); + + b.Property("StartedAt") + .HasColumnType("TEXT") + .HasColumnName("started_at"); + + b.HasKey("Id"); + + b.ToTable("events", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventInstanceEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("GroupCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("TEXT") + .HasColumnName("group_code"); + + b.Property("timeslot_id") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("timeslot_id"); + + b.ToTable("event_instances", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventInstancePreconfigurationEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("event_timeslot_id") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("event_timeslot_id"); + + b.ToTable("event_instance_preconfigurations", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventTimeslotEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("EventId") + .HasColumnType("INTEGER") + .HasColumnName("event_id"); + + b.Property("IsFallbackAllowed") + .HasColumnType("INTEGER") + .HasColumnName("is_fallback_allowed"); + + b.Property("MapId") + .HasColumnType("INTEGER") + .HasColumnName("map_id"); + + b.Property("Time") + .HasColumnType("TEXT") + .HasColumnName("time"); + + b.HasKey("Id"); + + b.HasIndex("MapId"); + + b.HasIndex("EventId", "Time") + .IsUnique(); + + b.ToTable("event_timeslots", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventTimeslotRegistrationEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("EventTimeslotId") + .HasColumnType("INTEGER") + .HasColumnName("event_timeslot_id"); + + b.Property("FallbackEventTimeslotId") + .HasColumnType("INTEGER") + .HasColumnName("fallback_event_timeslot_id"); + + b.Property("PlayerId") + .HasColumnType("INTEGER") + .HasColumnName("user_id"); + + b.HasKey("Id"); + + b.HasIndex("EventTimeslotId"); + + b.HasIndex("FallbackEventTimeslotId"); + + b.HasIndex("PlayerId"); + + b.ToTable("event_timeslot_registration", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.MinigolfMapEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(true) + .HasColumnName("active"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("TEXT") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.ToTable("maps", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.RoleEntity", b => + { + b.Property("Id") + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.ToTable("roles", (string)null); + + b.HasData( + new + { + Id = 0, + Name = "Player" + }, + new + { + Id = 1, + Name = "Admin" + }, + new + { + Id = 2, + Name = "Developer" + }); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.UserEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("Alias") + .HasMaxLength(150) + .HasColumnType("TEXT") + .HasColumnName("alias"); + + b.Property("LoginToken") + .HasMaxLength(32) + .HasColumnType("TEXT") + .HasColumnName("login_token"); + + b.Property("SettingsId") + .HasColumnType("INTEGER") + .HasColumnName("settings_id"); + + b.HasKey("Id"); + + b.HasIndex("LoginToken") + .IsUnique(); + + b.HasIndex("SettingsId"); + + b.ToTable("users", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.UserPushSubscriptionEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("Auth") + .HasMaxLength(255) + .HasColumnType("TEXT") + .HasColumnName("auth"); + + b.Property("Endpoint") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("TEXT") + .HasColumnName("endpoint"); + + b.Property("Lang") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("TEXT") + .HasColumnName("lang"); + + b.Property("P256DH") + .HasMaxLength(255) + .HasColumnType("TEXT") + .HasColumnName("p256dh"); + + b.Property("UserId") + .HasColumnType("INTEGER") + .HasColumnName("user_id"); + + b.HasKey("Id"); + + b.HasIndex("Endpoint") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("user_push_subscriptions", (string)null); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.UserSettingsEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("EnableNotifications") + .HasColumnType("INTEGER") + .HasColumnName("enable_notifications"); + + b.Property("NotifyOnEventPublish") + .HasColumnType("INTEGER") + .HasColumnName("notify_on_event_publish"); + + b.Property("NotifyOnEventStart") + .HasColumnType("INTEGER") + .HasColumnName("notify_on_event_start"); + + b.Property("NotifyOnEventUpdated") + .HasColumnType("INTEGER") + .HasColumnName("notify_on_event_updated"); + + b.Property("NotifyOnTimeslotStart") + .HasColumnType("INTEGER") + .HasColumnName("notify_on_timeslot_start"); + + b.Property("SecondsToNotifyBeforeTimeslotStart") + .HasColumnType("INTEGER") + .HasColumnName("seconds_to_notify_before_timeslot_start"); + + b.HasKey("Id"); + + b.ToTable("user_settings", (string)null); + }); + + modelBuilder.Entity("event_instances_to_users", b => + { + b.Property("event_instance_id") + .HasColumnType("INTEGER"); + + b.Property("user_id") + .HasColumnType("INTEGER"); + + b.HasKey("event_instance_id", "user_id"); + + b.HasIndex("user_id"); + + b.ToTable("event_instances_to_users"); + }); + + modelBuilder.Entity("users_to_avoided_users", b => + { + b.Property("avoided_user_id") + .HasColumnType("INTEGER"); + + b.Property("user_id") + .HasColumnType("INTEGER"); + + b.HasKey("avoided_user_id", "user_id"); + + b.HasIndex("user_id"); + + b.ToTable("users_to_avoided_users"); + }); + + modelBuilder.Entity("users_to_event_instance_preconfigurations", b => + { + b.Property("event_instance_preconfiguration_id") + .HasColumnType("INTEGER"); + + b.Property("user_id") + .HasColumnType("INTEGER"); + + b.HasKey("event_instance_preconfiguration_id", "user_id"); + + b.HasIndex("user_id"); + + b.ToTable("users_to_event_instance_preconfigurations"); + }); + + modelBuilder.Entity("users_to_preferred_users", b => + { + b.Property("preferred_user_id") + .HasColumnType("INTEGER"); + + b.Property("user_id") + .HasColumnType("INTEGER"); + + b.HasKey("preferred_user_id", "user_id"); + + b.HasIndex("user_id"); + + b.ToTable("users_to_preferred_users"); + }); + + modelBuilder.Entity("users_to_roles", b => + { + b.Property("role_id") + .HasColumnType("INTEGER"); + + b.Property("user_id") + .HasColumnType("INTEGER"); + + b.HasKey("role_id", "user_id"); + + b.HasIndex("user_id"); + + b.ToTable("users_to_roles"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventInstanceEntity", b => + { + b.HasOne("MinigolfFriday.Data.Entities.EventTimeslotEntity", "EventTimeslot") + .WithMany("Instances") + .HasForeignKey("timeslot_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EventTimeslot"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventInstancePreconfigurationEntity", b => + { + b.HasOne("MinigolfFriday.Data.Entities.EventTimeslotEntity", "EventTimeSlot") + .WithMany("Preconfigurations") + .HasForeignKey("event_timeslot_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EventTimeSlot"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventTimeslotEntity", b => + { + b.HasOne("MinigolfFriday.Data.Entities.EventEntity", "Event") + .WithMany("Timeslots") + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MinigolfFriday.Data.Entities.MinigolfMapEntity", "Map") + .WithMany("EventTimeslots") + .HasForeignKey("MapId"); + + b.Navigation("Event"); + + b.Navigation("Map"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventTimeslotRegistrationEntity", b => + { + b.HasOne("MinigolfFriday.Data.Entities.EventTimeslotEntity", "EventTimeslot") + .WithMany("Registrations") + .HasForeignKey("EventTimeslotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MinigolfFriday.Data.Entities.EventTimeslotEntity", "FallbackEventTimeslot") + .WithMany() + .HasForeignKey("FallbackEventTimeslotId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EventTimeslot"); + + b.Navigation("FallbackEventTimeslot"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.UserEntity", b => + { + b.HasOne("MinigolfFriday.Data.Entities.UserSettingsEntity", "Settings") + .WithMany("Users") + .HasForeignKey("SettingsId"); + + b.Navigation("Settings"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.UserPushSubscriptionEntity", b => + { + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", "User") + .WithMany("PushSubscriptions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("event_instances_to_users", b => + { + b.HasOne("MinigolfFriday.Data.Entities.EventInstanceEntity", null) + .WithMany() + .HasForeignKey("event_instance_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", null) + .WithMany() + .HasForeignKey("user_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("users_to_avoided_users", b => + { + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", null) + .WithMany() + .HasForeignKey("avoided_user_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", null) + .WithMany() + .HasForeignKey("user_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("users_to_event_instance_preconfigurations", b => + { + b.HasOne("MinigolfFriday.Data.Entities.EventInstancePreconfigurationEntity", null) + .WithMany() + .HasForeignKey("event_instance_preconfiguration_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", null) + .WithMany() + .HasForeignKey("user_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("users_to_preferred_users", b => + { + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", null) + .WithMany() + .HasForeignKey("preferred_user_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", null) + .WithMany() + .HasForeignKey("user_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("users_to_roles", b => + { + b.HasOne("MinigolfFriday.Data.Entities.RoleEntity", null) + .WithMany() + .HasForeignKey("role_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MinigolfFriday.Data.Entities.UserEntity", null) + .WithMany() + .HasForeignKey("user_id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventEntity", b => + { + b.Navigation("Timeslots"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.EventTimeslotEntity", b => + { + b.Navigation("Instances"); + + b.Navigation("Preconfigurations"); + + b.Navigation("Registrations"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.MinigolfMapEntity", b => + { + b.Navigation("EventTimeslots"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.UserEntity", b => + { + b.Navigation("PushSubscriptions"); + }); + + modelBuilder.Entity("MinigolfFriday.Data.Entities.UserSettingsEntity", b => + { + b.Navigation("Users"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/server/migrations/sqlite/Migrations/20240709194533_AddEventExternalUri.cs b/src/server/migrations/sqlite/Migrations/20240709194533_AddEventExternalUri.cs new file mode 100644 index 0000000..1c50a34 --- /dev/null +++ b/src/server/migrations/sqlite/Migrations/20240709194533_AddEventExternalUri.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MinigolfFriday.Migrations.Sqlite.Migrations +{ + /// + public partial class AddEventExternalUri : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "external_uri", + table: "events", + type: "TEXT", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "external_uri", + table: "events"); + } + } +} diff --git a/src/server/migrations/sqlite/Migrations/DatabaseContextModelSnapshot.cs b/src/server/migrations/sqlite/Migrations/DatabaseContextModelSnapshot.cs index 42cdd2b..d3e202e 100644 --- a/src/server/migrations/sqlite/Migrations/DatabaseContextModelSnapshot.cs +++ b/src/server/migrations/sqlite/Migrations/DatabaseContextModelSnapshot.cs @@ -28,6 +28,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT") .HasColumnName("date"); + b.Property("ExternalUri") + .HasColumnType("TEXT") + .HasColumnName("external_uri"); + b.Property("RegistrationDeadline") .HasColumnType("TEXT") .HasColumnName("registration_deadline");