diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 89eb78f..9cb3be8 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -36,10 +36,7 @@ env:
ARUBA_PASS: "string"
EXP_API_USER: "string"
EXP_API_PASS: "string"
- TILES_URL: "keydb://string"
- KEYDB_URL: "keydb://string"
JWT_SECRET: "335922ae"
- SKIP_REDIS: "false"
jobs:
diff --git a/apps/db/migrations/54_kvcache.up.sql b/apps/db/migrations/54_kvcache.up.sql
new file mode 100644
index 0000000..46923ec
--- /dev/null
+++ b/apps/db/migrations/54_kvcache.up.sql
@@ -0,0 +1,17 @@
+CREATE TABLE IF NOT EXISTS tiles_cache (
+ key TEXT NOT NULL,
+ value BYTEA NOT NULL,
+ expires_at TIMESTAMPTZ -- NULL = no expiry
+);
+DO $$ BEGIN IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'tiles_cache_pkey'
+ AND conrelid = 'public.tiles_cache'::regclass
+) THEN
+ALTER TABLE public.tiles_cache
+ADD CONSTRAINT tiles_cache_pkey PRIMARY KEY (key);
+END IF;
+END $$;
+
+CREATE INDEX IF NOT EXISTS tiles_cache_key_idx ON public.tiles_cache (key);
\ No newline at end of file
diff --git a/apps/infoalloggi/.dockerignore b/apps/infoalloggi/.dockerignore
index 7086742..1b23685 100644
--- a/apps/infoalloggi/.dockerignore
+++ b/apps/infoalloggi/.dockerignore
@@ -7,7 +7,7 @@ node_modules
.github
Dockerfile
docker-compose.yml
-keydb-docker-compose.yml
+dev-docker-compose.yml
pgadmin-docker-compose.yml
dev_utils_compose.sh
.env
diff --git a/apps/infoalloggi/Dockerfile b/apps/infoalloggi/Dockerfile
index 42cacb9..fd4d48a 100644
--- a/apps/infoalloggi/Dockerfile
+++ b/apps/infoalloggi/Dockerfile
@@ -13,8 +13,7 @@ COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NODE_ENV=production \
- NEXT_TELEMETRY_DISABLED=1 \
- SKIP_REDIS="true"
+ NEXT_TELEMETRY_DISABLED=1
ARG BASE_URL NEXT_PUBLIC_BASE_URL NEXT_PUBLIC_STRIPE_PUBLIC_KEY
ENV BASE_URL=$BASE_URL \
@@ -29,8 +28,7 @@ FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production \
- NEXT_TELEMETRY_DISABLED=1 \
- SKIP_REDIS="false"
+ NEXT_TELEMETRY_DISABLED=1
ARG BASE_URL
ENV BASE_URL=$BASE_URL
@@ -72,8 +70,6 @@ ARG EXP_API_USER
ENV EXP_API_USER=$EXP_API_USER
ARG EXP_API_PASS
ENV EXP_API_PASS=$EXP_API_PASS
-ARG KEYDB_URL
-ENV KEYDB_URL=$KEYDB_URL
ARG TILES_URL
ENV TILES_URL=$TILES_URL
ARG SKEBBY_USER
diff --git a/apps/infoalloggi/TODO b/apps/infoalloggi/TODO
index aafb233..8c8d41e 100644
--- a/apps/infoalloggi/TODO
+++ b/apps/infoalloggi/TODO
@@ -13,7 +13,7 @@ TODOS:
- pre attivazione se admin mostrare tastino contatti in preview
- onboarding submit (toast e email che invitano a pagare)
- migliorare onboarding e pag guida
-
+ - "fatto_nulla" status in servizio
AFTER MVP:
- TODO migrazione app router https://nextjs.org/docs/pages/building-your-application/upgrading/app-router-migration#migrating-from-pages-to-app
- Sezione "La mia locazione" con i dati della locazione attuale dell'utente
diff --git a/apps/infoalloggi/dev-docker-compose.yml b/apps/infoalloggi/dev-docker-compose.yml
index 8b893d9..22baa4c 100644
--- a/apps/infoalloggi/dev-docker-compose.yml
+++ b/apps/infoalloggi/dev-docker-compose.yml
@@ -1,20 +1,4 @@
services:
- tiles:
- image: eqalpha/keydb:latest
- volumes:
- - tiles-data:/data
- ports:
- - "6379:6379"
- command: keydb-server /etc/keydb/keydb.conf --maxmemory 100mb --maxmemory-policy volatile-lru --maxmemory-samples 5
- restart: unless-stopped
- keydb:
- image: eqalpha/keydb:latest
- volumes:
- - keydb-data:/data
- ports:
- - "6380:6379"
-
- restart: unless-stopped
storage:
image: "ghcr.io/marcopedone/go_fs:latest"
ports:
@@ -34,6 +18,4 @@ services:
start_period: 10s
volumes:
- tiles-data:
- keydb-data:
storage_data:
diff --git a/apps/infoalloggi/kanel.config.js b/apps/infoalloggi/kanel.config.js
index a9988af..5742720 100644
--- a/apps/infoalloggi/kanel.config.js
+++ b/apps/infoalloggi/kanel.config.js
@@ -161,6 +161,8 @@ module.exports = {
"pg_catalog.bpchar": "string",
'pg_catalog.numeric': 'number',
//"pg_catalog.json": "string",
+ 'pg_catalog.bytea': "Buffer"
+
},
preRenderHooks: [
@@ -177,6 +179,7 @@ module.exports = {
zodTypeMap: {
...defaultZodTypeMap,
"pg_catalog.numeric": "z.number()",
+ "pg_catalog.bytea": "z.instanceof(Buffer)"
}
}),
],
diff --git a/apps/infoalloggi/package-lock.json b/apps/infoalloggi/package-lock.json
index ae2b659..e575279 100644
--- a/apps/infoalloggi/package-lock.json
+++ b/apps/infoalloggi/package-lock.json
@@ -49,7 +49,6 @@
"embla-carousel-react": "^8.6.0",
"framer-motion": "^12.38.0",
"frimousse": "^0.3.0",
- "ioredis": "^5.9.2",
"jose": "^6.0.12",
"kysely": "^0.28.17",
"kysely-plugin-serialize": "^0.8.2",
@@ -1818,10 +1817,6 @@
"url": "https://opencollective.com/libvips"
}
},
- "node_modules/@ioredis/commands": {
- "version": "1.5.0",
- "license": "MIT"
- },
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"license": "MIT",
@@ -7420,13 +7415,6 @@
"node": ">=6"
}
},
- "node_modules/cluster-key-slot": {
- "version": "1.1.2",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/cmdk": {
"version": "1.1.1",
"license": "MIT",
@@ -7843,13 +7831,6 @@
"node": ">=0.4.0"
}
},
- "node_modules/denque": {
- "version": "2.1.0",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=0.10"
- }
- },
"node_modules/dequal": {
"version": "2.0.3",
"license": "MIT",
@@ -9041,28 +9022,6 @@
"node": ">= 0.10"
}
},
- "node_modules/ioredis": {
- "version": "5.9.2",
- "license": "MIT",
- "dependencies": {
- "@ioredis/commands": "1.5.0",
- "cluster-key-slot": "^1.1.0",
- "debug": "^4.3.4",
- "denque": "^2.1.0",
- "lodash.defaults": "^4.2.0",
- "lodash.isarguments": "^3.1.0",
- "redis-errors": "^1.2.0",
- "redis-parser": "^3.0.0",
- "standard-as-callback": "^2.1.0"
- },
- "engines": {
- "node": ">=12.22.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/ioredis"
- }
- },
"node_modules/is-array-buffer": {
"version": "3.0.5",
"dev": true,
@@ -10120,14 +10079,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/lodash.defaults": {
- "version": "4.2.0",
- "license": "MIT"
- },
- "node_modules/lodash.isarguments": {
- "version": "3.1.0",
- "license": "MIT"
- },
"node_modules/loose-envify": {
"version": "1.4.0",
"license": "MIT",
@@ -12228,23 +12179,6 @@
"node": ">= 10.13.0"
}
},
- "node_modules/redis-errors": {
- "version": "1.2.0",
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/redis-parser": {
- "version": "3.0.0",
- "license": "MIT",
- "dependencies": {
- "redis-errors": "^1.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/reflect.getprototypeof": {
"version": "1.0.10",
"dev": true,
@@ -12871,10 +12805,6 @@
"stacktrace-gps": "^3.0.4"
}
},
- "node_modules/standard-as-callback": {
- "version": "2.1.0",
- "license": "MIT"
- },
"node_modules/std-env": {
"version": "4.0.0",
"dev": true,
diff --git a/apps/infoalloggi/package.json b/apps/infoalloggi/package.json
index b825200..843d38d 100644
--- a/apps/infoalloggi/package.json
+++ b/apps/infoalloggi/package.json
@@ -64,7 +64,6 @@
"embla-carousel-react": "^8.6.0",
"framer-motion": "^12.38.0",
"frimousse": "^0.3.0",
- "ioredis": "^5.9.2",
"jose": "^6.0.12",
"kysely": "^0.28.17",
"kysely-plugin-serialize": "^0.8.2",
@@ -159,4 +158,4 @@
"axios": "^1.16.0"
}
}
-}
\ No newline at end of file
+}
diff --git a/apps/infoalloggi/src/env.ts b/apps/infoalloggi/src/env.ts
index 5ddcff4..cda98e3 100644
--- a/apps/infoalloggi/src/env.ts
+++ b/apps/infoalloggi/src/env.ts
@@ -14,14 +14,12 @@ export const env = createEnv({
FIC_COMPANY_ID: z.string(),
INTERNAL_BASE_URL: z.string(),
JWT_SECRET: z.string(),
- KEYDB_URL: z.string(),
NODE_ENV: z.enum(["development", "test", "production"]),
PGHOST: z.string(),
PGPORT: z.string(),
POSTGRES_DB: z.string(),
POSTGRES_PASSWORD: z.string(),
POSTGRES_USER: z.string(),
- SKIP_REDIS: z.string(),
STRIPE_SECRET_KEY: z.string(),
STRIPE_WEBHOOK_SECRET: z.string(),
TILES_URL: z.string(),
diff --git a/apps/infoalloggi/src/pages/api/tiles/[[...slug]].ts b/apps/infoalloggi/src/pages/api/tiles/[[...slug]].ts
index 78cf0f0..5422375 100644
--- a/apps/infoalloggi/src/pages/api/tiles/[[...slug]].ts
+++ b/apps/infoalloggi/src/pages/api/tiles/[[...slug]].ts
@@ -1,21 +1,10 @@
-import Redis from "ioredis";
import type { NextApiRequest, NextApiResponse } from "next";
-import { env } from "~/env";
+import { appRouter } from "~/server/api/root";
+import { createTRPCContext, t } from "~/server/api/trpc";
-let tiles: Redis | null = null;
-
-export function getTilesClient() {
- if (env.SKIP_REDIS === "true") {
- return null;
- }
- if (!tiles) {
- tiles = new Redis(`keydb://${env.TILES_URL}`);
- }
- return tiles;
-}
/*
* /api/tiles/[[...slug]]
- * This API route is used to fetch tiles from OpenStreetMap and cache them in KeyDb.
+ * This API route is used to fetch tiles from OpenStreetMap and cache them.
* The tile URL is constructed using the provided slug parameters: /api/tiles/s/z/x/y
* The slug should be an array of 4 elements: [s, z, x, y].
* The API caches the tile for 30 days and sets appropriate headers for caching.
@@ -53,14 +42,13 @@ export default async function handler(
const cacheKey = `tile:${z}:${x}:${y}`;
- const tiles = getTilesClient();
- if (!tiles) {
- return res.status(500).json({
- error: "KeyDB client is not initialized",
- });
- }
+ const ctx = await createTRPCContext({ req: req, res: res });
+
+ const createCaller = t.createCallerFactory(appRouter);
+ const caller = createCaller(ctx);
+
+ const cachedTile = await caller.tiles.get(cacheKey);
- const cachedTile = await tiles.getBuffer(cacheKey);
if (cachedTile) {
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
res.setHeader("Content-Type", "image/png");
@@ -80,8 +68,9 @@ export default async function handler(
}
const tileData = await response.arrayBuffer();
const data = Buffer.from(tileData);
+
// Cache for 30 days
- await tiles.setex(cacheKey, 2592000, data);
+ await caller.tiles.set({ key: cacheKey, value: data, ttlSec: 2592000 });
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
res.setHeader("Content-Type", "image/png");
diff --git a/apps/infoalloggi/src/pages/area-riservata/admin/impostazioni.tsx b/apps/infoalloggi/src/pages/area-riservata/admin/impostazioni.tsx
index 3a5ae50..d353238 100644
--- a/apps/infoalloggi/src/pages/area-riservata/admin/impostazioni.tsx
+++ b/apps/infoalloggi/src/pages/area-riservata/admin/impostazioni.tsx
@@ -1,8 +1,5 @@
import Head from "next/head";
-import toast from "react-hot-toast";
import { AreaRiservataLayout } from "~/components/Layout";
-import { LoadingPage } from "~/components/loading";
-import { Button } from "~/components/ui/button";
import type { NextPageWithLayout } from "~/pages/_app";
import { api } from "~/utils/api";
@@ -23,7 +20,7 @@ const Impostazioni: NextPageWithLayout = () => {
-
+ {/* */}
>
);
@@ -75,44 +72,44 @@ const StripeSection = () => {
);
};
-const RedisCacheSection = () => {
- const { data, isLoading } = api.revalidation.getRedisStats.useQuery();
- const utils = api.useUtils();
- const { mutate } = api.revalidation.invalidateRedisCache.useMutation({
- onMutate() {
- toast.loading("Invalidating cache...", {
- id: "invalidate-cache",
- });
- },
- onSuccess: async () => {
- toast.success("Cache invalidated successfully!", {
- id: "invalidate-cache",
- });
- await utils.revalidation.getRedisStats.invalidate();
- },
- onError() {
- toast.error("Failed to invalidate cache.", {
- id: "invalidate-cache",
- });
- },
- });
- if (isLoading) {
- return ;
- }
- return (
-
-
Invalidate Redis Cache
-
Current Cache Stats:
- {data?.success ? (
-
- - Pdf Schede Annuncio: {data?.schedaCount}
- - Pdf Condizioni: {data?.condizioniCount}
-
- ) : (
-
Error fetching cache stats
- )}
+// const RedisCacheSection = () => {
+// const { data, isLoading } = api.revalidation.getRedisStats.useQuery();
+// const utils = api.useUtils();
+// const { mutate } = api.revalidation.invalidateRedisCache.useMutation({
+// onMutate() {
+// toast.loading("Invalidating cache...", {
+// id: "invalidate-cache",
+// });
+// },
+// onSuccess: async () => {
+// toast.success("Cache invalidated successfully!", {
+// id: "invalidate-cache",
+// });
+// await utils.revalidation.getRedisStats.invalidate();
+// },
+// onError() {
+// toast.error("Failed to invalidate cache.", {
+// id: "invalidate-cache",
+// });
+// },
+// });
+// if (isLoading) {
+// return
;
+// }
+// return (
+//
+//
Invalidate Redis Cache
+//
Current Cache Stats:
+// {data?.success ? (
+//
+// - Pdf Schede Annuncio: {data?.schedaCount}
+// - Pdf Condizioni: {data?.condizioniCount}
+//
+// ) : (
+//
Error fetching cache stats
+// )}
-
-
- );
-};
+//
+//
+// );
+// };
diff --git a/apps/infoalloggi/src/schemas/public/PublicSchema.ts b/apps/infoalloggi/src/schemas/public/PublicSchema.ts
index c11ed33..e633cfb 100644
--- a/apps/infoalloggi/src/schemas/public/PublicSchema.ts
+++ b/apps/infoalloggi/src/schemas/public/PublicSchema.ts
@@ -11,6 +11,7 @@ import type { default as UsersStorageTable } from './UsersStorage';
import type { default as ServizioAnnunciTable } from './ServizioAnnunci';
import type { default as UserInvitesTable } from './UserInvites';
import type { default as EmailsTable } from './Emails';
+import type { default as TilesCacheTable } from './TilesCache';
import type { default as ComuniTable } from './Comuni';
import type { default as RinnoviTable } from './Rinnovi';
import type { default as EventQueueTable } from './EventQueue';
@@ -58,6 +59,8 @@ export default interface PublicSchema {
emails: EmailsTable;
+ tiles_cache: TilesCacheTable;
+
comuni: ComuniTable;
rinnovi: RinnoviTable;
diff --git a/apps/infoalloggi/src/schemas/public/TilesCache.ts b/apps/infoalloggi/src/schemas/public/TilesCache.ts
new file mode 100644
index 0000000..7f480b1
--- /dev/null
+++ b/apps/infoalloggi/src/schemas/public/TilesCache.ts
@@ -0,0 +1,40 @@
+import type { ColumnType, Selectable, Insertable, Updateable } from 'kysely';
+import { z } from 'zod';
+
+/** Identifier type for public.tiles_cache */
+export type TilesCacheKey = string & { __brand: 'public.tiles_cache' };
+
+/** Represents the table public.tiles_cache */
+export default interface TilesCacheTable {
+ key: ColumnType;
+
+ value: ColumnType;
+
+ expires_at: ColumnType;
+}
+
+export type TilesCache = Selectable;
+
+export type NewTilesCache = Insertable;
+
+export type TilesCacheUpdate = Updateable;
+
+export const tilesCacheKey = z.string().transform(value => value as TilesCacheKey);
+
+export const TilesCacheSchema = z.object({
+ key: tilesCacheKey,
+ value: z.instanceof(Buffer),
+ expires_at: z.date().nullable(),
+});
+
+export const NewTilesCacheSchema = z.object({
+ key: tilesCacheKey,
+ value: z.instanceof(Buffer),
+ expires_at: z.union([z.date(), z.string()]).pipe(z.coerce.date()).optional().nullable(),
+});
+
+export const TilesCacheUpdateSchema = z.object({
+ key: tilesCacheKey.optional(),
+ value: z.instanceof(Buffer).optional(),
+ expires_at: z.union([z.date(), z.string()]).pipe(z.coerce.date()).optional().nullable(),
+});
\ No newline at end of file
diff --git a/apps/infoalloggi/src/server/api/root.ts b/apps/infoalloggi/src/server/api/root.ts
index 0dcc35b..3147fdb 100644
--- a/apps/infoalloggi/src/server/api/root.ts
+++ b/apps/infoalloggi/src/server/api/root.ts
@@ -15,6 +15,7 @@ import { storageRouter } from "~/server/api/routers/storage";
import { stripeRouter } from "~/server/api/routers/stripe";
import { stripeReportsRouter } from "~/server/api/routers/stripe_reports";
import { testRouter } from "~/server/api/routers/test";
+import { tilesRouter } from "~/server/api/routers/tiles";
import { usersRouter } from "~/server/api/routers/users";
import { createTRPCRouter } from "~/server/api/trpc";
import { appuntiRouter } from "./routers/appunti";
@@ -48,6 +49,7 @@ export const appRouter = createTRPCRouter({
test: testRouter,
users: usersRouter,
//sync: syncRouter,
+ tiles: tilesRouter,
catasto: catastoRouter,
banners: bannersRouter,
flags: flagsRouter,
diff --git a/apps/infoalloggi/src/server/api/routers/annunci.ts b/apps/infoalloggi/src/server/api/routers/annunci.ts
index a92d985..a3dfc1b 100644
--- a/apps/infoalloggi/src/server/api/routers/annunci.ts
+++ b/apps/infoalloggi/src/server/api/routers/annunci.ts
@@ -25,10 +25,6 @@ import {
} from "~/server/controllers/annunci.controller";
import { getIncrociAnnuncio } from "~/server/controllers/servizio.controller";
import { db } from "~/server/db";
-import {
- invalidateCache,
- SCHEDA_ANNUNCIO_CACHE_PREFIX,
-} from "~/server/services/cache.service";
import { NewMail } from "~/server/services/mailer";
import { TypstGenerate } from "~/server/services/typst.service";
@@ -36,10 +32,6 @@ export const annunciRouter = createTRPCRouter({
editAnnuncio: adminProcedure
.input(z.object({ annuncioId: annunciId, data: AnnunciUpdateSchema }))
.mutation(async ({ input }) => {
- await invalidateCache(
- `${SCHEDA_ANNUNCIO_CACHE_PREFIX}${input.annuncioId}`,
- );
-
return await editAnnuncioHandler({
...input,
});
diff --git a/apps/infoalloggi/src/server/api/routers/revalidation.ts b/apps/infoalloggi/src/server/api/routers/revalidation.ts
index 7b3537f..c4c5b36 100644
--- a/apps/infoalloggi/src/server/api/routers/revalidation.ts
+++ b/apps/infoalloggi/src/server/api/routers/revalidation.ts
@@ -3,10 +3,6 @@ import { z } from "zod/v4";
import { env } from "~/env";
import { adminProcedure, createTRPCRouter } from "~/server/api/trpc";
import { getCodici_AnnunciHandler } from "~/server/controllers/annunci.controller";
-import {
- getCacheStats,
- invalidateAllCaches,
-} from "~/server/services/cache.service";
import {
revalidate,
revalidateMultiple,
@@ -135,11 +131,10 @@ export const revalidationRouter = createTRPCRouter({
}
}),
getRedisStats: adminProcedure.query(async () => {
- return await getCacheStats();
+ return "await getCacheStats();";
}),
invalidateRedisCache: adminProcedure.mutation(async () => {
try {
- await invalidateAllCaches();
return {
status: "success",
};
diff --git a/apps/infoalloggi/src/server/api/routers/tiles.ts b/apps/infoalloggi/src/server/api/routers/tiles.ts
new file mode 100644
index 0000000..dae9b3e
--- /dev/null
+++ b/apps/infoalloggi/src/server/api/routers/tiles.ts
@@ -0,0 +1,20 @@
+import z from "zod";
+import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
+import { get, set } from "~/server/services/tiles.service";
+
+export const tilesRouter = createTRPCRouter({
+ get: publicProcedure.input(z.string()).query(async ({ input }) => {
+ return await get(input);
+ }),
+ set: publicProcedure
+ .input(
+ z.object({
+ key: z.string(),
+ value: z.instanceof(Buffer),
+ ttlSec: z.number().optional(),
+ }),
+ )
+ .mutation(async ({ input }) => {
+ return await set(input.key, input.value, input.ttlSec);
+ }),
+});
diff --git a/apps/infoalloggi/src/server/controllers/event_queue.controller.ts b/apps/infoalloggi/src/server/controllers/event_queue.controller.ts
index 50b2382..e8120c8 100644
--- a/apps/infoalloggi/src/server/controllers/event_queue.controller.ts
+++ b/apps/infoalloggi/src/server/controllers/event_queue.controller.ts
@@ -24,11 +24,6 @@ import {
convertHtmlToTypst,
TypstGenerate,
} from "~/server/services/typst.service";
-import { getKeydbClient } from "~/utils/keydb";
-import {
- CONDIZIONI_CACHE_PREFIX,
- SCHEDA_ANNUNCIO_CACHE_PREFIX,
-} from "../services/cache.service";
type GeneratedAttachmentTypes =
| { type: "scheda_annuncio"; annuncioId: AnnunciId }
@@ -57,14 +52,6 @@ const generatePdfContent = async (params: GeneratedAttachmentTypes) => {
}
case "condizioni": {
- const keybd = getKeydbClient();
- const cacheKey = `${CONDIZIONI_CACHE_PREFIX}${params.stringId}`;
- if (keybd) {
- const cached = await keybd.get(cacheKey);
- if (cached) {
- return cached;
- }
- }
const data = await db
.selectFrom("testi_e_stringhe")
.selectAll()
@@ -83,30 +70,17 @@ const generatePdfContent = async (params: GeneratedAttachmentTypes) => {
data: { stringa },
});
const pdfBase64 = pdf.toString("base64");
- if (keybd) {
- await keybd.set(cacheKey, pdfBase64, "EX", 3600 * 24 * 5);
- }
return pdfBase64;
}
case "scheda_annuncio": {
- const keybd = getKeydbClient();
- const cacheKey = `${SCHEDA_ANNUNCIO_CACHE_PREFIX}${params.annuncioId}`;
- if (keybd) {
- const cached = await keybd.get(cacheKey);
- if (cached) {
- return cached;
- }
- }
const data = await getAnnunciSchedaData(params.annuncioId);
const pdf = await TypstGenerate({
templateId: "annuncio.typ",
data,
});
const pdfBase64 = pdf.toString("base64");
- if (keybd) {
- await keybd.set(cacheKey, pdfBase64, "EX", 3600 * 5);
- }
+
return pdfBase64;
}
default:
diff --git a/apps/infoalloggi/src/server/services/cache.service.ts b/apps/infoalloggi/src/server/services/cache.service.ts
deleted file mode 100644
index 8a95328..0000000
--- a/apps/infoalloggi/src/server/services/cache.service.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-import type Redis from "ioredis";
-import { getKeydbClient } from "~/utils/keydb";
-
-export const SCHEDA_ANNUNCIO_CACHE_PREFIX = "scheda-annuncio-";
-export const CONDIZIONI_CACHE_PREFIX = "condizioni-";
-export const invalidateCache = async (cacheKey: string) => {
- const keybd = getKeydbClient();
- if (keybd) {
- await keybd.del(cacheKey);
- }
-};
-
-async function deleteByPrefix(redis: Redis, prefix: string) {
- // Create a stream to find keys matching the pattern
- const stream = redis.scanStream({
- match: `${prefix}*`,
- count: 100, // Process 100 keys per iteration
- });
-
- stream.on("data", async (keys: string[]) => {
- if (keys.length > 0) {
- // Create a pipeline to delete keys in a single network round-trip
- const pipeline = redis.pipeline();
-
- // Use UNLINK instead of DEL for better performance on large keys
- keys.forEach((key) => pipeline.unlink(key));
-
- await pipeline.exec();
- console.log(`Deleted ${keys.length} keys`);
- }
- });
-
- stream.on("error", (err) => {
- console.error(
- `Error scanning/unlinking keys with prefix ${prefix}: ${err.message}`,
- );
- });
-}
-
-export const invalidateAllCaches = async () => {
- const keybd = getKeydbClient();
- if (keybd) {
- await deleteByPrefix(keybd, SCHEDA_ANNUNCIO_CACHE_PREFIX);
- await deleteByPrefix(keybd, CONDIZIONI_CACHE_PREFIX);
- }
-};
-
-export const getCacheStats = async () => {
- const keybd = getKeydbClient();
- if (keybd) {
- const schedaKeys = await keybd.keys(`${SCHEDA_ANNUNCIO_CACHE_PREFIX}*`);
- const condizioniKeys = await keybd.keys(`${CONDIZIONI_CACHE_PREFIX}*`);
- return {
- success: true,
- schedaCount: schedaKeys.length,
- condizioniCount: condizioniKeys.length,
- };
- }
- return {
- success: false,
- schedaCount: 0,
- condizioniCount: 0,
- };
-};
diff --git a/apps/infoalloggi/src/server/services/tiles.service.ts b/apps/infoalloggi/src/server/services/tiles.service.ts
new file mode 100644
index 0000000..a914b50
--- /dev/null
+++ b/apps/infoalloggi/src/server/services/tiles.service.ts
@@ -0,0 +1,58 @@
+import { TRPCError } from "@trpc/server";
+import type { TilesCacheKey } from "~/schemas/public/TilesCache";
+
+import { db } from "~/server/db";
+
+export async function get(key: string) {
+ try {
+ const row = await db
+ .selectFrom("tiles_cache")
+ .select(["value", "expires_at"])
+ .where("key", "=", key as TilesCacheKey)
+ // If the row is expired we treat it as missing
+ .where((eb) =>
+ eb.or([
+ eb("expires_at", "is", null),
+ eb("expires_at", ">", new Date()),
+ ]),
+ )
+ .executeTakeFirst();
+
+ return row?.value;
+ } catch (e) {
+ throw new TRPCError({
+ code: "INTERNAL_SERVER_ERROR",
+ message: `Failed get kv value: ${(e as Error).message}`,
+ });
+ }
+}
+
+export async function set(
+ key: string,
+ value: Buffer,
+ ttlSec?: number,
+): Promise {
+ const expiresAt = ttlSec ? new Date(Date.now() + ttlSec * 1_000) : null;
+
+ try {
+ await db
+ .insertInto("tiles_cache")
+ .values({
+ key: key as TilesCacheKey,
+ value: value,
+ expires_at: expiresAt,
+ })
+ .onConflict((oc) =>
+ oc.column("key").doUpdateSet({
+ value: value,
+ expires_at: expiresAt,
+ }),
+ )
+ .execute();
+ } catch (e) {
+ throw new TRPCError({
+ code: "INTERNAL_SERVER_ERROR",
+ message: `Error setting KV: ${(e as Error).message}`,
+ });
+ }
+}
diff --git a/apps/infoalloggi/src/utils/keydb.ts b/apps/infoalloggi/src/utils/keydb.ts
deleted file mode 100644
index 7598642..0000000
--- a/apps/infoalloggi/src/utils/keydb.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import Redis from "ioredis";
-import { env } from "~/env";
-
-const keydbConfig = `keydb://${env.KEYDB_URL}`;
-
-let keydb: Redis | null = null;
-
-export function getKeydbClient() {
- if (env.SKIP_REDIS === "true") return null;
- if (!keydb) {
- keydb = new Redis(keydbConfig);
- }
- return keydb;
-}
-
-export function getSubClient() {
- if (env.SKIP_REDIS === "true") return null;
- return new Redis(keydbConfig, { enableReadyCheck: false });
-}
diff --git a/docker-compose.yml b/docker-compose.yml
index 71a0276..d74c455 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -57,45 +57,6 @@ services:
retries: 5
start_period: 10s
- tiles:
- image: eqalpha/keydb:latest
- volumes:
- - tiles-data:/data
- networks:
- - dokploy-network
- expose:
- - 6379
- command: keydb-server /etc/keydb/keydb.conf --maxmemory 100mb --maxmemory-policy volatile-lru --maxmemory-samples 5
- healthcheck:
- test: ["CMD", "keydb-cli", "-h", "localhost", "ping"]
- interval: 5s
- timeout: 3s
- retries: 5
- restart: unless-stopped
- deploy:
- resources:
- limits:
- cpus: "0.5"
-
- keydb:
- image: eqalpha/keydb:latest
- volumes:
- - keydb-data:/data
- networks:
- - dokploy-network
- expose:
- - 6379
- healthcheck:
- test: ["CMD", "keydb-cli", "-h", "localhost", "ping"]
- interval: 5s
- timeout: 3s
- retries: 5
- restart: unless-stopped
- deploy:
- resources:
- limits:
- cpus: "0.5"
-
backend:
image: "${IMAGE_REGISTRY:-ghcr.io}/${OWNER:-marcopedone}/${PREFIX:-infoalloggi}-backend:${TAG:-latest}"
networks:
@@ -124,10 +85,6 @@ services:
condition: service_healthy
db:
condition: service_healthy
- keydb:
- condition: service_healthy
- tiles:
- condition: service_healthy
web:
image: "${IMAGE_REGISTRY:-ghcr.io}/${OWNER:-marcopedone}/${PREFIX:-infoalloggi}-web:${TAG:-latest}"
@@ -142,8 +99,6 @@ services:
PGHOST: db
PGPORT: "5432"
BACKENDSERVER_URL: http://backend:1323
- KEYDB_URL: keydb:6379
- TILES_URL: tiles:6379
BASE_URL: ${BASE_URL}
NEXT_PUBLIC_BASE_URL: ${BASE_URL}
STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY}
@@ -196,10 +151,6 @@ services:
condition: service_healthy
db:
condition: service_healthy
- keydb:
- condition: service_healthy
- tiles:
- condition: service_healthy
maintenance-page:
image: nginx:alpine
restart: always
@@ -224,6 +175,4 @@ networks:
volumes:
storage_data:
- tiles-data:
- keydb-data:
dbdata_v18: