更新 env变量使用收口到env.ts,constants暂时移除

This commit is contained in:
moux1024 2025-09-29 14:18:01 +08:00
parent 0b63661ae0
commit fa524af5b5
18 changed files with 268 additions and 87 deletions

View File

@ -4,7 +4,7 @@
### 目录结构与职责
- `constants.ts`:基础配置(`BASE_URL``NEXT_PUBLIC_BASE_URL` 注入)
- 基础配置从 `@/lib/env` 导入 `baseUrl`
- `request.ts`Axios 实例与拦截器、通用 `get/post/put/del``stream`SSE风格下载进度`downloadStream``streamJsonPost`
- `errorHandle.ts`:错误码映射与统一提示、特殊码处理(如 401 跳转登录、402 不弹提示)
- `common.ts`:通用类型与与上传相关的工具(获取七牛 Token、上传
@ -16,7 +16,7 @@
1. 使用 `request.ts` 提供的 `get/post/put/del` 包装函数发起请求,返回后端响应体(已通过响应拦截器做业务码检查)。
2. 业务成功码:`code === 0``code === 202`(长任务/排队等需要前端自行处理状态)。若非成功码,拦截器会调用 `errorHandle``Promise.reject`
3. 认证:前端从 `localStorage.token` 注入 `Authorization: Bearer <token>`,请确保登录流程写入 `token`
4. 基础地址:通过环境变量 `NEXT_PUBLIC_BASE_URL` 注入,构建前需设置
4. 基础地址:`@/lib/env``baseUrl` 获取,统一管理环境变量
### 错误处理约定
@ -90,7 +90,7 @@ await downloadStream('/download/file', 'result.mp4');
#### 浏览器前端React/Next.js CSR
- 直接使用 `get/post/put/del`;确保登录后将 `token` 写入 `localStorage`
- 环境变量:在 `.env.local` 配置 `NEXT_PUBLIC_BASE_URL`
- 环境变量:在 `.env.local` 配置 `NEXT_PUBLIC_BASE_URL`,通过 `@/lib/env` 统一管理
- 错误提示:由 `errorHandle` 统一处理402 会展示积分不足通知
#### Next.js Route Handler服务端 API
@ -101,7 +101,7 @@ await downloadStream('/download/file', 'result.mp4');
#### Next.js Server Components/SSR
- 服务端不具备 `localStorage`,如需鉴权请改为从 Cookie/Headers 传递 token并在转发时设置 `Authorization`
- 服务器端可直接使用 `fetch(BASE_URL + path, { headers })`
- 服务器端可直接使用 `fetch(baseUrl + path, { headers })`
#### Node/ServerlessVercel/Cloudflare

View File

@ -1,5 +1,5 @@
// Common API 相关接口
import { BASE_URL } from './constants'
import { baseUrl } from '@/lib/env';
export interface ApiResponse<T = any> {
code: number
@ -44,7 +44,7 @@ export const getUploadToken = async (timeoutMs: number = 10000): Promise<{ token
}, timeoutMs)
try {
const response = await fetch(`${BASE_URL}/common/get-upload-token`, {
const response = await fetch(`${baseUrl}/common/get-upload-token`, {
method: "GET",
headers: {
Accept: "application/json",

View File

@ -1,4 +0,0 @@
export const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL
// export const BASE_URL = 'https://77.smartvideo.py.qikongjian.com'
// export const BASE_URL ='http://192.168.120.5:8000'
//

View File

@ -1,5 +1,5 @@
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig, AxiosHeaders } from 'axios';
import { BASE_URL } from './constants'
import { baseUrl } from '@/lib/env';
import { errorHandle } from './errorHandle';
/**
@ -20,7 +20,7 @@ const handleRequestError = (error: any, defaultMessage: string = '请求失败')
};
// 创建 axios 实例
const request: AxiosInstance = axios.create({
baseURL: BASE_URL, // 设置基础URL
baseURL: baseUrl, // 设置基础URL
timeout: 300000, // 请求超时时间
headers: {
'Content-Type': 'application/json',
@ -102,7 +102,7 @@ export async function streamJsonPost<T = any>(
) {
try {
const token = localStorage?.getItem('token') || '';
const response = await fetch(`${BASE_URL}${url}`, {
const response = await fetch(`${baseUrl}${url}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',

View File

@ -1,4 +1,4 @@
import { BASE_URL } from "./constants";
import { baseUrl } from '@/lib/env';
import { post } from './request';
// 获取路演配置数据
@ -8,7 +8,7 @@ export const fetchRoadshowConfigs = async () => {
try {
console.log('开始请求接口数据...');
const response = await fetch(BASE_URL + '/serversetting/roadshow-configs', {
const response = await fetch(baseUrl + '/serversetting/roadshow-configs', {
method: 'POST',
headers: {
'Content-Type': 'application/json',

View File

@ -1,7 +1,6 @@
import { post, streamJsonPost } from "./request";
import { ProjectTypeEnum } from "@/app/model/enums";
import { ApiResponse } from "@/api/common";
import { BASE_URL } from "./constants";
import {
AITextEntity,
RoleEntity,

View File

@ -5,6 +5,7 @@ import { Providers } from '@/components/providers';
import { ConfigProvider, theme } from 'antd';
import CallbackModal from '@/components/common/CallbackModal';
import { useAppStartupAnalytics } from '@/hooks/useAppStartupAnalytics';
import { gaEnabled, gaMeasurementId } from '@/lib/env';
// 创建上下文来传递弹窗控制方法
const CallbackModalContext = createContext<{
@ -53,16 +54,16 @@ export default function RootLayout({
<link rel="icon" type="image/x-icon" sizes="32x32" href="/favicon.ico?v=1" />
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico?v=1" />
<link rel="apple-touch-icon" sizes="180x180" href="/favicon.ico?v=1" />
{process.env.NEXT_PUBLIC_GA_ENABLED === 'true' && (
{gaEnabled && (
<>
<script async src={`https://www.googletagmanager.com/gtag/js?id=${process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID}`}></script>
<script async src={`https://www.googletagmanager.com/gtag/js?id=${gaMeasurementId}`}></script>
<script
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){window.dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID}', {
gtag('config', '${gaMeasurementId}', {
page_title: document.title,
page_location: window.location.href,
send_page_view: true

View File

@ -4,6 +4,7 @@ import React, { useEffect, useState } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { CheckCircle, XCircle, Loader2, AlertTriangle } from "lucide-react";
import type { OAuthCallbackParams } from "@/app/types/google-oauth";
import { baseUrl } from '@/lib/env';
// 根据后端实际返回格式定义响应类型
interface GoogleOAuthResponse {
@ -98,8 +99,7 @@ export default function OAuthCallback() {
console.log('最终使用的邀请码:', finalInviteCode);
// 根据 jiekou.md 文档调用统一的 Python OAuth 接口
// 使用 NEXT_PUBLIC_BASE_URL 配置,默认为 https://77.smartvideo.py.qikongjian.com
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || 'https://77.smartvideo.py.qikongjian.com';
// 使用统一配置中的 baseUrl
console.log('🔧 调用 Python OAuth 接口:', baseUrl);
const response = await fetch(`${baseUrl}/api/oauth/google`, {

View File

@ -8,6 +8,7 @@
"use client";
import React, { useState, useCallback, useEffect, useRef } from 'react';
import { cutUrl } from '@/lib/env';
import { motion, AnimatePresence } from 'framer-motion';
import {
Zap,
@ -95,7 +96,6 @@ export const AIEditingIframe = React.forwardRef<AIEditingIframeHandle, AIEditing
const iframeRef = useRef<HTMLIFrameElement>(null);
const progressIntervalRef = useRef<NodeJS.Timeout | null>(null);
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
const cutUrl = process.env.NEXT_PUBLIC_CUT_URL || 'https://cut.movieflow.ai';
console.log('cutUrl', cutUrl);
// 构建智能剪辑URL

View File

@ -8,6 +8,7 @@ import { useUpdateEffect } from '@/app/hooks/useUpdateEffect';
import { LOADING_TEXT_MAP, TaskObject, Status, Stage } from '@/api/DTO/movieEdit';
import { AspectRatioValue } from '@/components/ChatInputBox/AspectRatioSelector';
import { useDeviceType } from '@/hooks/useDeviceType';
import { cutUrlTo, errorConfig } from '@/lib/env';
interface UseWorkflowDataProps {
onEditPlanGenerated?: () => void;
@ -40,7 +41,7 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa
const { isMobile, isTablet, isDesktop } = useDeviceType();
const cutUrl = process.env.NEXT_PUBLIC_CUT_URL_TO || 'https://smartcut.api.movieflow.ai';
const cutUrl = cutUrlTo;
console.log('cutUrl', cutUrl);
useEffect(() => {
@ -77,7 +78,6 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa
}
});
let loadingText: any = useRef(LOADING_TEXT_MAP.getInfo);
const errorConfig = Number(process.env.NEXT_PUBLIC_ERROR_CONFIG);
// 更新 taskObject 的类型

View File

@ -2,6 +2,8 @@
*
*/
import { getVideoEditApiConfig as getEnvVideoEditConfig, isDevelopment } from '@/lib/env';
export interface VideoEditApiConfig {
/** 是否使用Mock API */
useMockApi: boolean;
@ -26,32 +28,23 @@ export const defaultVideoEditApiConfig: VideoEditApiConfig = {
remoteApiBase: '/video-edit',
localApiBase: '/api/video-edit',
timeout: 10000,
enableDebugLog: process.env.NODE_ENV === 'development'
enableDebugLog: isDevelopment
};
/**
* API配置
*/
export function getVideoEditApiConfig(): VideoEditApiConfig {
// 可以从环境变量或其他配置源读
const config = { ...defaultVideoEditApiConfig };
// 从统一环境配置获
const envConfig = getEnvVideoEditConfig();
// 环境变量覆盖
if (process.env.NEXT_PUBLIC_VIDEO_EDIT_USE_MOCK === 'true') {
config.useMockApi = true;
config.useLocalApi = false;
}
if (process.env.NEXT_PUBLIC_VIDEO_EDIT_USE_REMOTE === 'true') {
config.useLocalApi = false;
config.useMockApi = false;
}
if (process.env.NEXT_PUBLIC_VIDEO_EDIT_REMOTE_BASE) {
config.remoteApiBase = process.env.NEXT_PUBLIC_VIDEO_EDIT_REMOTE_BASE;
}
return config;
return {
...defaultVideoEditApiConfig,
useMockApi: envConfig.useMockApi,
useLocalApi: !envConfig.useRemoteApi,
remoteApiBase: envConfig.remoteApiBase,
enableDebugLog: envConfig.enableDebugLog,
};
}
/**
@ -155,7 +148,7 @@ export const errorHandlingConfig = {
*/
export const performanceConfig = {
/** 是否启用性能监控 */
enabled: process.env.NODE_ENV === 'development',
enabled: isDevelopment,
/** 慢请求阈值(毫秒) */
slowRequestThreshold: 2000,
/** 是否记录所有请求 */
@ -193,7 +186,7 @@ export const uiConfig = {
/** 描述最大长度 */
maxDescriptionLength: 500,
/** 是否显示调试信息 */
showDebugInfo: process.env.NODE_ENV === 'development',
showDebugInfo: isDevelopment,
/** 动画配置 */
animations: {
enabled: true,

View File

@ -1,4 +1,5 @@
// src/components/VantaHaloBackground.jsx
// 未使用
'use client';
import React, { useRef, useEffect, memo } from 'react'

View File

@ -8,11 +8,9 @@ import type {
OAuthState
} from '@/app/types/google-oauth';
import { setUserProperties } from '@/utils/analytics';
import { javaUrl, baseUrl, googleClientId, getGoogleRedirectUri } from '@/lib/env';
// API配置
//const JAVA_BASE_URL = 'http://192.168.120.36:8080';
const JAVA_BASE_URL = process.env.NEXT_PUBLIC_JAVA_URL || 'https://77.app.java.auth.qikongjian.com';
const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL
// API配置 - 直接使用导入的配置变量
// Token存储键
const TOKEN_KEY = 'token';
@ -43,7 +41,7 @@ type RegisterUserResponse = {
*/
export const loginUser = async (email: string, password: string) => {
try {
const response = await fetch(`${JAVA_BASE_URL}/api/user/login`, {
const response = await fetch(`${javaUrl}/api/user/login`, {
method: 'POST',
headers: {
'Accept': 'application/json',
@ -205,9 +203,6 @@ export const authFetch = async (url: string, options: RequestInit = {}) => {
// Google OAuth相关函数
// Google Client ID - 从环境变量获取
const GOOGLE_CLIENT_ID = process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID || '847079918888-o1nne8d3ij80dn20qurivo987pv07225.apps.googleusercontent.com';
/**
* Google GSI SDK
*/
@ -268,10 +263,8 @@ export const signInWithGoogle = async (inviteCode?: string): Promise<void> => {
}
}
// 从环境变量获取配置
const clientId = process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID || '847079918888-o1nne8d3ij80dn20qurivo987pv07225.apps.googleusercontent.com';
const javaBaseUrl = process.env.NEXT_PUBLIC_JAVA_URL || 'https://auth.test.movieflow.ai';
const redirectUri = process.env.NEXT_PUBLIC_GOOGLE_REDIRECT_URI || `${javaBaseUrl}/api/auth/google/callback`;
// 从统一配置获取配置
const redirectUri = getGoogleRedirectUri();
// 生成随机nonce用于安全验证
const nonce = Array.from(crypto.getRandomValues(new Uint8Array(32)))
@ -286,12 +279,9 @@ export const signInWithGoogle = async (inviteCode?: string): Promise<void> => {
};
console.log('使用的配置:', {
clientId,
javaBaseUrl,
redirectUri,
envClientId: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID,
envJavaUrl: process.env.NEXT_PUBLIC_JAVA_URL,
envRedirectUri: process.env.NEXT_PUBLIC_GOOGLE_REDIRECT_URI
clientId: googleClientId,
javaBaseUrl: javaUrl,
redirectUri
});
// 详细的调试日志
@ -300,14 +290,14 @@ export const signInWithGoogle = async (inviteCode?: string): Promise<void> => {
console.log(' - 当前协议:', window.location.protocol);
console.log(' - 当前端口:', window.location.port);
console.log(' - 完整 origin:', window.location.origin);
console.log(' - 环境变量 NEXT_PUBLIC_GOOGLE_REDIRECT_URI:', process.env.NEXT_PUBLIC_GOOGLE_REDIRECT_URI);
console.log(' - 环境变量 NEXT_PUBLIC_GOOGLE_REDIRECT_URI:', redirectUri);
console.log(' - 最终使用的 redirect_uri:', redirectUri);
console.log(' - Google Client ID:', GOOGLE_CLIENT_ID);
console.log(' - Google Client ID:', googleClientId);
// 构建Google OAuth2授权URL
const authParams = new URLSearchParams({
access_type: 'online',
client_id: clientId,
client_id: googleClientId,
nonce: nonce,
redirect_uri: redirectUri,
response_type: 'code', // 使用授权码模式
@ -362,7 +352,7 @@ export const loginWithGoogleToken = async (idToken: string, action: 'login' | 'r
inviteCode
};
const response = await fetch(`${JAVA_BASE_URL}/api/auth/google/login`, {
const response = await fetch(`${javaUrl}/api/auth/google/login`, {
method: 'POST',
headers: {
'Accept': 'application/json',
@ -417,7 +407,7 @@ export const bindGoogleAccount = async (bindToken: string, idToken?: string) =>
confirm: true
};
const response = await fetch(`${JAVA_BASE_URL}/api/auth/google/bind`, {
const response = await fetch(`${javaUrl}/api/auth/google/bind`, {
method: 'POST',
headers: {
'Accept': 'application/json',
@ -453,7 +443,7 @@ export const getGoogleBindStatus = async () => {
throw new Error('User not authenticated');
}
const response = await fetch(`${JAVA_BASE_URL}/api/auth/google/status`, {
const response = await fetch(`${javaUrl}/api/auth/google/status`, {
method: 'GET',
headers: {
'Accept': 'application/json',
@ -529,7 +519,7 @@ export const getUserProfile = async (): Promise<any> => {
throw new Error('No token available');
}
const response = await fetch(`${BASE_URL}/auth/profile`, {
const response = await fetch(`${baseUrl}/auth/profile`, {
method: 'GET',
headers: {
'Accept': 'application/json',
@ -638,7 +628,7 @@ export const registerUser = async ({
inviteCode?: string;
}): Promise<any> => {
try {
const response = await fetch(`${BASE_URL}/api/user/register`, {
const response = await fetch(`${baseUrl}/api/user/register`, {
method: 'POST',
headers: {
'Accept': 'application/json',
@ -676,7 +666,7 @@ export const registerUserWithInvite = async ({
invite_code?: string;
}): Promise<RegisterUserResponse> => {
try {
const response = await fetch(`${BASE_URL}/api/user_fission/register_with_invite`, {
const response = await fetch(`${baseUrl}/api/user_fission/register_with_invite`, {
method: 'POST',
headers: {
'Accept': 'application/json',
@ -707,7 +697,7 @@ export const registerUserWithInvite = async ({
*/
export const sendVerificationLink = async (email: string) => {
try {
const response = await fetch(`${JAVA_BASE_URL}/api/user/sendVerificationLink?email=${email}`);
const response = await fetch(`${javaUrl}/api/user/sendVerificationLink?email=${email}`);
const data = await response.json();
if(!data.success){
throw new Error(data.message||data.msg)

202
lib/env.ts Normal file
View File

@ -0,0 +1,202 @@
/**
*
*
*/
/**
*
*/
export interface EnvConfig {
// 基础配置
nodeEnv: string;
isDevelopment: boolean;
isProduction: boolean;
// API 基础 URL 配置
baseUrl: string;
javaUrl: string;
cutUrl: string;
cutUrlTo: string;
// Google OAuth 配置
googleClientId: string;
googleRedirectUri: string;
// Google Analytics 配置
gaEnabled: boolean;
gaMeasurementId: string;
// 视频编辑配置
videoEditUseMock: boolean;
videoEditUseRemote: boolean;
videoEditRemoteBase: string;
// 其他配置
errorConfig: number;
}
/**
*
*/
export const getEnvConfig = (): EnvConfig => {
const nodeEnv = process.env.NODE_ENV || 'development';
return {
// 基础配置
nodeEnv,
isDevelopment: nodeEnv === 'development',
isProduction: nodeEnv === 'production',
// API 基础 URL 配置
baseUrl: process.env.NEXT_PUBLIC_BASE_URL || 'https://77.smartvideo.py.qikongjian.com',
javaUrl: process.env.NEXT_PUBLIC_JAVA_URL || 'https://77.app.java.auth.qikongjian.com',
cutUrl: process.env.NEXT_PUBLIC_CUT_URL || 'https://smartcut.api.movieflow.ai',
cutUrlTo: process.env.NEXT_PUBLIC_CUT_URL_TO || 'https://smartcut.api.movieflow.ai',
// Google OAuth 配置
googleClientId: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID || '847079918888-o1nne8d3ij80dn20qurivo987pv07225.apps.googleusercontent.com',
googleRedirectUri: process.env.NEXT_PUBLIC_GOOGLE_REDIRECT_URI || '',
// Google Analytics 配置
gaEnabled: process.env.NEXT_PUBLIC_GA_ENABLED === 'true',
gaMeasurementId: process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID || 'G-4BDXV6TWF4',
// 视频编辑配置
videoEditUseMock: process.env.NEXT_PUBLIC_VIDEO_EDIT_USE_MOCK === 'true',
videoEditUseRemote: process.env.NEXT_PUBLIC_VIDEO_EDIT_USE_REMOTE === 'true',
videoEditRemoteBase: process.env.NEXT_PUBLIC_VIDEO_EDIT_REMOTE_BASE || '/video-edit',
// 其他配置
errorConfig: Number(process.env.NEXT_PUBLIC_ERROR_CONFIG) || 0,
};
};
/**
*
*/
export const env = getEnvConfig();
/**
*
*/
export const {
// 基础配置
nodeEnv,
isDevelopment,
isProduction,
// API 基础 URL 配置
baseUrl,
javaUrl,
cutUrl,
cutUrlTo,
// Google OAuth 配置
googleClientId,
googleRedirectUri,
// Google Analytics 配置
gaEnabled,
gaMeasurementId,
// 视频编辑配置
videoEditUseMock,
videoEditUseRemote,
videoEditRemoteBase,
// 其他配置
errorConfig,
} = env;
/**
* Google OAuth URI
*/
export const getGoogleRedirectUri = (): string => {
if (googleRedirectUri) {
return googleRedirectUri;
}
return `${javaUrl}/api/auth/google/callback`;
};
/**
* Google Analytics
*/
export const isGAAvailable = (): boolean => {
return typeof window !== 'undefined' &&
typeof window.gtag === 'function' &&
gaEnabled;
};
/**
* API
*/
export const getVideoEditApiConfig = () => {
return {
useMockApi: videoEditUseMock,
useRemoteApi: videoEditUseRemote,
remoteApiBase: videoEditRemoteBase,
localApiBase: '/api/video-edit',
enableDebugLog: isDevelopment,
};
};
/**
*
*/
export const validateEnvConfig = (): { isValid: boolean; errors: string[] } => {
const errors: string[] = [];
// 验证必需的配置
if (!baseUrl) {
errors.push('NEXT_PUBLIC_BASE_URL is required');
}
if (!javaUrl) {
errors.push('NEXT_PUBLIC_JAVA_URL is required');
}
if (!googleClientId) {
errors.push('NEXT_PUBLIC_GOOGLE_CLIENT_ID is required');
}
// 验证 URL 格式
try {
new URL(baseUrl);
} catch {
errors.push('NEXT_PUBLIC_BASE_URL must be a valid URL');
}
try {
new URL(javaUrl);
} catch {
errors.push('NEXT_PUBLIC_JAVA_URL must be a valid URL');
}
return {
isValid: errors.length === 0,
errors,
};
};
/**
*
*/
export const logEnvConfig = (): void => {
if (isDevelopment) {
console.log('🔧 环境变量配置:', {
nodeEnv,
baseUrl,
javaUrl,
cutUrl,
cutUrlTo,
googleClientId,
googleRedirectUri: getGoogleRedirectUri(),
gaEnabled,
gaMeasurementId,
videoEditUseMock,
videoEditUseRemote,
videoEditRemoteBase,
errorConfig,
});
}
};

View File

@ -2,6 +2,8 @@
*
*/
import { baseUrl } from '@/lib/env';
// 注意:这里不使用 @/api/request 中的 post 函数,因为它会将请求发送到远程服务器
// 我们需要直接调用本地的 Next.js API 路由
@ -10,8 +12,7 @@
*/
const localPost = async <T>(url: string, data: any): Promise<T> => {
try {
// 使用环境变量中的 BASE_URL生产要求使用 NEXT_PUBLIC_BASE_URL
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || '';
// 使用统一配置中的 BASE_URL
const isAbsolute = /^https?:\/\//i.test(url);
const normalizedBase = baseUrl.replace(/\/$/, '');
const normalizedPath = url.startsWith('/') ? url : `/${url}`;

View File

@ -3,6 +3,8 @@
* 访
*/
import { isGAAvailable as checkGAAvailable, gaMeasurementId } from '@/lib/env';
// 扩展全局Window接口
declare global {
interface Window {
@ -74,16 +76,14 @@ const normalizeEventParams = (
* GA是否可用
*/
export const isGAAvailable = (): boolean => {
return typeof window !== 'undefined' &&
typeof window.gtag === 'function' &&
process.env.NEXT_PUBLIC_GA_ENABLED === 'true';
return checkGAAvailable();
};
/**
* GA测量ID
*/
export const getGAMeasurementId = (): string => {
return process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID || 'G-4BDXV6TWF4';
return gaMeasurementId;
};
/**
@ -113,7 +113,7 @@ export const trackEvent = (
window.gtag('event', eventName, eventParams);
// 开发环境下打印日志
if (process.env.NODE_ENV === 'development') {
if (typeof window !== 'undefined' && window.location.hostname === 'localhost') {
console.log('GA Event:', eventName, eventParams);
}
} catch (error) {
@ -150,7 +150,7 @@ export const trackPageView = (
window.gtag('config', getGAMeasurementId(), pageParams);
// 开发环境下打印日志
if (process.env.NODE_ENV === 'development') {
if (typeof window !== 'undefined' && window.location.hostname === 'localhost') {
console.log('GA Page View:', pagePath, pageParams);
}
} catch (error) {

View File

@ -3,10 +3,7 @@
* Next.js
*/
/**
*
*/
export const isDevelopment = process.env.NODE_ENV === 'development';
import { isDevelopment } from '@/lib/env';
/**
*

View File

@ -1,6 +1,7 @@
import { notification } from 'antd';
import { downloadVideo } from './tools';
import { getGenerateEditPlan } from '@/api/video_flow';
import { cutUrl } from '@/lib/env';
/**
* -
@ -115,7 +116,7 @@ export class VideoExportService {
this.config = {
maxRetries: config.maxRetries || 3,
pollInterval: config.pollInterval || 5000, // 5秒轮询
apiBaseUrl: process.env.NEXT_PUBLIC_CUT_URL || 'https://smartcut.api.movieflow.ai'
apiBaseUrl: cutUrl
};
}
@ -857,7 +858,7 @@ export class VideoExportService {
export const videoExportService = new VideoExportService({
maxRetries: 3,
pollInterval: 5000, // 5秒轮询间隔
// apiBaseUrl 使用环境变量 NEXT_PUBLIC_CUT_URL,在构造函数中处理
// apiBaseUrl 使用统一配置中的 cutUrl,在构造函数中处理
});
/**