diff --git a/apps/infoalloggi/.github/workflows/ci.yml b/apps/infoalloggi/.github/workflows/ci.yml
index 57a5120..e9b2d49 100644
--- a/apps/infoalloggi/.github/workflows/ci.yml
+++ b/apps/infoalloggi/.github/workflows/ci.yml
@@ -28,6 +28,7 @@ env:
SKIP_REDIS: "false"
SKEBBY_USER: "test"
SKEBBY_PASS : "test"
+ REVALIDATION_SECRET: "test"
jobs:
build:
runs-on: ubuntu-latest
diff --git a/apps/infoalloggi/Dockerfile b/apps/infoalloggi/Dockerfile
index fed08f8..2afbd31 100644
--- a/apps/infoalloggi/Dockerfile
+++ b/apps/infoalloggi/Dockerfile
@@ -63,6 +63,7 @@ ENV KEYDB_URL="localhost:6380"
ENV TILES_URL="localhost:6379"
ENV SKEBBY_USER="mock"
ENV SKEBBY_PASS="mock"
+ENV REVALIDATION_SECRET="mock"
RUN SKIP_ENV_VALIDATION=1 npm run build
@@ -128,6 +129,8 @@ ARG SKEBBY_USER
ENV SKEBBY_USER=$SKEBBY_USER
ARG SKEBBY_PASS
ENV SKEBBY_PASS=$SKEBBY_PASS
+ENV REVALIDATION_SECRET="SWKpgaaLfsyeqV1eOT0WG7TUFBewir8kJXure3O37ki8Lt4Z4IiEgBW4zPHDdM5c"
+
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
diff --git a/apps/infoalloggi/src/env.mjs b/apps/infoalloggi/src/env.mjs
index d2e8a4f..5ad3c91 100644
--- a/apps/infoalloggi/src/env.mjs
+++ b/apps/infoalloggi/src/env.mjs
@@ -30,6 +30,7 @@ const server = z.object({
TILES_URL: z.string(),
SKEBBY_USER: z.string(),
SKEBBY_PASS: z.string(),
+ REVALIDATION_SECRET: z.string(),
});
/**
@@ -73,6 +74,7 @@ const processEnv = {
TILES_URL: process.env.TILES_URL,
SKEBBY_USER: process.env.SKEBBY_USER,
SKEBBY_PASS: process.env.SKEBBY_PASS,
+ REVALIDATION_SECRET: process.env.REVALIDATION_SECRET,
};
// Don't touch the part below
diff --git a/apps/infoalloggi/src/forms/FormEditAnnuncio.tsx b/apps/infoalloggi/src/forms/FormEditAnnuncio.tsx
index 8eef78e..8e582ff 100644
--- a/apps/infoalloggi/src/forms/FormEditAnnuncio.tsx
+++ b/apps/infoalloggi/src/forms/FormEditAnnuncio.tsx
@@ -2,8 +2,10 @@ import { format } from "date-fns";
import { it } from "date-fns/locale";
import {
CalendarIcon,
+ CodeSquare,
Eye,
Printer,
+ RefreshCcw,
RotateCw,
TriangleAlert,
} from "lucide-react";
@@ -133,15 +135,21 @@ export const AnnuncioEditForm = ({ data }: { data: Annunci }) => {
const { mutateAsync: revalidate } =
api.settings.revalidateAnnuncio.useMutation({
onSettled: async () => {
- toast.success(
- "La revalidazione dell'annuncio è stata richiesta con successo,\n potrebbero volerci alcuni minuti prima che le modifiche siano visibili.",
- {
- duration: 5000,
- },
- );
+ toast.success("Revalidazione completata");
},
});
+ const { mutateAsync: update } = api.settings.updateAnnuncio.useMutation({
+ onSettled: async () => {
+ toast.success(
+ "L'aggiornamento dell'annuncio è stata richiesta con successo,\n potrebbero volerci alcuni minuti prima che le modifiche siano visibili.",
+ {
+ duration: 5000,
+ },
+ );
+ },
+ });
+
const defaultValues: FormValues = {
...data,
email: data.email || undefined,
@@ -214,57 +222,84 @@ export const AnnuncioEditForm = ({ data }: { data: Annunci }) => {
onSubmit={form.handleSubmit(onSubmit)}
>
-
-
- Annuncio {data.codice}
-
-
-
-
diff --git a/apps/infoalloggi/src/server/api/routers/settings.ts b/apps/infoalloggi/src/server/api/routers/settings.ts
index 1f5db66..2ae7a43 100644
--- a/apps/infoalloggi/src/server/api/routers/settings.ts
+++ b/apps/infoalloggi/src/server/api/routers/settings.ts
@@ -7,6 +7,7 @@ import {
createTRPCRouter,
publicProcedure,
} from "~/server/api/trpc";
+import { getCodici_AnnunciHandler } from "~/server/controllers/annunci.controller";
import {
addEtichetta,
getAllEtichette,
@@ -36,6 +37,10 @@ import {
newStringa,
updateStringhe,
} from "~/server/services/testi_stringhe.service";
+import {
+ revalidate,
+ revalidateMultiple,
+} from "~/server/utils/revalidationHelper";
import {
zEtichettaId,
zFlagsId,
@@ -250,22 +255,20 @@ export const settingsRouter = createTRPCRouter({
stringaValue: input.stringaValue,
});
}),
-
revalidateAllAnnunci: adminProcedure.mutation(async () => {
try {
- const response = await fetch(`${env.BACKENDSERVER_URL}/update`);
-
- if (!response.ok) {
- const errorData = await response.text().catch(() => "Unknown error");
- throw new Error(
- `Revalidation failed with status ${response.status}: ${errorData}`,
- );
+ const annunciOnline = await getCodici_AnnunciHandler();
+ const { success, failed } = await revalidateMultiple(
+ annunciOnline.map((cod) => `/annuncio/${cod}`),
+ );
+ console.log(`Successfully revalidated pages: ${success.join(", ")}`);
+ if (failed.length > 0) {
+ failed.forEach(({ path, error }) => {
+ console.error(`Failed to revalidate ${path}: ${error}`);
+ });
}
-
- const data = await response.json().catch(() => ({}));
return {
status: "success",
- details: data,
};
} catch (err) {
console.error("Revalidation error:", err);
@@ -284,21 +287,9 @@ export const settingsRouter = createTRPCRouter({
)
.mutation(async ({ input }) => {
try {
- const response = await fetch(
- `${env.BACKENDSERVER_URL}/update-cod/${input.cod}`,
- );
-
- if (!response.ok) {
- const errorData = await response.text().catch(() => "Unknown error");
- throw new Error(
- `Revalidation failed with status ${response.status}: ${errorData}`,
- );
- }
-
- const data = await response.json().catch(() => ({}));
+ await revalidate(`/annuncio/${input.cod}`);
return {
status: "success",
- details: data,
};
} catch (err) {
console.error("Revalidation error:", err);
@@ -309,4 +300,74 @@ export const settingsRouter = createTRPCRouter({
});
}
}),
+
+ updateAllAnnunci: adminProcedure.mutation(async () => {
+ try {
+ const response = await fetch(`${env.BACKENDSERVER_URL}/update`);
+
+ if (!response.ok) {
+ const errorData = await response.text().catch(() => "Unknown error");
+ throw new Error(
+ `Update failed with status ${response.status}: ${errorData}`,
+ );
+ }
+
+ const data = await response.json().catch(() => ({}));
+ const annunciOnline = await getCodici_AnnunciHandler();
+ const { success, failed } = await revalidateMultiple(
+ annunciOnline.map((cod) => `/annuncio/${cod}`),
+ );
+ console.log(`Successfully revalidated pages: ${success.join(", ")}`);
+ if (failed.length > 0) {
+ failed.forEach(({ path, error }) => {
+ console.error(`Failed to revalidate ${path}: ${error}`);
+ });
+ }
+ return {
+ status: "success",
+ details: data,
+ };
+ } catch (err) {
+ console.error("Update error:", err);
+ throw new TRPCError({
+ code: "INTERNAL_SERVER_ERROR",
+ message: err instanceof Error ? err.message : "Update failed",
+ cause: err,
+ });
+ }
+ }),
+ updateAnnuncio: adminProcedure
+ .input(
+ z.object({
+ cod: z.string(),
+ }),
+ )
+ .mutation(async ({ input }) => {
+ try {
+ const response = await fetch(
+ `${env.BACKENDSERVER_URL}/update-cod/${input.cod}`,
+ );
+
+ if (!response.ok) {
+ const errorData = await response.text().catch(() => "Unknown error");
+ throw new Error(
+ `Update failed with status ${response.status}: ${errorData}`,
+ );
+ }
+
+ const data = await response.json().catch(() => ({}));
+ await revalidate(`/annuncio/${input.cod}`);
+ return {
+ status: "success",
+ details: data,
+ };
+ } catch (err) {
+ console.error("Update error:", err);
+ throw new TRPCError({
+ code: "INTERNAL_SERVER_ERROR",
+ message: err instanceof Error ? err.message : "Update failed",
+ cause: err,
+ });
+ }
+ }),
});
diff --git a/apps/infoalloggi/src/server/controllers/annunci.controller.ts b/apps/infoalloggi/src/server/controllers/annunci.controller.ts
index b160234..38628d4 100644
--- a/apps/infoalloggi/src/server/controllers/annunci.controller.ts
+++ b/apps/infoalloggi/src/server/controllers/annunci.controller.ts
@@ -9,6 +9,7 @@ import type { ServizioServizioId } from "~/schemas/public/Servizio";
import { db } from "~/server/db";
import { AnnuncioObjectWithImages } from "~/server/services/annunci.service";
import { createSrcset } from "~/server/services/imageServer";
+import { revalidate } from "../utils/revalidationHelper";
// const ratelimit = new RateLimiterHandler({
// windowSize: 10,
@@ -371,13 +372,15 @@ export const editAnnuncioHandler = async ({
data: AnnunciUpdate;
}) => {
try {
- await db
+ const annuncio = await db
.updateTable("annunci")
.set({
...data,
})
.where("id", "=", annuncioId)
- .execute();
+ .returning("codice")
+ .executeTakeFirstOrThrow();
+ await revalidate(`/annuncio/${annuncio.codice}`);
return true;
} catch (e) {
throw new TRPCError({
diff --git a/apps/infoalloggi/src/server/utils/revalidationHelper.ts b/apps/infoalloggi/src/server/utils/revalidationHelper.ts
new file mode 100644
index 0000000..8cd0a79
--- /dev/null
+++ b/apps/infoalloggi/src/server/utils/revalidationHelper.ts
@@ -0,0 +1,56 @@
+export const revalidate = async (path: string) => {
+ try {
+ const res = await fetch(
+ `${process.env.NEXT_PUBLIC_BASE_URL}/api/revalidation?secret=${process.env.REVALIDATION_SECRET}&path=${encodeURIComponent(path)}`,
+ {
+ method: "GET",
+ },
+ );
+ if (!res.ok) {
+ const errorData = (await res.json()) as {
+ message: string;
+ success: boolean;
+ };
+ throw new Error(
+ `Failed to revalidate path ${path}: ${errorData.message}`,
+ );
+ }
+ } catch (error) {
+ console.error(`Error revalidating path ${path}:`, error);
+ throw error;
+ }
+};
+
+export const revalidateMultiple = async (
+ paths: string[],
+): Promise<{
+ success: string[];
+ failed: Array<{ path: string; error: string }>;
+}> => {
+ const results = await Promise.allSettled(
+ paths.map((path) => revalidate(path)),
+ );
+
+ const success: string[] = [];
+ const failed: Array<{ path: string; error: string }> = [];
+
+ results.forEach((result, index) => {
+ const path = paths[index];
+ if (!path) {
+ return;
+ }
+ if (result.status === "fulfilled") {
+ success.push(path);
+ } else {
+ failed.push({
+ path,
+ error:
+ result.reason instanceof Error
+ ? result.reason.message
+ : "Unknown error",
+ });
+ }
+ });
+
+ return { success, failed };
+};