This repository has been archived on 2025-10-01. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
gwg-redis-api-archived/index.ts
GarandPLG 8eacb68a7e Dodaj mikroserwis Redis API z obsługą Docker i Bun (Migracja api do oddzielnego repozytorium)
- Utworzono API w Bun i TypeScript do obsługi Redis (GET/POST/DELETE) z różnymi typami danych (string, list, set, hash).
- Dodano `docker-compose.yaml` i `dockerfile` do uruchamiania Redis i API jako kontenery.
- Skonfigurowano `.dockerignore` i `.gitignore` dla czystości repozytorium.
- Użyto `bun.lockb` oraz `package.json` do zależności i blokady wersji.
- Skonfigurowano `tsconfig.json` dla kompilatora TypeScript.
2025-05-15 21:50:11 +02:00

196 lines
5.5 KiB
TypeScript

import { serve } from 'bun';
import { createClient } from 'redis';
const client = createClient({
url: Bun.env.IS_DOCKER ? 'redis://redis-stack:6379' : 'redis://localhost:6379'
});
client.on('error', (err: Error) => console.error('Redis Client Error', err));
await client.connect();
const healthCheck = async () => {
try {
await client.ping();
return { status: 'OK' };
} catch (error: unknown) {
return {
status: 'ERROR',
message: error instanceof Error ? error.message : 'Nieznany błąd'
};
}
};
const handleRedisAction = async (
method: string,
key?: string,
value?: any,
ttl?: number,
type: string = 'string'
) => {
switch (method) {
case 'GET':
if (!key) return { error: 'Brak klucza' };
const keyExists = await client.exists(key);
if (!keyExists) return { value: null, type: null };
const valueType = await client.type(key);
switch (valueType) {
case 'string':
const getValue = await client.get(key);
return { value: getValue ? JSON.parse(getValue) : null, type: valueType };
case 'list':
const getList = await client.lRange(key, 0, -1);
return {
value: getList ? getList.map(item => JSON.parse(item)) : [],
type: valueType
};
case 'hash':
const getHash = await client.hGetAll(key);
const parsedHash = Object.fromEntries(
Object.entries(getHash).map(([k, v]) => [k, JSON.parse(v)])
);
return { value: parsedHash || {}, type: valueType };
case 'set':
const getSet = await client.sMembers(key);
return {
value: getSet ? getSet.map(item => JSON.parse(item)) : [],
type: valueType
};
default:
return { error: 'Nieobsługiwany typ danych' };
}
case 'POST':
if (!key) return { error: 'Brak klucza' };
switch (type) {
case 'string':
if (!value) return { error: 'Brak wartości' };
await client.set(key, JSON.stringify(value));
if (ttl && ttl > 0) await client.expire(key, ttl);
return { message: 'Klucz string ustawiony' };
case 'hash':
if (!value || typeof value !== 'object')
return { error: 'Brak wartości dla hash lub niepoprawny typ' };
const stringifiedHash = Object.fromEntries(
Object.entries(value).map(([k, v]) => [k, JSON.stringify(v)])
);
await client.hSet(key, stringifiedHash);
if (ttl && ttl > 0) await client.expire(key, ttl);
return { message: 'Hash ustawiony' };
case 'list':
if (!Array.isArray(value)) return { error: 'Wartość musi być tablicą' };
const stringifiedList = value.map(item => JSON.stringify(item));
await client.rPush(key, stringifiedList);
if (ttl && ttl > 0) await client.expire(key, ttl);
return { message: 'Lista ustawiona' };
case 'set':
if (!Array.isArray(value)) return { error: 'Wartość musi być tablicą' };
const stringifiedSet = value.map(item => JSON.stringify(item));
await client.sAdd(key, stringifiedSet);
if (ttl && ttl > 0) await client.expire(key, ttl);
return { message: 'Set ustawiony' };
// case 'zset':
// if (
// !Array.isArray(value) ||
// !value.every((item: any) => item.score !== undefined && item.value !== undefined)
// )
// return { error: 'Wartość musi być tablicą obiektów z polem "score" i "value"' };
// for (const item of value) {
// await client.zAdd(key, { score: item.score, value: item.value });
// }
// console.log('SET (zset)', key, value);
// return { message: 'Sorted Set ustawiony' };
default:
return { error: 'Nieobsługiwany typ danych' };
}
case 'DELETE':
if (!key) return { error: 'Brak klucza' };
if (key === 'FLUSHALL') {
await client.flushAll();
return { message: 'Wszystkie klucze zostały usunięte' };
}
await client.del(key);
return { message: 'Klucz usunięty' };
default:
return { error: 'Nieobsługiwana metoda' };
}
};
serve({
port: 5001,
async fetch(req: Request) {
const url = new URL(req.url);
const method = req.method;
const headers: { [key: string]: string } = {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type'
};
if (method === 'OPTIONS') {
return new Response('', { status: 200, headers });
}
if (url.pathname === '/api/redis') {
try {
const body = method !== 'GET' && method !== 'DELETE' ? await req.json() : null;
const key =
method === 'GET' || method === 'DELETE' ? url.searchParams.get('key') : body?.key;
const value = body?.value;
const ttl = body?.ttl ? parseInt(body.ttl, 10) : 0;
const type = body?.type;
const result = await handleRedisAction(method, key, value, ttl, type);
if (result?.error)
return new Response(JSON.stringify({ error: result.error }), { status: 400, headers });
return new Response(JSON.stringify(result), { status: 200, headers });
} catch (error) {
return new Response(
JSON.stringify({ error: error instanceof Error ? error.message : 'Nieznany błąd' }),
{ status: 400, headers }
);
}
} else if (url.pathname === '/api/health') {
const health = await healthCheck();
return new Response(JSON.stringify(health), {
status: health.status === 'OK' ? 200 : 500,
headers
});
}
return new Response(JSON.stringify({ error: 'Nieobsługiwany adres URL' }), {
status: 404,
headers
});
}
});
console.log('Redis API uruchomiony na porcie 5001');