Skip to content

Commit adb9095

Browse files
authored
Merge pull request #2327 from tekdi/feat-prod-fix
Feat prod fix to 13 prod
2 parents 8af3825 + 4168ce1 commit adb9095

File tree

2 files changed

+92
-87
lines changed

2 files changed

+92
-87
lines changed

apps/learner-web-app/service-account.ts

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

apps/learner-web-app/src/app/api/analytics/pageviews/route.ts

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,86 @@ import "server-only";
22

33
import { NextRequest, NextResponse } from "next/server";
44
import { BetaAnalyticsDataClient } from "@google-analytics/data";
5-
import serviceAccount from "../../../../../service-account";
5+
// import serviceAccount from "../../../../utils/service-account";
6+
interface ServiceAccountConfig {
7+
type?: string;
8+
project_id?: string;
9+
private_key_id?: string;
10+
private_key?: string;
11+
client_email?: string;
12+
client_id?: string;
13+
auth_uri?: string;
14+
token_uri?: string;
15+
auth_provider_x509_cert_url?: string;
16+
client_x509_cert_url?: string;
17+
universe_domain?: string;
18+
}
19+
20+
let decodedFromB64: ServiceAccountConfig | null = null;
21+
22+
if (process.env.GOOGLE_SERVICE_ACCOUNT_B64) {
23+
try {
24+
decodedFromB64 = JSON.parse(
25+
Buffer.from(process.env.GOOGLE_SERVICE_ACCOUNT_B64, "base64").toString(
26+
"utf8"
27+
)
28+
);
29+
} catch (error) {
30+
console.error("Failed to decode GOOGLE_SERVICE_ACCOUNT_B64:", error);
31+
}
32+
}
33+
34+
// Helper function to properly format private key
35+
function formatPrivateKey(key: string | undefined): string | undefined {
36+
if (!key) return undefined;
37+
38+
// First, handle the case where the key might be wrapped in quotes
39+
let formatted = key.replace(/^"|"$/g, '');
40+
41+
// If it's already properly formatted (has newlines), just return it
42+
if (formatted.includes('\n') && formatted.includes('-----BEGIN PRIVATE KEY-----')) {
43+
return formatted;
44+
}
45+
46+
// Handle literal "\n" strings (common in .env files)
47+
if (formatted.includes('\\n')) {
48+
formatted = formatted.replace(/\\n/g, '\n');
49+
} else {
50+
// If no newlines and no literal "\n", it might be a single line string
51+
// Try to insert newlines around headers if they exist but are on the same line
52+
formatted = formatted
53+
.replace('-----BEGIN PRIVATE KEY-----', '-----BEGIN PRIVATE KEY-----\n')
54+
.replace('-----END PRIVATE KEY-----', '\n-----END PRIVATE KEY-----');
55+
56+
// If the body is still one long string, we might need to chunk it (PEM format usually requires 64 char lines)
57+
// However, Node's crypto usually handles single-line bodies if headers are correct.
58+
// Let's at least ensure headers are on their own lines.
59+
}
60+
61+
// Remove any extra whitespace but preserve the structure
62+
formatted = formatted.trim();
63+
64+
return formatted;
65+
}
66+
67+
const serviceAccount = decodedFromB64 ?? {
68+
type: process.env.GOOGLE_TYPE,
69+
project_id: process.env.GOOGLE_PROJECT_ID,
70+
private_key_id: process.env.GOOGLE_PRIVATE_KEY_ID,
71+
private_key: formatPrivateKey(process.env.GOOGLE_PRIVATE_KEY),
72+
client_email: process.env.GOOGLE_CLIENT_EMAIL,
73+
client_id: process.env.GOOGLE_CLIENT_ID,
74+
auth_uri: process.env.GOOGLE_AUTH_URI,
75+
token_uri: process.env.GOOGLE_TOKEN_URI,
76+
auth_provider_x509_cert_url: process.env.GOOGLE_AUTH_PROVIDER_CERT_URL,
77+
client_x509_cert_url: process.env.GOOGLE_CLIENT_CERT_URL,
78+
universe_domain: process.env.GOOGLE_UNIVERSE_DOMAIN,
79+
};
80+
81+
// Ensure private_key is properly formatted even if decoded from base64
82+
if (serviceAccount.private_key && typeof serviceAccount.private_key === "string") {
83+
serviceAccount.private_key = formatPrivateKey(serviceAccount.private_key);
84+
}console.log("Analytics Route Module Loading...");
685

786
const PROPERTY_ID = (process.env.GOOGLE_ANALYTICS_PROPERTY_ID || "496861567").replace(/['"]/g, '');
887

@@ -64,7 +143,19 @@ export const dynamic = "force-dynamic";
64143

65144
export async function GET(request: NextRequest) {
66145
try {
146+
console.log("Analytics Route Handler Called");
67147
const { searchParams } = new URL(request.url);
148+
149+
// Health check for debugging deployment
150+
if (searchParams.get("test") === "true") {
151+
return NextResponse.json({
152+
status: "ok",
153+
message: "Analytics route is reachable",
154+
hasServiceAccount: !!serviceAccount,
155+
hasClientEmail: !!serviceAccount?.client_email
156+
});
157+
}
158+
68159
const pagePath = searchParams.get("path");
69160
const startDateParam = searchParams.get("startDate");
70161
const endDateParam = searchParams.get("endDate");
@@ -174,5 +265,3 @@ dimensionFilter: {
174265
return NextResponse.json({ error: message }, { status: 500 });
175266
}
176267
}
177-
178-

0 commit comments

Comments
 (0)