Skip to content

Commit 3690adf

Browse files
committed
Fix image proxy handling and remove Discord proxy
1 parent 1295fc6 commit 3690adf

40 files changed

Lines changed: 297 additions & 498 deletions

scripts/generate-data-files.ts

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import {
3838
} from "../src/lib/reports";
3939
import { type CachedMessage } from "../src/lib/discord-cache";
4040
import { getGuildMembers, getGuildRoles, isDiscordConfigured } from "../src/lib/discord";
41-
import { getProxiedDiscordImage } from "../src/lib/image-proxy";
41+
import { getProxiedImageUrl } from "../src/lib/image-proxy";
4242
import { getAccountAddressFromDiscordUserId } from "../src/lib/citizenwallet";
4343
import { parseTokenValue } from "../src/lib/etherscan";
4444
import {
@@ -146,15 +146,9 @@ function generateChannelImages(year: string, month: string): void {
146146
const ext = path.extname(urlWithoutQuery) || ".jpg";
147147
const filePath = `/data/${year}/${month}/channels/discord/images/${attachment.id}${ext}`;
148148

149-
// Generate proxy URL with all required metadata (relative for static files)
150-
const proxyUrl = getProxiedDiscordImage(
151-
channelId,
152-
msg.id,
153-
attachment.id,
154-
msg.timestamp,
155-
undefined,
156-
{ relative: true }
157-
);
149+
const proxyUrl = getProxiedImageUrl(filePath, undefined, {
150+
relative: true,
151+
});
158152

159153
channelImages.push({
160154
id: attachment.id,
@@ -267,15 +261,9 @@ function generateLatestImages(): number {
267261
// Use "latest" path since images are stored in data/latest/channels/discord/images/
268262
const filePath = `/data/latest/channels/discord/images/${attachment.id}${ext}`;
269263

270-
// Generate proxy URL with all required metadata (relative for static files)
271-
const proxyUrl = getProxiedDiscordImage(
272-
channelId,
273-
msg.id,
274-
attachment.id,
275-
msg.timestamp,
276-
undefined,
277-
{ relative: true }
278-
);
264+
const proxyUrl = getProxiedImageUrl(filePath, undefined, {
265+
relative: true,
266+
});
279267

280268
const imageData = {
281269
id: attachment.id,

src/app/[year]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
import { DiscordImageGallery } from "@/components/discord-image-gallery";
1313
import { ReportFinancialCharts } from "@/components/report-financial-charts";
1414
import { Users, TrendingUp, TrendingDown, Loader2, ChevronDown } from "lucide-react";
15-
import Image from "next/image";
15+
import Image from "@/components/optimized-image";
1616
import Link from "next/link";
1717

1818
interface YearlyReportData {

src/app/api/discord-image-proxy/route.ts

Lines changed: 0 additions & 218 deletions
This file was deleted.

src/app/api/image-proxy/route.ts

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
type ImageSize,
99
SIZE_CONFIG,
1010
CACHE_DURATION,
11+
resolveRequestedImageSize,
1112
} from "@/lib/image-proxy-server";
1213

1314
function getCacheControl(request: NextRequest, localFile: boolean): string {
@@ -25,16 +26,20 @@ function getCacheControl(request: NextRequest, localFile: boolean): string {
2526
* Image Proxy API - Handles both local data paths and external image URLs
2627
*
2728
* Query parameters:
28-
* - url: Image URL or local path (e.g., /data/2025/11/messages/discord/images/123.jpg) (required)
29+
* - url: Image URL or local path (e.g., /data/2025/11/channels/discord/images/123.jpg or /images/foo.jpg) (required)
2930
* - size: Optional size parameter (xs|sm|md|lg) for resizing
3031
*
31-
* Example: /api/image-proxy?url=/data/2025/11/messages/discord/images/123.jpg&size=sm
32+
* Example: /api/image-proxy?url=/data/2025/11/channels/discord/images/123.jpg&size=sm
33+
* Example: /api/image-proxy?url=/images/chb-facade.avif&size=md
3234
* Example: /api/image-proxy?url=https://example.com/image.jpg&size=md
3335
*/
3436
export async function GET(request: NextRequest) {
3537
const { searchParams } = new URL(request.url);
3638
const url = searchParams.get("url");
37-
const sizeParam = searchParams.get("size") as ImageSize | null;
39+
const sizeParam = resolveRequestedImageSize(
40+
searchParams.get("size"),
41+
searchParams.get("w")
42+
);
3843

3944
console.log("[image-proxy] Request - url:", url, "size:", sizeParam);
4045

@@ -46,7 +51,7 @@ export async function GET(request: NextRequest) {
4651
}
4752

4853
// Prevent recursive proxying - reject if URL is already a proxy URL
49-
if (url.includes("/api/image-proxy") || url.includes("/api/discord-image-proxy")) {
54+
if (url.includes("/api/image-proxy")) {
5055
console.log("[image-proxy] Rejected recursive proxy attempt:", url);
5156
return NextResponse.json(
5257
{ error: "Cannot proxy a proxy URL" },
@@ -56,7 +61,17 @@ export async function GET(request: NextRequest) {
5661

5762
// Check if this is a local data path
5863
if (url.startsWith("/data/")) {
59-
return handleLocalDataPath(request, url, sizeParam);
64+
return handleLocalPath(request, url, sizeParam, {
65+
rootDir: DATA_DIR,
66+
urlPrefix: "/data/",
67+
});
68+
}
69+
70+
if (url.startsWith("/images/")) {
71+
return handleLocalPath(request, url, sizeParam, {
72+
rootDir: path.join(process.cwd(), "public"),
73+
urlPrefix: "/",
74+
});
6075
}
6176

6277
// Otherwise, handle as external URL
@@ -66,27 +81,31 @@ export async function GET(request: NextRequest) {
6681
}
6782

6883
/**
69-
* Handle local data directory paths
84+
* Handle local file paths rooted in a specific directory
7085
*/
71-
async function handleLocalDataPath(
86+
async function handleLocalPath(
7287
request: NextRequest,
7388
localPath: string,
74-
sizeParam: ImageSize | null
89+
sizeParam: ImageSize | null,
90+
options: {
91+
rootDir: string;
92+
urlPrefix: string;
93+
}
7594
): Promise<NextResponse> {
7695
try {
77-
const dataDir = DATA_DIR;
78-
// Remove leading /data/ and construct absolute path
79-
const relativePath = localPath.replace(/^\/data\//, "");
80-
const absolutePath = path.join(dataDir, relativePath);
96+
const { rootDir, urlPrefix } = options;
97+
const normalizedPrefix = urlPrefix.endsWith("/") ? urlPrefix : `${urlPrefix}/`;
98+
const relativePath = localPath.replace(new RegExp(`^${normalizedPrefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`), "");
99+
const absolutePath = path.join(rootDir, relativePath);
81100

82101
console.log("[image-proxy] Serving local file:", absolutePath);
83102

84-
// Security check - ensure path is within data directory
85-
const resolvedDataDir = path.resolve(dataDir);
103+
// Security check - ensure path is within the configured root directory
104+
const resolvedRootDir = path.resolve(rootDir);
86105
const resolvedPath = path.resolve(absolutePath);
87-
if (!resolvedPath.startsWith(resolvedDataDir)) {
106+
if (!resolvedPath.startsWith(resolvedRootDir)) {
88107
console.log("[image-proxy] Security: Path outside data directory:", resolvedPath);
89-
console.log("[image-proxy] Expected to be within:", resolvedDataDir);
108+
console.log("[image-proxy] Expected to be within:", resolvedRootDir);
90109
return NextResponse.json({ error: "Invalid path" }, { status: 403 });
91110
}
92111

@@ -105,6 +124,7 @@ async function handleLocalDataPath(
105124
".png": "image/png",
106125
".gif": "image/gif",
107126
".webp": "image/webp",
127+
".avif": "image/avif",
108128
};
109129
let contentType = contentTypeMap[ext] || "image/jpeg";
110130

0 commit comments

Comments
 (0)