8 Commits

Author SHA1 Message Date
dstrawsb 4989bf6de4 upgraded to next 15 and react 19.
CI / test (push) Successful in 5s
2026-01-30 22:22:41 -05:00
dstrawsb 4267c34399 package.json changes
CI / test (push) Successful in 5s
2026-01-29 22:45:10 -05:00
dstrawsb 6d1697e672 small name change
CI / test (push) Successful in 6s
2026-01-29 22:40:30 -05:00
dstrawsb 45270f9b18 fixes to admin using client fetch
CI / test (push) Successful in 7s
2026-01-29 21:52:12 -05:00
sean a13687635b middleware fixes
CI / test (push) Successful in 5s
2026-01-27 18:57:08 -05:00
sean c2600b6a68 fix env and middleware
CI / test (push) Successful in 5s
2026-01-27 13:40:49 -05:00
sean b45493f5bd fixed umami
CI / test (push) Successful in 5s
2026-01-27 12:54:16 -05:00
sean cb46430ce4 fixed login auth issue
CI / test (push) Successful in 5s
2026-01-27 09:23:38 -05:00
86 changed files with 3251 additions and 523 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
NEXT_PUBLIC_API_BASE_URL=http://battlbuilder-api:8080 NEXT_PUBLIC_API_BASE_URL=http://battlbuilder-api:8080
# Middleware to limited site to only root page # Middleware to limited site to only root page
NEXT_PUBLIC_LAUNCH_ONLY_ROOT=true LAUNCH_ONLY_ROOT=true
NEXT_PUBLIC_SHORTLINK_BASE_URL=https://battl.build NEXT_PUBLIC_SHORTLINK_BASE_URL=https://battl.build
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="db-tree-configuration">
<option name="data" value="" />
</component>
</project>
+2 -2
View File
@@ -417,9 +417,9 @@ export default function PrivacyPolicy() {
<br /> <br />
Battl Builder, LLC. Battl Builder, LLC.
<br /> <br />
[Your Address] {/* [Your Address]
<br /> <br />
[City, State, ZIP] [City, State, ZIP] */}
</p> </p>
</div> </div>
<p className="mt-4 text-sm"> <p className="mt-4 text-sm">
+4 -6
View File
@@ -3,7 +3,6 @@
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { Plus, Pencil, Trash2, X } from "lucide-react"; import { Plus, Pencil, Trash2, X } from "lucide-react";
import { useAuth } from "@/context/AuthContext";
import { import {
createPlatform, createPlatform,
deletePlatform, deletePlatform,
@@ -15,7 +14,6 @@ import {
} from "@/lib/api/platforms"; } from "@/lib/api/platforms";
export default function AdminPlatformsPage() { export default function AdminPlatformsPage() {
const { getAuthHeaders } = useAuth();
const [platforms, setPlatforms] = useState<Platform[]>([]); const [platforms, setPlatforms] = useState<Platform[]>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
@@ -36,7 +34,7 @@ export default function AdminPlatformsPage() {
try { try {
setLoading(true); setLoading(true);
setError(null); setError(null);
const data = await fetchPlatforms(getAuthHeaders()); const data = await fetchPlatforms({});
setPlatforms(data); setPlatforms(data);
} catch (e: any) { } catch (e: any) {
console.error(e); console.error(e);
@@ -77,7 +75,7 @@ export default function AdminPlatformsPage() {
try { try {
setSaving(true); setSaving(true);
setError(null); setError(null);
await deletePlatform(getAuthHeaders(), p.id); await deletePlatform({}, p.id);
await load(); await load();
} catch (e: any) { } catch (e: any) {
console.error(e); console.error(e);
@@ -103,7 +101,7 @@ export default function AdminPlatformsPage() {
setError(null); setError(null);
if (modalMode === "create") { if (modalMode === "create") {
await createPlatform(getAuthHeaders(), { ...form, key, label }); await createPlatform({}, { ...form, key, label });
} else if (selected) { } else if (selected) {
const update: UpdatePlatformDto = {}; const update: UpdatePlatformDto = {};
if (key !== selected.key) update.key = key; if (key !== selected.key) update.key = key;
@@ -111,7 +109,7 @@ export default function AdminPlatformsPage() {
if ((form.isActive ?? true) !== selected.isActive) if ((form.isActive ?? true) !== selected.isActive)
update.isActive = form.isActive; update.isActive = form.isActive;
await updatePlatform(getAuthHeaders(), selected.id, update); await updatePlatform({}, selected.id, update);
} }
setShowModal(false); setShowModal(false);
+30 -29
View File
@@ -1,4 +1,4 @@
import { API_BASE, getToken, qs } from "./auth"; import { API_BASE, qs } from "./auth";
import type { import type {
AdminProductRow, AdminProductRow,
PageResponse, PageResponse,
@@ -9,25 +9,14 @@ import type {
import type { CaliberDto } from "./types"; import type { CaliberDto } from "./types";
async function authedFetch(url: string, init?: RequestInit) {
const token = getToken();
const res = await fetch(url, {
credentials: "include",
cache: "no-store",
...(init ?? {}),
headers: {
Accept: "application/json",
...(init?.headers ?? {}),
...(token ? { Authorization: `Bearer ${token}` } : {}),
} as any,
});
return res;
}
export async function fetchAdminRoles(params: Record<string, any>) { export async function fetchAdminRoles(params: Record<string, any>) {
const res = await authedFetch( const queryString = qs(params);
`${API_BASE}/api/v1/admin/products/roles?${qs(params)}` const res = await fetch(
`/api/admin/products/roles${queryString ? `?${queryString}` : ''}`,
{
credentials: "include",
cache: "no-store",
}
); );
if (!res.ok) throw new Error(`Failed to load roles (${res.status}): ${await res.text()}`); if (!res.ok) throw new Error(`Failed to load roles (${res.status}): ${await res.text()}`);
return (await res.json()) as string[]; return (await res.json()) as string[];
@@ -35,8 +24,13 @@ export async function fetchAdminRoles(params: Record<string, any>) {
// Facet/filtering can still use distinct values seen on products (optional) // Facet/filtering can still use distinct values seen on products (optional)
export async function fetchProductCaliberFacet(params: Record<string, any>) { export async function fetchProductCaliberFacet(params: Record<string, any>) {
const res = await authedFetch( const queryString = qs(params);
`${API_BASE}/api/v1/admin/products/calibers?${qs(params)}` const res = await fetch(
`/api/admin/products/calibers${queryString ? `?${queryString}` : ''}`,
{
credentials: "include",
cache: "no-store",
}
); );
if (!res.ok) if (!res.ok)
throw new Error( throw new Error(
@@ -46,8 +40,13 @@ export async function fetchProductCaliberFacet(params: Record<string, any>) {
} }
export async function fetchAdminProducts(params: Record<string, any>) { export async function fetchAdminProducts(params: Record<string, any>) {
const res = await authedFetch( const queryString = qs(params);
`${API_BASE}/api/v1/admin/products?${qs(params)}` const res = await fetch(
`/api/admin/products${queryString ? `?${queryString}` : ''}`,
{
credentials: "include",
cache: "no-store",
}
); );
if (!res.ok) throw new Error(`Failed to load admin products (${res.status}): ${await res.text()}`); if (!res.ok) throw new Error(`Failed to load admin products (${res.status}): ${await res.text()}`);
return (await res.json()) as PageResponse<AdminProductRow>; return (await res.json()) as PageResponse<AdminProductRow>;
@@ -84,14 +83,11 @@ export async function bulkUpdate(
classificationReason: "Admin bulk override"; classificationReason: "Admin bulk override";
}> }>
) { ) {
const token = getToken(); const res = await fetch(`/api/admin/products/bulk`, {
const res = await fetch(`${API_BASE}/api/v1/admin/products/bulk`, {
method: "PATCH", method: "PATCH",
credentials: "include", credentials: "include",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
...(token ? { Authorization: `Bearer ${token}` } : {}),
}, },
body: JSON.stringify({ productIds, ...set }), body: JSON.stringify({ productIds, ...set }),
}); });
@@ -103,8 +99,13 @@ export async function bulkUpdate(
export async function fetchAdminCalibers(params: Record<string, any>) { export async function fetchAdminCalibers(params: Record<string, any>) {
const res = await authedFetch( const queryString = qs(params);
`${API_BASE}/api/v1/admin/calibers?${qs(params)}` const res = await fetch(
`/api/admin/calibers${queryString ? `?${queryString}` : ''}`,
{
credentials: "include",
cache: "no-store",
}
); );
if (!res.ok) { if (!res.ok) {
throw new Error(`Failed to load calibers (${res.status}): ${await res.text()}`); throw new Error(`Failed to load calibers (${res.status}): ${await res.text()}`);
+2 -1
View File
@@ -17,11 +17,12 @@ export default function AdminUsersPage() {
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
async function fetchUsers() { async function fetchUsers() {
console.log("in the users fetch");
try { try {
setLoading(true); setLoading(true);
setError(null); setError(null);
const res = await fetch(`${API_BASE_URL}/admin/users`, { const res = await fetch(`/api/admin/users`, {
headers: { headers: {
...getAuthHeaders(), ...getAuthHeaders(),
}, },
+3 -1
View File
@@ -6,7 +6,9 @@ const API_BASE_URL =
// POST /api/admin/beta-invites?dryRun=true&limit=10&tokenMinutes=30 // POST /api/admin/beta-invites?dryRun=true&limit=10&tokenMinutes=30
export async function POST(req: Request) { export async function POST(req: Request) {
const token = cookies().get("session_token")?.value; const cookieStore = await cookies();
const token = cookieStore.get("session_token")?.value;
if (!token) { if (!token) {
return NextResponse.json({ error: "Missing admin token" }, { status: 401 }); return NextResponse.json({ error: "Missing admin token" }, { status: 401 });
+35
View File
@@ -0,0 +1,35 @@
import { NextResponse } from "next/server";
import { cookies } from "next/headers";
const API_BASE_URL =
process.env.NEXT_PUBLIC_API_BASE_URL ?? "";
// POST /api/admin/beta-invites?dryRun=true&limit=10&tokenMinutes=30
export async function POST(req: Request) {
const token = cookies().get("session_token")?.value;
if (!token) {
return NextResponse.json({ error: "Missing admin token" }, { status: 401 });
}
const url = new URL(req.url);
const dryRun = url.searchParams.get("dryRun") ?? "true";
const limit = url.searchParams.get("limit") ?? "0";
const tokenMinutes = url.searchParams.get("tokenMinutes") ?? "30";
const upstream = `${API_BASE_URL}/api/v1/admin/beta/invites/send?dryRun=${dryRun}&limit=${limit}&tokenMinutes=${tokenMinutes}`;
const res = await fetch(upstream, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
},
cache: "no-store",
});
const text = await res.text();
return new NextResponse(text, {
status: res.status,
headers: { "Content-Type": res.headers.get("content-type") ?? "application/json" },
});
}
@@ -5,19 +5,21 @@ const API_BASE_URL =
process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL; process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() { async function getAuthHeader() {
const token = cookies().get("session_token")?.value; const cookieStore = await cookies();
const token = cookieStore.get("session_token")?.value;
if (!token) throw new Error("Unauthorized"); if (!token) throw new Error("Unauthorized");
return { Authorization: `Bearer ${token}` }; return { Authorization: `Bearer ${token}` };
} }
export async function POST( export async function POST(
_req: Request, _req: Request,
{ params }: { params: { id: string } } { params }: { params: Promise<{ id: string }> }
) { ) {
try { try {
const resolvedParams = await params;
const headers = await getAuthHeader(); const headers = await getAuthHeader();
const res = await fetch( const res = await fetch(
`${API_BASE_URL}/api/v1/admin/beta/requests/${params.id}/invite`, `${API_BASE_URL}/api/v1/admin/beta/requests/${resolvedParams.id}/invite`,
{ {
method: "POST", method: "POST",
headers, headers,
@@ -0,0 +1,39 @@
import { NextResponse } from "next/server";
import { cookies } from "next/headers";
const API_BASE_URL =
process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const token = cookies().get("session_token")?.value;
if (!token) throw new Error("Unauthorized");
return { Authorization: `Bearer ${token}` };
}
export async function POST(
_req: Request,
{ params }: { params: { id: string } }
) {
try {
const headers = await getAuthHeader();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/beta/requests/${params.id}/invite`,
{
method: "POST",
headers,
cache: "no-store",
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
}
+2 -1
View File
@@ -5,7 +5,8 @@ const API_BASE_URL =
process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL; process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() { async function getAuthHeader() {
const token = cookies().get("session_token")?.value; const cookieStore = await cookies();
const token = cookieStore.get("session_token")?.value;
if (!token) throw new Error("Unauthorized"); if (!token) throw new Error("Unauthorized");
return { Authorization: `Bearer ${token}` }; return { Authorization: `Bearer ${token}` };
} }
+39
View File
@@ -0,0 +1,39 @@
import { NextResponse } from "next/server";
import { cookies } from "next/headers";
const API_BASE_URL =
process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const token = cookies().get("session_token")?.value;
if (!token) throw new Error("Unauthorized");
return { Authorization: `Bearer ${token}` };
}
export async function GET(req: Request) {
try {
const headers = await getAuthHeader();
const url = new URL(req.url);
const search = url.searchParams.toString();
const upstream = `${API_BASE_URL}/api/v1/admin/beta/requests${
search ? `?${search}` : ""
}`;
const res = await fetch(upstream, {
headers,
cache: "no-store",
});
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
}
+42
View File
@@ -0,0 +1,42 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
export async function GET(request: NextRequest) {
try {
const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const searchParams = request.nextUrl.searchParams;
const queryString = searchParams.toString();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/calibers${queryString ? `?${queryString}` : ''}`,
{
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/json',
},
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Internal server error' },
{ status: 500 }
);
}
}
+40
View File
@@ -0,0 +1,40 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
export async function GET(request: NextRequest) {
try {
const token = cookies().get('session_token')?.value;
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const searchParams = request.nextUrl.searchParams;
const queryString = searchParams.toString();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/calibers${queryString ? `?${queryString}` : ''}`,
{
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/json',
},
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Internal server error' },
{ status: 500 }
);
}
}
+2 -1
View File
@@ -4,7 +4,8 @@ import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL; const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() { async function getAuthHeader() {
const token = cookies().get('session_token')?.value; const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) throw new Error('Unauthorized'); if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` }; return { Authorization: `Bearer ${token}` };
} }
@@ -0,0 +1,35 @@
import { NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const token = cookies().get('session_token')?.value;
if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` };
}
export async function GET() {
try {
const headers = await getAuthHeader();
const res = await fetch(`${API_BASE_URL}/api/admin/dashboard/overview`, {
headers,
cache: 'no-store',
});
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Unauthorized' },
{ status: 401 }
);
}
}
+8 -5
View File
@@ -5,20 +5,22 @@ const API_BASE_URL =
process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL; process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() { async function getAuthHeader() {
const token = cookies().get("session_token")?.value; const cookieStore = await cookies();
const token = cookieStore.get("session_token")?.value;
if (!token) throw new Error("Unauthorized"); if (!token) throw new Error("Unauthorized");
return { Authorization: `Bearer ${token}` }; return { Authorization: `Bearer ${token}` };
} }
export async function PUT( export async function PUT(
request: NextRequest, request: NextRequest,
{ params }: { params: { id: string } } { params }: { params: Promise<{ id: string }> }
) { ) {
try { try {
const resolvedParams = await params;
const headers = await getAuthHeader(); const headers = await getAuthHeader();
const body = await request.json(); const body = await request.json();
const res = await fetch(`${API_BASE_URL}/api/email/${params.id}`, { const res = await fetch(`${API_BASE_URL}/api/email/${resolvedParams.id}`, {
method: "PUT", method: "PUT",
headers: { ...headers, "Content-Type": "application/json" }, headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify(body), body: JSON.stringify(body),
@@ -39,12 +41,13 @@ export async function PUT(
export async function DELETE( export async function DELETE(
request: NextRequest, request: NextRequest,
{ params }: { params: { id: string } } { params }: { params: Promise<{ id: string }> }
) { ) {
try { try {
const resolvedParams = await params;
const headers = await getAuthHeader(); const headers = await getAuthHeader();
const res = await fetch(`${API_BASE_URL}/api/email/${params.id}`, { const res = await fetch(`${API_BASE_URL}/api/email/${resolvedParams.id}`, {
method: "DELETE", method: "DELETE",
headers, headers,
}); });
+63
View File
@@ -0,0 +1,63 @@
import { NextRequest, NextResponse } from "next/server";
import { cookies } from "next/headers";
const API_BASE_URL =
process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const token = cookies().get("session_token")?.value;
if (!token) throw new Error("Unauthorized");
return { Authorization: `Bearer ${token}` };
}
export async function PUT(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const headers = await getAuthHeader();
const body = await request.json();
const res = await fetch(`${API_BASE_URL}/api/email/${params.id}`, {
method: "PUT",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify(body),
});
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
}
export async function DELETE(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const headers = await getAuthHeader();
const res = await fetch(`${API_BASE_URL}/api/email/${params.id}`, {
method: "DELETE",
headers,
});
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json({ ok: true });
} catch (err: any) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
}
+2 -1
View File
@@ -5,7 +5,8 @@ const API_BASE_URL =
process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL; process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() { async function getAuthHeader() {
const token = cookies().get("session_token")?.value; const cookieStore = await cookies();
const token = cookieStore.get("session_token")?.value;
if (!token) throw new Error("Unauthorized"); if (!token) throw new Error("Unauthorized");
return { Authorization: `Bearer ${token}` }; return { Authorization: `Bearer ${token}` };
} }
+56
View File
@@ -0,0 +1,56 @@
import { NextRequest, NextResponse } from "next/server";
import { cookies } from "next/headers";
const API_BASE_URL =
process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const token = cookies().get("session_token")?.value;
if (!token) throw new Error("Unauthorized");
return { Authorization: `Bearer ${token}` };
}
export async function GET() {
try {
const headers = await getAuthHeader();
const res = await fetch(`${API_BASE_URL}/api/email`, {
headers,
cache: "no-store",
});
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
}
export async function POST(request: NextRequest) {
try {
const headers = await getAuthHeader();
const body = await request.json();
const res = await fetch(`${API_BASE_URL}/api/email`, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify(body),
});
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
}
@@ -0,0 +1,52 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
export async function POST(
request: NextRequest,
{ params }: { params: Promise<{ id: string; action: string }> }
) {
try {
const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const resolvedParams = await params;
const { id, action } = resolvedParams;
const res = await fetch(
`${API_BASE_URL}/api/admin/enrichment/${id}/${action}`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
const contentType = res.headers.get('content-type');
if (contentType?.includes('application/json')) {
return NextResponse.json(await res.json());
}
return NextResponse.json({ success: true });
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Internal server error' },
{ status: 500 }
);
}
}
@@ -0,0 +1,48 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
export async function POST(
request: NextRequest,
{ params }: { params: { id: string; action: string } }
) {
try {
const token = cookies().get('session_token')?.value;
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const { id, action } = params;
const res = await fetch(
`${API_BASE_URL}/api/admin/enrichment/${id}/${action}`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
const contentType = res.headers.get('content-type');
if (contentType?.includes('application/json')) {
return NextResponse.json(await res.json());
}
return NextResponse.json({ success: true });
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Internal server error' },
{ status: 500 }
);
}
}
+48
View File
@@ -0,0 +1,48 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
export async function POST(request: NextRequest) {
try {
const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const searchParams = request.nextUrl.searchParams;
const queryString = searchParams.toString();
const res = await fetch(
`${API_BASE_URL}/api/admin/enrichment/ai/run${queryString ? `?${queryString}` : ''}`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
const contentType = res.headers.get('content-type');
if (contentType?.includes('application/json')) {
return NextResponse.json(await res.json());
}
return NextResponse.json({ success: true });
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Internal server error' },
{ status: 500 }
);
}
}
@@ -0,0 +1,46 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
export async function POST(request: NextRequest) {
try {
const token = cookies().get('session_token')?.value;
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const searchParams = request.nextUrl.searchParams;
const queryString = searchParams.toString();
const res = await fetch(
`${API_BASE_URL}/api/admin/enrichment/ai/run${queryString ? `?${queryString}` : ''}`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
const contentType = res.headers.get('content-type');
if (contentType?.includes('application/json')) {
return NextResponse.json(await res.json());
}
return NextResponse.json({ success: true });
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Internal server error' },
{ status: 500 }
);
}
}
+46
View File
@@ -0,0 +1,46 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
import { fixImageUrls } from '@/lib/server/image-utils';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
export async function GET(request: NextRequest) {
try {
const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const searchParams = request.nextUrl.searchParams;
const queryString = searchParams.toString();
const res = await fetch(
`${API_BASE_URL}/api/admin/enrichment/queue2${queryString ? `?${queryString}` : ''}`,
{
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/json',
},
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
const data = await res.json();
// Fix mixed content warnings: upgrade HTTP image URLs to HTTPS
return NextResponse.json(fixImageUrls(data));
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Internal server error' },
{ status: 500 }
);
}
}
@@ -0,0 +1,44 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
import { fixImageUrls } from '@/lib/server/image-utils';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
export async function GET(request: NextRequest) {
try {
const token = cookies().get('session_token')?.value;
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const searchParams = request.nextUrl.searchParams;
const queryString = searchParams.toString();
const res = await fetch(
`${API_BASE_URL}/api/admin/enrichment/queue2${queryString ? `?${queryString}` : ''}`,
{
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/json',
},
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
const data = await res.json();
// Fix mixed content warnings: upgrade HTTP image URLs to HTTPS
return NextResponse.json(fixImageUrls(data));
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Internal server error' },
{ status: 500 }
);
}
}
+48
View File
@@ -0,0 +1,48 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
export async function POST(request: NextRequest) {
try {
const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const searchParams = request.nextUrl.searchParams;
const queryString = searchParams.toString();
const res = await fetch(
`${API_BASE_URL}/api/admin/enrichment/run${queryString ? `?${queryString}` : ''}`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
const contentType = res.headers.get('content-type');
if (contentType?.includes('application/json')) {
return NextResponse.json(await res.json());
}
return NextResponse.json({ success: true });
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Internal server error' },
{ status: 500 }
);
}
}
+46
View File
@@ -0,0 +1,46 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
export async function POST(request: NextRequest) {
try {
const token = cookies().get('session_token')?.value;
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const searchParams = request.nextUrl.searchParams;
const queryString = searchParams.toString();
const res = await fetch(
`${API_BASE_URL}/api/admin/enrichment/run${queryString ? `?${queryString}` : ''}`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
const contentType = res.headers.get('content-type');
if (contentType?.includes('application/json')) {
return NextResponse.json(await res.json());
}
return NextResponse.json({ success: true });
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Internal server error' },
{ status: 500 }
);
}
}
@@ -4,7 +4,8 @@ import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL; const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() { async function getAuthHeader() {
const token = cookies().get('session_token')?.value; const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) throw new Error('Unauthorized'); if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` }; return { Authorization: `Bearer ${token}` };
} }
@@ -0,0 +1,35 @@
import { NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const token = cookies().get('session_token')?.value;
if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` };
}
export async function GET() {
try {
const headers = await getAuthHeader();
const res = await fetch(`${API_BASE_URL}/api/admin/import-status/by-merchant`, {
headers,
cache: 'no-store',
});
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Unauthorized' },
{ status: 401 }
);
}
}
+2 -1
View File
@@ -4,7 +4,8 @@ import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL; const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() { async function getAuthHeader() {
const token = cookies().get('session_token')?.value; const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) throw new Error('Unauthorized'); if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` }; return { Authorization: `Bearer ${token}` };
} }
@@ -0,0 +1,35 @@
import { NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const token = cookies().get('session_token')?.value;
if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` };
}
export async function GET() {
try {
const headers = await getAuthHeader();
const res = await fetch(`${API_BASE_URL}/api/admin/import-status/summary`, {
headers,
cache: 'no-store',
});
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Unauthorized' },
{ status: 401 }
);
}
}
+2 -1
View File
@@ -4,7 +4,8 @@ import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL; const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() { async function getAuthHeader() {
const token = cookies().get('session_token')?.value; const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) throw new Error('Unauthorized'); if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` }; return { Authorization: `Bearer ${token}` };
} }
+40
View File
@@ -0,0 +1,40 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const token = cookies().get('session_token')?.value;
if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` };
}
export async function POST(request: NextRequest) {
try {
const headers = await getAuthHeader();
const body = await request.json();
const res = await fetch(`${API_BASE_URL}/api/admin/mapping/apply`, {
method: 'POST',
headers: {
...headers,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Unauthorized' },
{ status: 401 }
);
}
}
+2 -1
View File
@@ -4,7 +4,8 @@ import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL; const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() { async function getAuthHeader() {
const token = cookies().get('session_token')?.value; const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) throw new Error('Unauthorized'); if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` }; return { Authorization: `Bearer ${token}` };
} }
@@ -0,0 +1,35 @@
import { NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const token = cookies().get('session_token')?.value;
if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` };
}
export async function GET() {
try {
const headers = await getAuthHeader();
const res = await fetch(`${API_BASE_URL}/api/admin/mapping/options`, {
headers,
cache: 'no-store',
});
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Unauthorized' },
{ status: 401 }
);
}
}
@@ -4,7 +4,8 @@ import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL; const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() { async function getAuthHeader() {
const token = cookies().get('session_token')?.value; const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) throw new Error('Unauthorized'); if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` }; return { Authorization: `Bearer ${token}` };
} }
@@ -0,0 +1,35 @@
import { NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const token = cookies().get('session_token')?.value;
if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` };
}
export async function GET() {
try {
const headers = await getAuthHeader();
const res = await fetch(`${API_BASE_URL}/api/admin/mapping/pending-buckets`, {
headers,
cache: 'no-store',
});
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Unauthorized' },
{ status: 401 }
);
}
}
@@ -4,7 +4,8 @@ import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL; const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() { async function getAuthHeader() {
const token = cookies().get('session_token')?.value; const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) throw new Error('Unauthorized'); if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` }; return { Authorization: `Bearer ${token}` };
} }
@@ -0,0 +1,39 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const token = cookies().get('session_token')?.value;
if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` };
}
export async function GET(request: NextRequest) {
try {
const headers = await getAuthHeader();
const { searchParams } = new URL(request.url);
const res = await fetch(
`${API_BASE_URL}/api/admin/mapping/raw-categories?${searchParams.toString()}`,
{
headers,
cache: 'no-store',
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Unauthorized' },
{ status: 401 }
);
}
}
+2 -1
View File
@@ -4,7 +4,8 @@ import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL; const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() { async function getAuthHeader() {
const token = cookies().get('session_token')?.value; const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) throw new Error('Unauthorized'); if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` }; return { Authorization: `Bearer ${token}` };
} }
+40
View File
@@ -0,0 +1,40 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const token = cookies().get('session_token')?.value;
if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` };
}
export async function POST(request: NextRequest) {
try {
const headers = await getAuthHeader();
const body = await request.json();
const res = await fetch(`${API_BASE_URL}/api/admin/mapping/upsert`, {
method: 'POST',
headers: {
...headers,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Unauthorized' },
{ status: 401 }
);
}
}
+5 -3
View File
@@ -4,20 +4,22 @@ import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL; const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() { async function getAuthHeader() {
const token = cookies().get('session_token')?.value; const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) throw new Error('Unauthorized'); if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` }; return { Authorization: `Bearer ${token}` };
} }
export async function POST( export async function POST(
request: NextRequest, request: NextRequest,
{ params }: { params: { id: string } } { params }: { params: Promise<{ id: string }> }
) { ) {
try { try {
const resolvedParams = await params;
const headers = await getAuthHeader(); const headers = await getAuthHeader();
const res = await fetch( const res = await fetch(
`${API_BASE_URL}/api/v1/admin/merchants/${params.id}/import`, `${API_BASE_URL}/api/v1/admin/merchants/${resolvedParams.id}/import`,
{ method: 'POST', headers } { method: 'POST', headers }
); );
@@ -0,0 +1,35 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const token = cookies().get('session_token')?.value;
if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` };
}
export async function POST(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const headers = await getAuthHeader();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/merchants/${params.id}/import`,
{ method: 'POST', headers }
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
}
@@ -4,20 +4,22 @@ import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL; const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() { async function getAuthHeader() {
const token = cookies().get('session_token')?.value; const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) throw new Error('Unauthorized'); if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` }; return { Authorization: `Bearer ${token}` };
} }
export async function POST( export async function POST(
request: NextRequest, request: NextRequest,
{ params }: { params: { id: string } } { params }: { params: Promise<{ id: string }> }
) { ) {
try { try {
const resolvedParams = await params;
const headers = await getAuthHeader(); const headers = await getAuthHeader();
const res = await fetch( const res = await fetch(
`${API_BASE_URL}/api/v1/admin/merchants/${params.id}/offer-sync`, `${API_BASE_URL}/api/v1/admin/merchants/${resolvedParams.id}/offer-sync`,
{ method: 'POST', headers } { method: 'POST', headers }
); );
@@ -0,0 +1,35 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const token = cookies().get('session_token')?.value;
if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` };
}
export async function POST(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const headers = await getAuthHeader();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/merchants/${params.id}/offer-sync`,
{ method: 'POST', headers }
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
}
+11 -7
View File
@@ -4,20 +4,22 @@ import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL; const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() { async function getAuthHeader() {
const token = cookies().get('session_token')?.value; const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) throw new Error('Unauthorized'); if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` }; return { Authorization: `Bearer ${token}` };
} }
export async function GET( export async function GET(
request: NextRequest, request: NextRequest,
{ params }: { params: { id: string } } { params }: { params: Promise<{ id: string }> }
) { ) {
try { try {
const resolvedParams = await params;
const headers = await getAuthHeader(); const headers = await getAuthHeader();
const res = await fetch( const res = await fetch(
`${API_BASE_URL}/api/v1/admin/merchants/${params.id}`, `${API_BASE_URL}/api/v1/admin/merchants/${resolvedParams.id}`,
{ headers } { headers }
); );
@@ -36,14 +38,15 @@ export async function GET(
export async function PUT( export async function PUT(
request: NextRequest, request: NextRequest,
{ params }: { params: { id: string } } { params }: { params: Promise<{ id: string }> }
) { ) {
try { try {
const resolvedParams = await params;
const headers = await getAuthHeader(); const headers = await getAuthHeader();
const body = await request.json(); const body = await request.json();
const res = await fetch( const res = await fetch(
`${API_BASE_URL}/api/v1/admin/merchants/${params.id}`, `${API_BASE_URL}/api/v1/admin/merchants/${resolvedParams.id}`,
{ {
method: 'PUT', method: 'PUT',
headers: { ...headers, 'Content-Type': 'application/json' }, headers: { ...headers, 'Content-Type': 'application/json' },
@@ -66,13 +69,14 @@ export async function PUT(
export async function DELETE( export async function DELETE(
request: NextRequest, request: NextRequest,
{ params }: { params: { id: string } } { params }: { params: Promise<{ id: string }> }
) { ) {
try { try {
const resolvedParams = await params;
const headers = await getAuthHeader(); const headers = await getAuthHeader();
const res = await fetch( const res = await fetch(
`${API_BASE_URL}/api/v1/admin/merchants/${params.id}`, `${API_BASE_URL}/api/v1/admin/merchants/${resolvedParams.id}`,
{ method: 'DELETE', headers } { method: 'DELETE', headers }
); );
+90
View File
@@ -0,0 +1,90 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const token = cookies().get('session_token')?.value;
if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` };
}
export async function GET(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const headers = await getAuthHeader();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/merchants/${params.id}`,
{ headers }
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
}
export async function PUT(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const headers = await getAuthHeader();
const body = await request.json();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/merchants/${params.id}`,
{
method: 'PUT',
headers: { ...headers, 'Content-Type': 'application/json' },
body: JSON.stringify(body),
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
}
export async function DELETE(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const headers = await getAuthHeader();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/merchants/${params.id}`,
{ method: 'DELETE', headers }
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
}
+2 -1
View File
@@ -4,7 +4,8 @@ import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL; const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() { async function getAuthHeader() {
const token = cookies().get('session_token')?.value; const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) throw new Error('Unauthorized'); if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` }; return { Authorization: `Bearer ${token}` };
} }
+54
View File
@@ -0,0 +1,54 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const token = cookies().get('session_token')?.value;
if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` };
}
export async function GET() {
try {
const headers = await getAuthHeader();
const res = await fetch(`${API_BASE_URL}/api/v1/admin/merchants`, {
headers,
});
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
}
export async function POST(request: NextRequest) {
try {
const headers = await getAuthHeader();
const body = await request.json();
const res = await fetch(`${API_BASE_URL}/api/v1/admin/merchants`, {
method: 'POST',
headers: { ...headers, 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
}
+71
View File
@@ -0,0 +1,71 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` };
}
export async function PATCH(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const resolvedParams = await params;
const headers = await getAuthHeader();
const body = await request.json();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/platforms/${resolvedParams.id}`,
{
method: 'PATCH',
headers: {
...headers,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
}
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const resolvedParams = await params;
const headers = await getAuthHeader();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/platforms/${resolvedParams.id}`,
{ method: 'DELETE', headers }
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json({ ok: true });
} catch (err: any) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
}
+68
View File
@@ -0,0 +1,68 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const token = cookies().get('session_token')?.value;
if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` };
}
export async function PATCH(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const headers = await getAuthHeader();
const body = await request.json();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/platforms/${params.id}`,
{
method: 'PATCH',
headers: {
...headers,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
}
export async function DELETE(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const headers = await getAuthHeader();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/platforms/${params.id}`,
{ method: 'DELETE', headers }
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json({ ok: true });
} catch (err: any) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
}
+73
View File
@@ -0,0 +1,73 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` };
}
export async function GET(request: NextRequest) {
try {
const headers = await getAuthHeader();
const searchParams = request.nextUrl.searchParams;
const queryString = searchParams.toString();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/platforms${queryString ? `?${queryString}` : ''}`,
{
headers: {
...headers,
Accept: 'application/json',
},
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Unauthorized' },
{ status: 401 }
);
}
}
export async function POST(request: NextRequest) {
try {
const headers = await getAuthHeader();
const body = await request.json();
const res = await fetch(`${API_BASE_URL}/api/v1/admin/platforms`, {
method: 'POST',
headers: {
...headers,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Unauthorized' },
{ status: 401 }
);
}
}
+72
View File
@@ -0,0 +1,72 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const token = cookies().get('session_token')?.value;
if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` };
}
export async function GET(request: NextRequest) {
try {
const headers = await getAuthHeader();
const searchParams = request.nextUrl.searchParams;
const queryString = searchParams.toString();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/platforms${queryString ? `?${queryString}` : ''}`,
{
headers: {
...headers,
Accept: 'application/json',
},
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Unauthorized' },
{ status: 401 }
);
}
}
export async function POST(request: NextRequest) {
try {
const headers = await getAuthHeader();
const body = await request.json();
const res = await fetch(`${API_BASE_URL}/api/v1/admin/platforms`, {
method: 'POST',
headers: {
...headers,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Unauthorized' },
{ status: 401 }
);
}
}
+43
View File
@@ -0,0 +1,43 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
export async function PATCH(request: NextRequest) {
try {
const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const body = await request.json();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/products/bulk`,
{
method: 'PATCH',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Internal server error' },
{ status: 500 }
);
}
}
+41
View File
@@ -0,0 +1,41 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
export async function PATCH(request: NextRequest) {
try {
const token = cookies().get('session_token')?.value;
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const body = await request.json();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/products/bulk`,
{
method: 'PATCH',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Internal server error' },
{ status: 500 }
);
}
}
+42
View File
@@ -0,0 +1,42 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
export async function GET(request: NextRequest) {
try {
const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const searchParams = request.nextUrl.searchParams;
const queryString = searchParams.toString();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/products/calibers${queryString ? `?${queryString}` : ''}`,
{
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/json',
},
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Internal server error' },
{ status: 500 }
);
}
}
@@ -0,0 +1,40 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
export async function GET(request: NextRequest) {
try {
const token = cookies().get('session_token')?.value;
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const searchParams = request.nextUrl.searchParams;
const queryString = searchParams.toString();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/products/calibers${queryString ? `?${queryString}` : ''}`,
{
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/json',
},
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Internal server error' },
{ status: 500 }
);
}
}
+42
View File
@@ -0,0 +1,42 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
export async function GET(request: NextRequest) {
try {
const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const searchParams = request.nextUrl.searchParams;
const queryString = searchParams.toString();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/products/roles${queryString ? `?${queryString}` : ''}`,
{
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/json',
},
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Internal server error' },
{ status: 500 }
);
}
}
+40
View File
@@ -0,0 +1,40 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
export async function GET(request: NextRequest) {
try {
const token = cookies().get('session_token')?.value;
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const searchParams = request.nextUrl.searchParams;
const queryString = searchParams.toString();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/products/roles${queryString ? `?${queryString}` : ''}`,
{
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/json',
},
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Internal server error' },
{ status: 500 }
);
}
}
+44
View File
@@ -0,0 +1,44 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
import { fixImageUrls } from '@/lib/server/image-utils';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
export async function GET(request: NextRequest) {
try {
const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const searchParams = request.nextUrl.searchParams;
const queryString = searchParams.toString();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/products${queryString ? `?${queryString}` : ''}`,
{
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/json',
},
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
const data = await res.json();
return NextResponse.json(fixImageUrls(data));
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Internal server error' },
{ status: 500 }
);
}
}
+42
View File
@@ -0,0 +1,42 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
import { fixImageUrls } from '@/lib/server/image-utils';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
export async function GET(request: NextRequest) {
try {
const token = cookies().get('session_token')?.value;
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const searchParams = request.nextUrl.searchParams;
const queryString = searchParams.toString();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/products${queryString ? `?${queryString}` : ''}`,
{
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/json',
},
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
const data = await res.json();
return NextResponse.json(fixImageUrls(data));
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Internal server error' },
{ status: 500 }
);
}
}
+71
View File
@@ -0,0 +1,71 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` };
}
export async function PATCH(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const resolvedParams = await params;
const headers = await getAuthHeader();
const body = await request.json();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/platforms/${resolvedParams.id}`,
{
method: 'PATCH',
headers: {
...headers,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
}
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const resolvedParams = await params;
const headers = await getAuthHeader();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/platforms/${resolvedParams.id}`,
{ method: 'DELETE', headers }
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json({ ok: true });
} catch (err: any) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
}
+68
View File
@@ -0,0 +1,68 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const token = cookies().get('session_token')?.value;
if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` };
}
export async function PATCH(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const headers = await getAuthHeader();
const body = await request.json();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/platforms/${params.id}`,
{
method: 'PATCH',
headers: {
...headers,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
}
export async function DELETE(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const headers = await getAuthHeader();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/platforms/${params.id}`,
{ method: 'DELETE', headers }
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json({ ok: true });
} catch (err: any) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
}
+73
View File
@@ -0,0 +1,73 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` };
}
export async function GET(request: NextRequest) {
try {
const headers = await getAuthHeader();
const searchParams = request.nextUrl.searchParams;
const queryString = searchParams.toString();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/users${queryString ? `?${queryString}` : ''}`,
{
headers: {
...headers,
Accept: 'application/json',
},
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
const data = await res.json();
return NextResponse.json(data);
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Unauthorized' },
{ status: 401 }
);
}
}
export async function POST(request: NextRequest) {
try {
const headers = await getAuthHeader();
const body = await request.json();
const res = await fetch(`${API_BASE_URL}/api/v1/admin/users`, {
method: 'POST',
headers: {
...headers,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Unauthorized' },
{ status: 401 }
);
}
}
+72
View File
@@ -0,0 +1,72 @@
import { NextRequest, NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() {
const token = cookies().get('session_token')?.value;
if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` };
}
export async function GET(request: NextRequest) {
try {
const headers = await getAuthHeader();
const searchParams = request.nextUrl.searchParams;
const queryString = searchParams.toString();
const res = await fetch(
`${API_BASE_URL}/api/v1/admin/users${queryString ? `?${queryString}` : ''}`,
{
headers: {
...headers,
Accept: 'application/json',
},
}
);
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
const data = await res.json();
return NextResponse.json(data);
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Unauthorized' },
{ status: 401 }
);
}
}
export async function POST(request: NextRequest) {
try {
const headers = await getAuthHeader();
const body = await request.json();
const res = await fetch(`${API_BASE_URL}/api/v1/admin/users`, {
method: 'POST',
headers: {
...headers,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Unauthorized' },
{ status: 401 }
);
}
}
+31
View File
@@ -0,0 +1,31 @@
import { NextRequest, NextResponse } from 'next/server';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const res = await fetch(`${API_BASE_URL}/api/auth/beta/signup`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Internal server error' },
{ status: 500 }
);
}
}
+2 -1
View File
@@ -25,7 +25,8 @@ export async function POST(request: NextRequest) {
const data = await res.json(); const data = await res.json();
// Set HTTP-only cookie instead of returning token // Set HTTP-only cookie instead of returning token
cookies().set('session_token', data.token, { const cookieStore = await cookies();
cookieStore.set('session_token', data.token, {
httpOnly: true, httpOnly: true,
secure: process.env.NODE_ENV === 'production', secure: process.env.NODE_ENV === 'production',
sameSite: 'lax', sameSite: 'lax',
+2 -1
View File
@@ -2,6 +2,7 @@ import { NextResponse } from 'next/server';
import { cookies } from 'next/headers'; import { cookies } from 'next/headers';
export async function POST() { export async function POST() {
cookies().delete('session_token'); const cookieStore = await cookies();
cookieStore.delete('session_token');
return NextResponse.json({ success: true }); return NextResponse.json({ success: true });
} }
+31
View File
@@ -0,0 +1,31 @@
import { NextRequest, NextResponse } from 'next/server';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const res = await fetch(`${API_BASE_URL}/api/auth/magic/exchange`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
if (!res.ok) {
return NextResponse.json(
{ error: await res.text() },
{ status: res.status }
);
}
return NextResponse.json(await res.json());
} catch (err: any) {
return NextResponse.json(
{ error: err?.message || 'Internal server error' },
{ status: 500 }
);
}
}
+3 -2
View File
@@ -4,7 +4,8 @@ import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL; const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
export async function GET(request: NextRequest) { export async function GET(request: NextRequest) {
const token = cookies().get('session_token')?.value; const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) { if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
@@ -16,7 +17,7 @@ export async function GET(request: NextRequest) {
}); });
if (!res.ok) { if (!res.ok) {
cookies().delete('session_token'); cookieStore.delete('session_token');
return NextResponse.json({ error: 'Session expired' }, { status: 401 }); return NextResponse.json({ error: 'Session expired' }, { status: 401 });
} }
+2 -1
View File
@@ -26,7 +26,8 @@ export async function POST(request: NextRequest) {
// Set HTTP-only cookie on successful registration // Set HTTP-only cookie on successful registration
if (data.token) { if (data.token) {
cookies().set('session_token', data.token, { const cookieStore = await cookies();
cookieStore.set('session_token', data.token, {
httpOnly: true, httpOnly: true,
secure: process.env.NODE_ENV === 'production', secure: process.env.NODE_ENV === 'production',
sameSite: 'lax', sameSite: 'lax',
+11 -7
View File
@@ -4,20 +4,22 @@ import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL; const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() { async function getAuthHeader() {
const token = cookies().get('session_token')?.value; const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) throw new Error('Unauthorized'); if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` }; return { Authorization: `Bearer ${token}` };
} }
export async function GET( export async function GET(
request: NextRequest, request: NextRequest,
{ params }: { params: { id: string } } { params }: { params: Promise<{ id: string }> }
) { ) {
try { try {
const { id } = await params;
const headers = await getAuthHeader(); const headers = await getAuthHeader();
const res = await fetch( const res = await fetch(
`${API_BASE_URL}/api/v1/gunbuilder/builds/${params.id}`, `${API_BASE_URL}/api/v1/gunbuilder/builds/${id}`,
{ headers } { headers }
); );
@@ -36,14 +38,15 @@ export async function GET(
export async function PUT( export async function PUT(
request: NextRequest, request: NextRequest,
{ params }: { params: { id: string } } { params }: { params: Promise<{ id: string }> }
) { ) {
try { try {
const { id } = await params;
const headers = await getAuthHeader(); const headers = await getAuthHeader();
const body = await request.json(); const body = await request.json();
const res = await fetch( const res = await fetch(
`${API_BASE_URL}/api/v1/gunbuilder/builds/${params.id}`, `${API_BASE_URL}/api/v1/gunbuilder/builds/${id}`,
{ {
method: 'PUT', method: 'PUT',
headers: { ...headers, 'Content-Type': 'application/json' }, headers: { ...headers, 'Content-Type': 'application/json' },
@@ -66,13 +69,14 @@ export async function PUT(
export async function DELETE( export async function DELETE(
request: NextRequest, request: NextRequest,
{ params }: { params: { id: string } } { params }: { params: Promise<{ id: string }> }
) { ) {
try { try {
const { id } = await params;
const headers = await getAuthHeader(); const headers = await getAuthHeader();
const res = await fetch( const res = await fetch(
`${API_BASE_URL}/api/v1/gunbuilder/builds/${params.id}`, `${API_BASE_URL}/api/v1/gunbuilder/builds/${id}`,
{ method: 'DELETE', headers } { method: 'DELETE', headers }
); );
+2 -1
View File
@@ -4,7 +4,8 @@ import { cookies } from 'next/headers';
const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL; const API_BASE_URL = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL;
async function getAuthHeader() { async function getAuthHeader() {
const token = cookies().get('session_token')?.value; const cookieStore = await cookies();
const token = cookieStore.get('session_token')?.value;
if (!token) throw new Error('Unauthorized'); if (!token) throw new Error('Unauthorized');
return { Authorization: `Bearer ${token}` }; return { Authorization: `Bearer ${token}` };
} }
+5 -3
View File
@@ -29,11 +29,13 @@ function timeAgo(iso?: string | null) {
export default async function BuildBreakdownPage({ export default async function BuildBreakdownPage({
params, params,
}: { }: {
params: { buildId: string }; params: Promise<{ buildId: string }>;
}) { }) {
const { buildId } = await params;
// Public detail endpoint - use Next.js API route // Public detail endpoint - use Next.js API route
const baseUrl = process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'; const baseUrl = process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000';
const res = await fetch(`${baseUrl}/api/builds/public/${params.buildId}`, { const res = await fetch(`${baseUrl}/api/builds/public/${buildId}`, {
cache: "no-store", cache: "no-store",
}); });
@@ -359,7 +361,7 @@ export default async function BuildBreakdownPage({
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span className="text-zinc-500">Build ID</span> <span className="text-zinc-500">Build ID</span>
<span className="text-zinc-300"> <span className="text-zinc-300">
{(build.uuid ?? params.buildId).slice(0, 8)} {(build.uuid ?? buildId).slice(0, 8)}
</span> </span>
</div> </div>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
+6 -9
View File
@@ -2,20 +2,17 @@
import type { ReactNode } from "react"; import type { ReactNode } from "react";
import { Suspense } from "react"; import { Suspense } from "react";
import { AuthProvider } from "@/context/AuthContext";
import { BuilderNav } from "@/components/BuilderNav"; import { BuilderNav } from "@/components/BuilderNav";
import { TopNav } from "@/components/TopNav"; import { TopNav } from "@/components/TopNav";
export default function BuilderLayout({ children }: { children: ReactNode }) { export default function BuilderLayout({ children }: { children: ReactNode }) {
return ( return (
<div className="min-h-screen bg-neutral-950 text-zinc-50"> <div className="min-h-screen bg-neutral-950 text-zinc-50">
<AuthProvider> <TopNav />
<TopNav /> <Suspense fallback={<div className="h-[52px]" />}>
<Suspense fallback={<div className="h-[52px]" />}> <BuilderNav />
<BuilderNav /> </Suspense>
</Suspense> <main className="min-h-screen">{children}</main>
<main className="min-h-screen">{children}</main>
</AuthProvider>
</div> </div>
); );
} }
+1 -1
View File
@@ -40,7 +40,7 @@ export default function RootLayout({ children }: { children: ReactNode }) {
<body className="bg-dark text-zinc-950 dark:bg-black dark:text-zinc-50"> <body className="bg-dark text-zinc-950 dark:bg-black dark:text-zinc-50">
{" "} {" "}
<Script src="https://umami.ash.gofwd.group/script.js" data-website-id="ad310b4a-aa5e-471a-938b-47a6c0f4108d" <Script src="https://umami.ash.gofwd.group/script.js" data-website-id="15c6b9ad-4119-4981-829c-26db69c07a15"
strategy="lazyOnload" strategy="lazyOnload"
/> />
<AuthProvider> <AuthProvider>
+2 -1
View File
@@ -13,7 +13,7 @@ const API_BASE_URL =
function LoginPageContent() { function LoginPageContent() {
const router = useRouter(); const router = useRouter();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const { login, loading } = useAuth(); const { login, refreshUser, loading } = useAuth();
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
@@ -36,6 +36,7 @@ function LoginPageContent() {
try { try {
await login({ email, password }); await login({ email, password });
await refreshUser();
router.push(next); router.push(next);
} catch (err: any) { } catch (err: any) {
setError(err?.message ?? "Failed to log in"); setError(err?.message ?? "Failed to log in");
+10 -10
View File
@@ -3,17 +3,17 @@ import { useMemo } from "react";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import type React from "react"; import type React from "react";
import "react-quill/dist/quill.snow.css"; // import "react-quill/dist/quill.snow.css";
const ReactQuill = dynamic(() => import("react-quill-new"), { ssr: false });
type RichTextEditorProps = {
value: string;
onChange: (html: string) => void;
placeholder?: string;
className?: string;
};
const ReactQuill = dynamic(() => import("react-quill"), { ssr: false });
type RichTextEditorProps = {
value: string;
onChange: (html: string) => void;
placeholder?: string;
className?: string;
};
export default function RichTextEditor({ export default function RichTextEditor({
value, value,
onChange, onChange,
+13 -16
View File
@@ -14,9 +14,6 @@ export type CreatePlatformDto = {
export type UpdatePlatformDto = Partial<CreatePlatformDto>; export type UpdatePlatformDto = Partial<CreatePlatformDto>;
const API_BASE =
process.env.NEXT_PUBLIC_API_BASE_URL ?? "";
function normalizePlatform(raw: any): Platform { function normalizePlatform(raw: any): Platform {
const id = Number(raw?.id); const id = Number(raw?.id);
const key = String(raw?.key ?? "").trim(); const key = String(raw?.key ?? "").trim();
@@ -33,9 +30,9 @@ function normalizePlatform(raw: any): Platform {
return { id, key, label, isActive }; return { id, key, label, isActive };
} }
export async function fetchPlatforms(tokenHeaders: HeadersInit): Promise<Platform[]> { export async function fetchPlatforms(_tokenHeaders: HeadersInit): Promise<Platform[]> {
const res = await fetch(`${API_BASE}/api/platforms`, { const res = await fetch('/api/admin/platforms', {
headers: { ...tokenHeaders }, credentials: 'include',
}); });
if (!res.ok) { if (!res.ok) {
@@ -48,15 +45,15 @@ export async function fetchPlatforms(tokenHeaders: HeadersInit): Promise<Platfor
} }
export async function createPlatform( export async function createPlatform(
tokenHeaders: HeadersInit, _tokenHeaders: HeadersInit,
dto: CreatePlatformDto dto: CreatePlatformDto
): Promise<Platform> { ): Promise<Platform> {
const res = await fetch(`${API_BASE}/api/platforms`, { const res = await fetch('/api/admin/platforms', {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
...tokenHeaders,
}, },
credentials: 'include',
body: JSON.stringify(dto), body: JSON.stringify(dto),
}); });
@@ -69,17 +66,17 @@ export async function createPlatform(
} }
export async function updatePlatform( export async function updatePlatform(
tokenHeaders: HeadersInit, _tokenHeaders: HeadersInit,
id: number, id: number,
dto: UpdatePlatformDto dto: UpdatePlatformDto
): Promise<Platform> { ): Promise<Platform> {
const res = await fetch(`${API_BASE}/api/platforms/${id}`, { const res = await fetch(`/api/admin/platforms/${id}`, {
method: "PATCH", method: "PATCH",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
...tokenHeaders,
}, },
body: JSON.stringify({ isActive: dto.isActive }), credentials: 'include',
body: JSON.stringify(dto),
}); });
if (!res.ok) { if (!res.ok) {
@@ -90,10 +87,10 @@ export async function updatePlatform(
return normalizePlatform(await res.json()); return normalizePlatform(await res.json());
} }
export async function deletePlatform(tokenHeaders: HeadersInit, id: number): Promise<void> { export async function deletePlatform(_tokenHeaders: HeadersInit, id: number): Promise<void> {
const res = await fetch(`${API_BASE}/api/platforms/${id}`, { const res = await fetch(`/api/admin/platforms/${id}`, {
method: "DELETE", method: "DELETE",
headers: { ...tokenHeaders }, credentials: 'include',
}); });
if (!res.ok) { if (!res.ok) {
+43
View File
@@ -0,0 +1,43 @@
/**
* Fix mixed content warnings by upgrading HTTP image URLs to HTTPS
* This handles image URLs stored in the database as http:// which cause
* browser warnings when loaded from HTTPS pages.
*/
export function fixImageUrl(url: string | null | undefined): string | null | undefined {
if (!url || typeof url !== 'string') return url;
return url.replace(/^http:\/\//i, 'https://');
}
/**
* Recursively fix all image URL fields in an object
* Common fields: imageUrl, mainImageUrl, coverImageUrl, thumbnailUrl, etc.
*/
export function fixImageUrls(data: any): any {
if (!data) return data;
if (Array.isArray(data)) {
return data.map(item => fixImageUrls(item));
}
if (typeof data === 'object') {
const fixed: any = {};
for (const [key, value] of Object.entries(data)) {
// Fix any field that looks like an image URL
if (
(key.toLowerCase().includes('image') ||
key.toLowerCase().includes('photo') ||
key.toLowerCase().includes('picture')) &&
typeof value === 'string'
) {
fixed[key] = fixImageUrl(value);
} else if (typeof value === 'object') {
fixed[key] = fixImageUrls(value);
} else {
fixed[key] = value;
}
}
return fixed;
}
return data;
}
+66 -16
View File
@@ -2,42 +2,92 @@
import { NextResponse } from "next/server"; import { NextResponse } from "next/server";
import type { NextRequest } from "next/server"; import type { NextRequest } from "next/server";
function redirectToLogin(req: NextRequest) {
const url = req.nextUrl.clone();
url.pathname = "/login";
url.searchParams.set("next", req.nextUrl.pathname);
return NextResponse.redirect(url);
}
function isPublicPath(pathname: string) {
// Public pages
if (pathname === "/") return true;
if (pathname === "/login") return true;
if (pathname === "/tos") return true;
if (pathname === "/privacy") return true;
if (pathname === "/beta") return true;
if (pathname.startsWith("/beta/")) return true;
if (pathname.startsWith("/auth")) return true;
if (pathname.startsWith("/api/auth")) return true;
// Static / framework assets
if (pathname.startsWith("/_next")) return true;
if (pathname === "/favicon.ico") return true;
if (pathname === "/robots.txt") return true;
if (pathname === "/sitemap.xml") return true;
// Health / misc APIs you may want public
if (pathname === "/api/health") return true;
return false;
}
function isProtectedLaunchPath(pathname: string) {
return (
pathname === "/builder" ||
pathname.startsWith("/builder/") ||
pathname === "/builds" ||
pathname.startsWith("/builds/") ||
pathname === "/vault" ||
pathname.startsWith("/vault/")
);
}
export function middleware(req: NextRequest) { export function middleware(req: NextRequest) {
const { pathname } = req.nextUrl; const { pathname } = req.nextUrl;
const token = req.cookies.get("session_token")?.value; const token = req.cookies.get("session_token")?.value;
const isLoggedIn = Boolean(token);
console.log("is logged in: " + isLoggedIn);
console.log("MW:", req.nextUrl.pathname)
// Protect admin API routes // IMPORTANT: Use a server-only env var for middleware behavior.
// Set LAUNCH_ONLY_ROOT=true in your Docker compose.
const launchOnlyRoot = process.env.LAUNCH_ONLY_ROOT === "true";
// In "launch only" mode, anonymous users are blocked only from gated app areas.
if (launchOnlyRoot && !isLoggedIn && isProtectedLaunchPath(pathname)) {
return redirectToLogin(req);
}
// --- Protected API routes ---
if (pathname.startsWith("/api/admin")) { if (pathname.startsWith("/api/admin")) {
if (!token) { if (!isLoggedIn) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
} }
} }
// Protect builds API routes
if (pathname.startsWith("/api/builds")) { if (pathname.startsWith("/api/builds")) {
if (!token) { if (!isLoggedIn) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
} }
} }
// Protect admin pages // --- Protected pages ---
if (pathname.startsWith("/admin")) { if (pathname.startsWith("/admin")) {
if (!token) { if (!isLoggedIn) return redirectToLogin(req);
const url = req.nextUrl.clone(); }
url.pathname = "/login";
url.searchParams.set("next", pathname); // Protect the builder UI
return NextResponse.redirect(url); if (pathname === "/builder" || pathname.startsWith("/builder/")) {
} if (!isLoggedIn) return redirectToLogin(req);
} }
return NextResponse.next(); return NextResponse.next();
} }
export const config = { export const config = {
// Run middleware for all application routes, but avoid static assets.
matcher: [ matcher: [
"/api/admin/:path*", "/((?!_next/static|_next/image|favicon.ico|robots.txt|sitemap.xml).*)",
"/api/builds",
"/api/builds/:path+",
"/admin/:path*",
], ],
}; };
+2 -1
View File
@@ -1,5 +1,6 @@
/// <reference types="next" /> /// <reference types="next" />
/// <reference types="next/image-types/global" /> /// <reference types="next/image-types/global" />
/// <reference path="./.next/types/routes.d.ts" />
// NOTE: This file should not be edited // NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information. // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
-3
View File
@@ -1,8 +1,5 @@
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = { const nextConfig = {
experimental: {
appDir: true,
},
typescript: { typescript: {
ignoreBuildErrors: true, ignoreBuildErrors: true,
}, },
+791 -371
View File
File diff suppressed because it is too large Load Diff
+22 -7
View File
@@ -1,7 +1,23 @@
{ {
"name": "gunbuilder-prototype", "name": "battl.builders",
"version": "0.1.0", "version": "1.0",
"private": true, "private": true,
"author": {
"name": "Forward Group, LLC",
"email": "info@goforward.group",
"url": "https://goforward.group"
},
"contributors": [
{
"name": "Sean Strawsburg",
"email": "sean@goforward.group"
},
{
"name": "Don Strawsburg",
"email": "don@goforward.group"
}
],
"license": "UNLICENSED",
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
"build": "next build", "build": "next build",
@@ -13,12 +29,11 @@
"@heroicons/react": "^2.2.0", "@heroicons/react": "^2.2.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"lucide-react": "^0.555.0", "lucide-react": "^0.555.0",
"next": "14.2.3", "next": "15.5.11",
"quill": "^2.0.3",
"quill-image-resize-module": "^3.0.0", "quill-image-resize-module": "^3.0.0",
"react": "18.3.1", "react": "^19.2.4",
"react-dom": "18.3.1", "react-dom": "^19.2.4",
"react-quill": "^2.0.0" "react-quill-new": "^3.7.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "20.12.7", "@types/node": "20.12.7",