first commit
This commit is contained in:
67
app/api/asc/localizations/route.ts
Normal file
67
app/api/asc/localizations/route.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { appleApiRequest } from "@/app/asc/api";
|
||||
|
||||
// GET /api/asc/localizations?versionId=xxx
|
||||
export async function GET(req: NextRequest) {
|
||||
const versionId = req.nextUrl.searchParams.get("versionId");
|
||||
if (!versionId) return NextResponse.json({ error: "versionId gerekli" }, { status: 400 });
|
||||
|
||||
const result = await appleApiRequest(
|
||||
`/appStoreVersions/${versionId}/appStoreVersionLocalizations?fields[appStoreVersionLocalizations]=locale,description,keywords,whatsNew,promotionalText,marketingUrl,supportUrl`
|
||||
);
|
||||
|
||||
if (result.error) return NextResponse.json({ error: result.error }, { status: 502 });
|
||||
return NextResponse.json(result.data);
|
||||
}
|
||||
|
||||
// PATCH /api/asc/localizations { localizationId, payload }
|
||||
export async function PATCH(req: NextRequest) {
|
||||
const body = await req.json();
|
||||
const { localizationId, payload } = body ?? {};
|
||||
|
||||
if (!localizationId) return NextResponse.json({ error: "localizationId gerekli" }, { status: 400 });
|
||||
|
||||
// Apple API does not allow 'locale' in UPDATE requests (409 ENTITY_ERROR.ATTRIBUTE.NOT_ALLOWED)
|
||||
const { locale: _locale, ...attributes } = payload ?? {};
|
||||
|
||||
const result = await appleApiRequest(`/appStoreVersionLocalizations/${localizationId}`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify({
|
||||
data: {
|
||||
type: "appStoreVersionLocalizations",
|
||||
id: localizationId,
|
||||
attributes,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
if (result.error) return NextResponse.json({ error: result.error }, { status: 502 });
|
||||
return NextResponse.json(result.data);
|
||||
}
|
||||
|
||||
// POST /api/asc/localizations { versionId, locale }
|
||||
export async function POST(req: NextRequest) {
|
||||
const body = await req.json();
|
||||
const { versionId, locale } = body ?? {};
|
||||
|
||||
if (!versionId || !locale)
|
||||
return NextResponse.json({ error: "versionId ve locale gerekli" }, { status: 400 });
|
||||
|
||||
const result = await appleApiRequest(`/appStoreVersionLocalizations`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
data: {
|
||||
type: "appStoreVersionLocalizations",
|
||||
attributes: { locale },
|
||||
relationships: {
|
||||
appStoreVersion: {
|
||||
data: { type: "appStoreVersions", id: versionId },
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
if (result.error) return NextResponse.json({ error: result.error }, { status: 502 });
|
||||
return NextResponse.json(result.data, { status: 201 });
|
||||
}
|
||||
19
app/api/asc/review-response/route.ts
Normal file
19
app/api/asc/review-response/route.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { respondToReview } from "@/app/asc/actions";
|
||||
|
||||
// POST /api/asc/review-response { reviewId, responseBody }
|
||||
export async function POST(req: NextRequest) {
|
||||
const body = await req.json();
|
||||
const { reviewId, responseBody } = body ?? {};
|
||||
|
||||
if (!reviewId || !responseBody?.trim()) {
|
||||
return NextResponse.json({ error: "reviewId ve responseBody gerekli" }, { status: 400 });
|
||||
}
|
||||
|
||||
const result = await respondToReview(reviewId, responseBody.trim());
|
||||
|
||||
if (result.error) {
|
||||
return NextResponse.json({ error: result.error }, { status: 502 });
|
||||
}
|
||||
return NextResponse.json({ ok: true });
|
||||
}
|
||||
24
app/api/asc/screenshots/commit/route.ts
Normal file
24
app/api/asc/screenshots/commit/route.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { appleApiRequest } from "@/app/asc/api";
|
||||
|
||||
// PATCH /api/asc/screenshots/commit { screenshotId, md5, ... }
|
||||
// Step 3: Commit the uploaded screenshot to mark upload as complete
|
||||
export async function PATCH(req: NextRequest) {
|
||||
const body = await req.json();
|
||||
const { screenshotId } = body ?? {};
|
||||
if (!screenshotId) return NextResponse.json({ error: "screenshotId gerekli" }, { status: 400 });
|
||||
|
||||
const result = await appleApiRequest(`/appScreenshots/${screenshotId}`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify({
|
||||
data: {
|
||||
type: "appScreenshots",
|
||||
id: screenshotId,
|
||||
attributes: { uploaded: true },
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
if (result.error) return NextResponse.json({ error: result.error }, { status: 502 });
|
||||
return NextResponse.json(result.data);
|
||||
}
|
||||
97
app/api/asc/screenshots/route.ts
Normal file
97
app/api/asc/screenshots/route.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { appleApiRequest } from "@/app/asc/api";
|
||||
|
||||
const DISPLAY_TYPE = "APP_IPHONE_65"; // iPhone 6.5" (6.7" Super Retina)
|
||||
|
||||
// GET /api/asc/screenshots?localizationId=xxx
|
||||
// Returns the APP_IPHONE_65 screenshot set and its screenshots
|
||||
export async function GET(req: NextRequest) {
|
||||
const locId = req.nextUrl.searchParams.get("localizationId");
|
||||
if (!locId) return NextResponse.json({ error: "localizationId gerekli" }, { status: 400 });
|
||||
|
||||
// 1. Fetch screenshot sets for this localization
|
||||
const setsResult = await appleApiRequest(
|
||||
`/appStoreVersionLocalizations/${locId}/appScreenshotSets?filter[screenshotDisplayType]=${DISPLAY_TYPE}&include=appScreenshots&limit=1`
|
||||
);
|
||||
if (setsResult.error) return NextResponse.json({ error: setsResult.error }, { status: 502 });
|
||||
|
||||
const sets = setsResult.data?.data ?? [];
|
||||
if (sets.length === 0) {
|
||||
return NextResponse.json({ set: null, screenshots: [] });
|
||||
}
|
||||
|
||||
const set = sets[0];
|
||||
// screenshots are included via ?include=appScreenshots
|
||||
const included = setsResult.data?.included ?? [];
|
||||
const screenshots = included.filter((r: { type: string }) => r.type === "appScreenshots");
|
||||
|
||||
return NextResponse.json({ set, screenshots });
|
||||
}
|
||||
|
||||
// POST /api/asc/screenshots/reserve { localizationId, fileName, fileSize, md5 }
|
||||
// Step 1: Creates screenshot set if needed, then creates the upload reservation
|
||||
export async function POST(req: NextRequest) {
|
||||
const body = await req.json();
|
||||
const { localizationId, fileName, fileSize, md5 } = body ?? {};
|
||||
|
||||
if (!localizationId || !fileName || !fileSize)
|
||||
return NextResponse.json({ error: "localizationId, fileName, fileSize gerekli" }, { status: 400 });
|
||||
|
||||
// 1. Find or create the APP_IPHONE_65 screenshot set
|
||||
const setsResult = await appleApiRequest(
|
||||
`/appStoreVersionLocalizations/${localizationId}/appScreenshotSets?filter[screenshotDisplayType]=${DISPLAY_TYPE}&limit=1`
|
||||
);
|
||||
if (setsResult.error) return NextResponse.json({ error: setsResult.error }, { status: 502 });
|
||||
|
||||
let setId: string;
|
||||
if ((setsResult.data?.data ?? []).length > 0) {
|
||||
setId = setsResult.data.data[0].id;
|
||||
} else {
|
||||
// Create the set
|
||||
const createSet = await appleApiRequest(`/appScreenshotSets`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
data: {
|
||||
type: "appScreenshotSets",
|
||||
attributes: { screenshotDisplayType: DISPLAY_TYPE },
|
||||
relationships: {
|
||||
appStoreVersionLocalization: {
|
||||
data: { type: "appStoreVersionLocalizations", id: localizationId },
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
if (createSet.error) return NextResponse.json({ error: createSet.error }, { status: 502 });
|
||||
setId = createSet.data.data.id;
|
||||
}
|
||||
|
||||
// 2. Create screenshot upload reservation
|
||||
const reserveResult = await appleApiRequest(`/appScreenshots`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
data: {
|
||||
type: "appScreenshots",
|
||||
attributes: { fileName, fileSize, ...(md5 ? { md5 } : {}) },
|
||||
relationships: {
|
||||
appScreenshotSet: {
|
||||
data: { type: "appScreenshotSets", id: setId },
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
if (reserveResult.error) return NextResponse.json({ error: reserveResult.error }, { status: 502 });
|
||||
return NextResponse.json(reserveResult.data, { status: 201 });
|
||||
}
|
||||
|
||||
// DELETE /api/asc/screenshots?screenshotId=xxx
|
||||
export async function DELETE(req: NextRequest) {
|
||||
const screenshotId = req.nextUrl.searchParams.get("screenshotId");
|
||||
if (!screenshotId) return NextResponse.json({ error: "screenshotId gerekli" }, { status: 400 });
|
||||
|
||||
const result = await appleApiRequest(`/appScreenshots/${screenshotId}`, { method: "DELETE" });
|
||||
if (result.error) return NextResponse.json({ error: result.error }, { status: 502 });
|
||||
return new NextResponse(null, { status: 204 });
|
||||
}
|
||||
76
app/api/config/[slug]/route.ts
Normal file
76
app/api/config/[slug]/route.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { db } from "@/db";
|
||||
import { apps, remoteConfig } from "@/db/schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
/**
|
||||
* GET /api/config/:slug
|
||||
*
|
||||
* :slug → bundle ID (önerilen) örn: com.sirket.uygulama
|
||||
* → numeric app ID (geriye dönük uyumluluk) örn: 4
|
||||
*
|
||||
* Kimlik doğrulama — iki yöntemden biri:
|
||||
* Authorization: Bearer <CONFIG_API_TOKEN>
|
||||
* ?token=<CONFIG_API_TOKEN>
|
||||
*/
|
||||
export async function GET(
|
||||
request: Request,
|
||||
{ params }: { params: Promise<{ slug: string }> }
|
||||
) {
|
||||
// ── Auth ──────────────────────────────────────────────────────────────
|
||||
const apiToken = process.env.CONFIG_API_TOKEN;
|
||||
if (apiToken) {
|
||||
const authHeader = request.headers.get("Authorization") ?? "";
|
||||
const url = new URL(request.url);
|
||||
const queryToken = url.searchParams.get("token") ?? "";
|
||||
const bearerToken = authHeader.startsWith("Bearer ")
|
||||
? authHeader.slice(7)
|
||||
: "";
|
||||
|
||||
if ((bearerToken || queryToken) !== apiToken) {
|
||||
return NextResponse.json(
|
||||
{ error: "Unauthorized" },
|
||||
{
|
||||
status: 401,
|
||||
headers: { "WWW-Authenticate": 'Bearer realm="config"' },
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Lookup ────────────────────────────────────────────────────────────
|
||||
const { slug } = await params;
|
||||
const numericId = parseInt(slug);
|
||||
const isNumeric = !isNaN(numericId) && String(numericId) === slug;
|
||||
|
||||
const app = await db.query.apps.findFirst({
|
||||
where: isNumeric
|
||||
? eq(apps.id, numericId)
|
||||
: eq(apps.bundleId, slug),
|
||||
});
|
||||
|
||||
if (!app) {
|
||||
return NextResponse.json({ error: "App not found" }, { status: 404 });
|
||||
}
|
||||
|
||||
// Numeric ID ile gelindiyse bundle ID'li URL'e kalıcı yönlendir
|
||||
if (isNumeric) {
|
||||
const url = new URL(request.url);
|
||||
url.pathname = `/api/config/${app.bundleId}`;
|
||||
return NextResponse.redirect(url.toString(), 301);
|
||||
}
|
||||
|
||||
// ── Config ────────────────────────────────────────────────────────────
|
||||
const configs = await db.query.remoteConfig.findMany({
|
||||
where: eq(remoteConfig.appId, app.id),
|
||||
});
|
||||
|
||||
const configObject = configs.reduce<Record<string, unknown>>((acc, c) => {
|
||||
acc[c.configKey] = c.configValue;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return NextResponse.json(configObject, {
|
||||
headers: { "Cache-Control": "no-store" },
|
||||
});
|
||||
}
|
||||
21
app/api/items/[key]/route.ts
Normal file
21
app/api/items/[key]/route.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { db } from "@/db";
|
||||
import { items } from "@/db/schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function GET(
|
||||
request: Request,
|
||||
{ params }: { params: Promise<{ key: string }> }
|
||||
) {
|
||||
const { key } = await params;
|
||||
|
||||
const item = await db.query.items.findFirst({
|
||||
where: eq(items.key, key),
|
||||
});
|
||||
|
||||
if (!item) {
|
||||
return NextResponse.json({ error: "Item not found" }, { status: 404 });
|
||||
}
|
||||
|
||||
return NextResponse.json(item);
|
||||
}
|
||||
Reference in New Issue
Block a user