mirror of
https://github.com/open-reception/appointment-booking-software.git
synced 2026-08-17 21:25:52 +02:00
Removed expiry_date from appointments. Added housekeeping task to remove old appointments from db.
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
co-authored by
Copilot
parent
2a8477cd0b
commit
7942e151c6
@@ -107,8 +107,6 @@ export const tenantConfig = pgTable(
|
||||
}),
|
||||
);
|
||||
|
||||
// TODO: Filter in API für bestimmte Rollen
|
||||
|
||||
export const user = pgTable(
|
||||
"user",
|
||||
{
|
||||
|
||||
@@ -173,8 +173,6 @@ export const appointment = pgTable("appointment", {
|
||||
duration: integer("duration").notNull(),
|
||||
/** Timezone of client booking appointment */
|
||||
timezone: text("timezone").notNull(),
|
||||
/** When appointment data expires and can be auto-deleted */
|
||||
expiryDate: date("expiry_date"),
|
||||
/** Current status of the appointment - defaults depend on channel's requiresConfirmation setting */
|
||||
status: appointmentStatusEnum("status").notNull(),
|
||||
/** Encrypted appointment data (name, email, phone) - Legacy field */
|
||||
|
||||
@@ -4,7 +4,7 @@ import { type SelectAppointment } from "../db/tenant-schema";
|
||||
import * as centralSchema from "../db/central-schema";
|
||||
import logger from "$lib/logger";
|
||||
import { ValidationError, NotFoundError, InternalError, ConflictError } from "../utils/errors";
|
||||
import { and, eq, gte, lte, asc } from "drizzle-orm";
|
||||
import { and, eq, gte, lte, lt, asc } from "drizzle-orm";
|
||||
import type { AppointmentResponse } from "$lib/types/appointment";
|
||||
import {
|
||||
sendAppointmentCreatedEmail,
|
||||
@@ -19,6 +19,8 @@ import { challengeStore } from "./challenge-store";
|
||||
import { challengeThrottleService } from "./challenge-throttle";
|
||||
import { timingSafeEqual } from "node:crypto";
|
||||
|
||||
const CUTOFF_DAYS = 90;
|
||||
|
||||
export interface ClientTunnelData {
|
||||
tunnelId: string;
|
||||
channelId: string;
|
||||
@@ -78,6 +80,25 @@ export class AppointmentService {
|
||||
}
|
||||
}
|
||||
|
||||
async cleanupExpiredAppointments(): Promise<void> {
|
||||
const log = logger.setContext("AppointmentService");
|
||||
const db = await this.getDb();
|
||||
|
||||
const cutoffDate = new Date();
|
||||
cutoffDate.setDate(cutoffDate.getDate() - CUTOFF_DAYS);
|
||||
|
||||
const deletedAppointments = await db
|
||||
.delete(tenantSchema.appointment)
|
||||
.where(lt(tenantSchema.appointment.appointmentDate, cutoffDate))
|
||||
.returning({ id: tenantSchema.appointment.id });
|
||||
|
||||
log.info("Expired appointments cleaned up", {
|
||||
tenantId: this.tenantId,
|
||||
deletedCount: deletedAppointments.length,
|
||||
cutoffDate: cutoffDate.toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
async hasAppointments(): Promise<boolean> {
|
||||
const log = logger.setContext("AppointmentService");
|
||||
log.debug("Checking if tenant has appointments", { tenantId: this.tenantId });
|
||||
@@ -738,7 +759,6 @@ export class AppointmentService {
|
||||
encryptedPayload: tenantSchema.appointment.encryptedPayload,
|
||||
iv: tenantSchema.appointment.iv,
|
||||
authTag: tenantSchema.appointment.authTag,
|
||||
expiryDate: tenantSchema.appointment.expiryDate,
|
||||
createdAt: tenantSchema.appointment.createdAt,
|
||||
updatedAt: tenantSchema.appointment.updatedAt,
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ import { InviteService } from "./invite-service";
|
||||
import { SessionService } from "../auth/session-service";
|
||||
import { ClientPinResetService } from "./client-pin-reset-service";
|
||||
import { UniversalLogger } from "$lib/logger";
|
||||
import { AppointmentService } from "./appointment-service";
|
||||
|
||||
const logger = new UniversalLogger().setContext("StartupService");
|
||||
const TWELVE_HOURS_IN_MS = 12 * 60 * 60 * 1000;
|
||||
@@ -172,6 +173,13 @@ export class StartupService {
|
||||
for (const tenantData of tenants) {
|
||||
try {
|
||||
const pinResetService = await ClientPinResetService.forTenant(tenantData.id);
|
||||
const appointmentService = await AppointmentService.forTenant(tenantData.id);
|
||||
await appointmentService.cleanupExpiredAppointments();
|
||||
logger.info("Cleaned up expired appointments for tenant", {
|
||||
tenantId: tenantData.id,
|
||||
shortName: tenantData.shortName,
|
||||
});
|
||||
|
||||
const deletedTokens = await pinResetService.cleanupExpiredTokens();
|
||||
logger.info(`Cleaned up ${deletedTokens} expired PIN reset tokens for tenant`, {
|
||||
tenantId: tenantData.id,
|
||||
|
||||
@@ -56,11 +56,6 @@ registerOpenAPIRoute("/tenants/{id}/appointments", "GET", {
|
||||
format: "date-time",
|
||||
description: "Appointment date and time",
|
||||
},
|
||||
expiryDate: {
|
||||
type: "string",
|
||||
format: "date",
|
||||
description: "Data expiry date (nullable)",
|
||||
},
|
||||
status: {
|
||||
type: "string",
|
||||
enum: ["NEW", "CONFIRMED", "HELD", "REJECTED", "NO_SHOW"],
|
||||
|
||||
@@ -54,11 +54,6 @@ registerOpenAPIRoute("/tenants/{id}/appointments/{appointmentId}", "GET", {
|
||||
format: "date-time",
|
||||
description: "Appointment date and time",
|
||||
},
|
||||
expiryDate: {
|
||||
type: "string",
|
||||
format: "date",
|
||||
description: "Data expiry date (nullable)",
|
||||
},
|
||||
status: {
|
||||
type: "string",
|
||||
enum: ["NEW", "CONFIRMED", "HELD", "REJECTED", "NO_SHOW"],
|
||||
|
||||
@@ -48,11 +48,6 @@ registerOpenAPIRoute("/tenants/{id}/appointments/{appointmentId}/cancel", "PUT",
|
||||
format: "date-time",
|
||||
description: "Appointment date and time",
|
||||
},
|
||||
expiryDate: {
|
||||
type: "string",
|
||||
format: "date",
|
||||
description: "Data expiry date (nullable)",
|
||||
},
|
||||
status: {
|
||||
type: "string",
|
||||
enum: ["REJECTED"],
|
||||
|
||||
-1
@@ -53,7 +53,6 @@ describe("Appointment Cancel API Route", () => {
|
||||
tunnelId: "tunnel-123",
|
||||
channelId: "channel-123",
|
||||
appointmentDate: "2024-01-01T10:00:00.000Z",
|
||||
expiryDate: null,
|
||||
status: "REJECTED",
|
||||
encryptedData: null,
|
||||
dataKey: null,
|
||||
|
||||
@@ -79,11 +79,6 @@ registerOpenAPIRoute("/tenants/{id}/appointments/{appointmentId}/confirm", "PUT"
|
||||
format: "date-time",
|
||||
description: "Appointment date and time",
|
||||
},
|
||||
expiryDate: {
|
||||
type: "string",
|
||||
format: "date",
|
||||
description: "Data expiry date (nullable)",
|
||||
},
|
||||
status: {
|
||||
type: "string",
|
||||
enum: ["CONFIRMED"],
|
||||
|
||||
-1
@@ -53,7 +53,6 @@ describe("Appointment Confirm API Route", () => {
|
||||
tunnelId: "tunnel-123",
|
||||
channelId: "channel-123",
|
||||
appointmentDate: "2024-01-01T10:00:00.000Z",
|
||||
expiryDate: null,
|
||||
status: "CONFIRMED",
|
||||
encryptedData: null,
|
||||
dataKey: null,
|
||||
|
||||
@@ -269,7 +269,6 @@ export const POST: RequestHandler = async ({ request, params }) => {
|
||||
agentId: appointment.agentId,
|
||||
appointmentDate: appointment.appointmentDate,
|
||||
timezone: appointment.timezone,
|
||||
expiryDate: appointment.expiryDate,
|
||||
status: appointment.status,
|
||||
encryptedPayload: appointment.encryptedPayload,
|
||||
duration: appointment.duration,
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE "appointment" DROP COLUMN "expiry_date";
|
||||
File diff suppressed because it is too large
Load Diff
@@ -106,6 +106,13 @@
|
||||
"when": 1775654963847,
|
||||
"tag": "0014_fluffy_ezekiel",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 15,
|
||||
"version": "7",
|
||||
"when": 1777901096310,
|
||||
"tag": "0015_loud_tarot",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user