899c54e1fc
Auth: drop localStorage session token entirely, talk to netrunner-backend via credentials:"include" HttpOnly cookies shared over the common parent domain, and redirect to the account subdomain (ЛК) after login instead of a dead local /profile route. Config: NEXT_PUBLIC_* values used by client components (API_URL, ACCOUNT_ORIGIN, BOT_USERNAME, PB_URL) get inlined into the JS bundle at image build time and can't be overridden by docker-compose environment at container start. Moved these reads into server components (page.tsx) and thread them down as props, so one built image can genuinely serve both prod and dev by config alone. CI/CD: split the old single auto-deploy-to-prod workflow into build.yml (build+push on every push to main) and deploy.yml with deploy-dev (auto, same VPS as netrunner-backend's dev environment) and deploy-prod (manual dispatch only) — mirrors netrunner-backend's pipeline shape. Added docker-compose.dev-remote.yml pointing at the dev backend. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
103 lines
3.7 KiB
TypeScript
103 lines
3.7 KiB
TypeScript
import PocketBase, { RecordModel } from "pocketbase";
|
|
|
|
const isServer = typeof window === "undefined";
|
|
|
|
const publicUrl =
|
|
process.env.NEXT_PUBLIC_PB_URL || "https://netrunner-vpn.com/cms-api";
|
|
const internalUrl = process.env.PB_INTERNAL_URL; // Не ставим дефолт здесь
|
|
|
|
export const PB_URL = isServer ? internalUrl || publicUrl : publicUrl;
|
|
|
|
export const pb = new PocketBase(PB_URL);
|
|
|
|
// Отключаем автоотмену на сервере, чтобы избежать AbortError при SSR/ISR
|
|
if (isServer) {
|
|
pb.autoCancellation(false);
|
|
}
|
|
|
|
if (typeof window !== "undefined") {
|
|
// Фильтруем ошибки абортов (CANCELLED), которые генерит PocketBase
|
|
const originalError = console.error;
|
|
console.error = (...args) => {
|
|
if (args[0]?.message?.includes("The request was autocancelled")) return;
|
|
originalError.apply(console, args);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Базовый интерфейс для записей PocketBase.
|
|
* Наследуемся от RecordModel, чтобы иметь системные поля (id, created, updated, и т.д.)
|
|
*/
|
|
export interface BaseRecord extends RecordModel {
|
|
id: string;
|
|
collectionId: string;
|
|
collectionName: string;
|
|
created: string;
|
|
updated: string;
|
|
}
|
|
|
|
/**
|
|
* Типизация для постов блога
|
|
*/
|
|
export interface PBPost extends BaseRecord {
|
|
title: string;
|
|
slug: string;
|
|
excerpt: string;
|
|
content: string;
|
|
image: string; // имя файла
|
|
lang: "ru" | "en";
|
|
published: boolean;
|
|
}
|
|
|
|
/**
|
|
* Типизация для FAQ
|
|
*/
|
|
export interface PBFaq extends BaseRecord {
|
|
question: string;
|
|
answer: string;
|
|
order: number;
|
|
lang: string;
|
|
}
|
|
|
|
const FILE_BASE_URL =
|
|
process.env.NEXT_PUBLIC_PB_URL || "https://netrunner-vpn.com/cms-api";
|
|
|
|
/**
|
|
* Функция для генерации ссылки на картинку из PB
|
|
* @param record - Объект записи (должен содержать id и collectionId)
|
|
* @param filename - Имя файла из поля записи
|
|
*/
|
|
export function getPbImage(
|
|
record: Pick<BaseRecord, "id" | "collectionId">,
|
|
filename: string | undefined,
|
|
): string {
|
|
if (!filename) return "";
|
|
return `${FILE_BASE_URL}/api/files/${record.collectionId}/${record.id}/${filename}`;
|
|
}
|
|
/**
|
|
* Типизация для карточек платформ на странице /download (коллекции downloads_en/downloads_ru)
|
|
*/
|
|
export interface PBDownload extends BaseRecord {
|
|
name: string; // Имя (напр. "Windows 11")
|
|
platformId: "windows" | "android" | "linux" | "macos" | "ios"; // Строгий ID для иконок
|
|
version: string; // Строка версии
|
|
status: "active" | "dev"; // Статус разработки
|
|
file: string; // Сам бинарник (файл)
|
|
storeLink: string; // URL на магазин или github
|
|
order: number; // Для сортировки (1, 2, 3...)
|
|
}
|
|
|
|
// Идентична getPbImage по формуле URL, но семантически отдельная функция для
|
|
// бинарников (используется с атрибутом download на странице /download), а не картинок.
|
|
// baseUrl передаётся явно (а не берётся из FILE_BASE_URL), т.к. единственный вызывающий —
|
|
// download-client.tsx, "use client"-компонент, которому runtime-адрес PocketBase приходит
|
|
// пропсом из серверного page.tsx (см. app/[lang]/download/page.tsx).
|
|
export function getPbFileUrl(
|
|
record: Pick<BaseRecord, "id" | "collectionId">,
|
|
filename: string | undefined,
|
|
baseUrl: string,
|
|
): string {
|
|
if (!filename) return "";
|
|
return `${baseUrl}/api/files/${record.collectionId}/${record.id}/${filename}`;
|
|
}
|