Files
erika/app/admin/backend/actions.ts
T

82 lines
2.1 KiB
TypeScript

"use server";
import { revalidateTag } from "next/cache";
import { readdirSync } from "fs";
import { join } from "path";
export async function refetchPlatform(
platform: string
): Promise<{ count: number | null }> {
// Validate platform against actual files to prevent path traversal
const dir = join(process.cwd(), "lib", "followers");
const validPlatforms = readdirSync(dir)
.filter((f) => f.endsWith(".ts") && f !== "index.ts")
.map((f) => f.replace(".ts", ""));
if (!validPlatforms.includes(platform)) {
throw new Error("Invalid platform");
}
let count: number | null = null;
let tiktokProfile = null;
try {
const mod = await import(`@/lib/followers/${platform}`);
const fns = Object.entries(mod).filter(
([, v]) => typeof v === "function"
) as [string, Function][];
// Prefer function with "Count" or "Member" in name
const countFn = fns.find(([name]) => /count|member/i.test(name));
const fn = countFn || fns[0];
if (fn) {
const result = await fn[1]();
if (typeof result === "number") {
count = result;
} else if (result === null) {
count = null;
} else if (
result &&
typeof result === "object" &&
"followerCount" in result
) {
count = result.followerCount;
tiktokProfile = result;
}
}
} catch (err) {
console.error(`Failed to fetch ${platform}:`, err);
throw new Error(`Failed to fetch ${platform}`);
}
// Update Redis cache
try {
const { getRedisClient } = await import("@/lib/redis");
const redis = await getRedisClient();
const cached = await redis.get("erika:follower_counts");
const counts: Record<string, number | null> = cached
? JSON.parse(cached)
: {};
counts[platform] = count;
await redis.set("erika:follower_counts", JSON.stringify(counts), { ex: 3600 });
if (tiktokProfile) {
await redis.set(
"erika:tiktok_profile",
JSON.stringify(tiktokProfile),
{ ex: 3600 }
);
}
} catch {
// Redis unavailable
}
// @ts-ignore
revalidateTag("follower-counts-api");
return { count };
}