网页框架部署成功

This commit is contained in:
2025-09-04 16:03:09 +08:00
parent 71a648fdf4
commit 6f850caad1
16 changed files with 287 additions and 96 deletions

View File

@@ -1,5 +1,5 @@
// 本地后端API接口
const LOCAL_API_BASE = 'http://localhost:5000/api/60s';
const LOCAL_API_BASE = 'https://infogenie.api.shumengya.top/api/60s';
// API接口列表备用
const API_ENDPOINTS = [

View File

@@ -0,0 +1,2 @@
# 生产环境API配置
REACT_APP_API_URL=https://infogenie.api.shumengya.top

View File

@@ -44,6 +44,5 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"proxy": "http://localhost:5000"
}
}

48
frontend/react-app/public/sw.js vendored Normal file
View File

@@ -0,0 +1,48 @@
// Service Worker for InfoGenie App
const CACHE_NAME = 'infogenie-cache-v1';
const urlsToCache = [
'/',
'/index.html',
'/manifest.json'
];
// 安装Service Worker
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
// 拦截请求并从缓存中响应
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
// 如果找到缓存的响应,则返回缓存
if (response) {
return response;
}
return fetch(event.request);
})
);
});
// 更新Service Worker
self.addEventListener('activate', event => {
const cacheWhitelist = [CACHE_NAME];
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheWhitelist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
}
})
);
})
);
});

View File

@@ -9,3 +9,16 @@ root.render(
<App />
</React.StrictMode>
);
// 注册Service Worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('ServiceWorker registration successful with scope: ', registration.scope);
})
.catch(error => {
console.error('ServiceWorker registration failed: ', error);
});
});
}

View File

@@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom';
import styled from 'styled-components';
import { FiCpu, FiUser, FiExternalLink, FiArrowLeft } from 'react-icons/fi';
import { useUser } from '../contexts/UserContext';
import axios from 'axios';
import api from '../utils/api';
const AiContainer = styled.div`
min-height: calc(100vh - 140px);
@@ -262,7 +262,7 @@ const AiModelPage = () => {
const fetchApps = async () => {
try {
setLoadingApps(true);
const response = await axios.get('/api/aimodelapp/scan-directories');
const response = await api.get('/api/aimodelapp/scan-directories');
if (response.data.success) {
setApps(response.data.apps);
} else {
@@ -278,7 +278,8 @@ const AiModelPage = () => {
const handleLaunchApp = (app) => {
// 将相对路径转换为完整的服务器地址
const fullLink = `http://localhost:5000${app.link}`;
const baseUrl = process.env.REACT_APP_API_URL || 'https://infogenie.api.shumengya.top';
const fullLink = `${baseUrl}${app.link}`;
setEmbeddedApp({ ...app, link: fullLink });
};

View File

@@ -238,81 +238,26 @@ const Api60sPage = () => {
// 从后端API获取目录结构
const scanDirectories = async () => {
try {
const response = await fetch('http://localhost:5000/api/60s/scan-directories');
// 使用环境变量中配置的API URL
const baseUrl = process.env.REACT_APP_API_URL || 'https://infogenie.api.shumengya.top';
const apiUrl = `${baseUrl}/api/60s/scan-directories`;
console.log('正在请求API目录结构:', apiUrl);
const response = await fetch(apiUrl);
if (response.ok) {
const data = await response.json();
return data;
}
} catch (error) {
console.warn('无法从后端获取目录结构,使用前端扫描方式');
console.warn('无法从后端获取目录结构:', error);
}
return null;
};
// 前端扫描方式(备用)
const frontendScan = async () => {
const categories = [];
for (const [categoryName, config] of Object.entries(categoryConfig)) {
const apis = [];
// 尝试访问已知的模块列表(只包含实际存在的模块)
const knownModules = {
'热搜榜单': ['抖音热搜榜'],
'日更资讯': [],
'实用功能': [],
'娱乐消遣': []
};
// 前端扫描方式已移除
const moduleNames = knownModules[categoryName] || [];
for (let i = 0; i < moduleNames.length; i++) {
const moduleName = moduleNames[i];
try {
const indexPath = `/60sapi/${categoryName}/${moduleName}/index.html`;
const fullUrl = `http://localhost:5000${indexPath}`;
const response = await fetch(fullUrl, { method: 'HEAD' });
if (response.ok) {
// 获取页面标题
const htmlResponse = await fetch(fullUrl);
const html = await htmlResponse.text();
const titleMatch = html.match(/<title>(.*?)<\/title>/i);
const title = titleMatch ? titleMatch[1].trim() : moduleName;
apis.push({
title,
description: `${moduleName}相关功能`,
link: fullUrl,
status: 'active',
color: gradientColors[i % gradientColors.length]
});
}
} catch (error) {
// 忽略访问失败的模块
}
}
if (apis.length > 0) {
categories.push({
title: categoryName,
icon: config.icon,
color: config.color,
apis
});
}
}
return categories;
};
// 首先尝试后端扫描,失败则使用前端扫描
// 只使用后端扫描
const backendResult = await scanDirectories();
if (backendResult && backendResult.success) {
return backendResult.categories || [];
} else {
return await frontendScan();
}
return backendResult && backendResult.success ? backendResult.categories || [] : [];
} catch (error) {
console.error('扫描API模块时出错:', error);

View File

@@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react';
import styled from 'styled-components';
import { FiGrid, FiPlay, FiExternalLink, FiArrowLeft } from 'react-icons/fi';
import axios from 'axios';
import api from '../utils/api';
const GameContainer = styled.div`
min-height: calc(100vh - 140px);
@@ -233,7 +233,7 @@ const SmallGamePage = () => {
const fetchGames = async () => {
try {
setLoading(true);
const response = await axios.get('/api/smallgame/scan-directories');
const response = await api.get('/api/smallgame/scan-directories');
if (response.data.success) {
setGames(response.data.games);
} else {
@@ -249,7 +249,8 @@ const SmallGamePage = () => {
const handlePlayGame = (game) => {
// 将相对路径转换为完整的服务器地址
const fullLink = `http://localhost:5000${game.link}`;
const baseUrl = process.env.REACT_APP_API_URL || 'https://infogenie.api.shumengya.top';
const fullLink = `${baseUrl}${game.link}`;
setEmbeddedGame({ ...game, link: fullLink });
};

View File

@@ -3,7 +3,7 @@ import toast from 'react-hot-toast';
// 创建axios实例
const api = axios.create({
baseURL: process.env.REACT_APP_API_URL || '/api',
baseURL: process.env.REACT_APP_API_URL || 'https://infogenie.api.shumengya.top',
timeout: 10000,
withCredentials: true, // 支持携带cookie
headers: {
@@ -11,6 +11,9 @@ const api = axios.create({
}
});
// 打印当前使用的API URL便于调试
console.log('API Base URL:', process.env.REACT_APP_API_URL || 'https://infogenie.api.shumengya.top');
// 请求拦截器
api.interceptors.request.use(
(config) => {
@@ -44,63 +47,63 @@ api.interceptors.response.use(
// 认证相关API
export const authAPI = {
// 发送验证码
sendVerification: (data) => api.post('/auth/send-verification', data),
sendVerification: (data) => api.post('/api/auth/send-verification', data),
// 验证验证码
verifyCode: (data) => api.post('/auth/verify-code', data),
verifyCode: (data) => api.post('/api/auth/verify-code', data),
// 登录
login: (credentials) => api.post('/auth/login', credentials),
login: (credentials) => api.post('/api/auth/login', credentials),
// 注册
register: (userData) => api.post('/auth/register', userData),
register: (userData) => api.post('/api/auth/register', userData),
// 登出
logout: () => api.post('/auth/logout'),
logout: () => api.post('/api/auth/logout'),
// 检查登录状态
checkLogin: () => api.get('/auth/check'),
checkLogin: () => api.get('/api/auth/check'),
};
// 用户相关API
export const userAPI = {
// 获取用户资料
getProfile: () => api.get('/user/profile'),
getProfile: () => api.get('/api/user/profile'),
// 修改密码
changePassword: (passwordData) => api.post('/user/change-password', passwordData),
changePassword: (passwordData) => api.post('/api/user/change-password', passwordData),
// 获取用户统计
getStats: () => api.get('/user/stats'),
getStats: () => api.get('/api/user/stats'),
// 删除账户
deleteAccount: (password) => api.post('/user/delete', { password }),
deleteAccount: (password) => api.post('/api/user/delete', { password }),
};
// 60s API相关接口
export const api60s = {
// 抖音热搜
getDouyinHot: () => api.get('/60s/douyin'),
getDouyinHot: () => api.get('/api/60s/douyin'),
// 微博热搜
getWeiboHot: () => api.get('/60s/weibo'),
getWeiboHot: () => api.get('/api/60s/weibo'),
// 猫眼票房
getMaoyanBoxOffice: () => api.get('/60s/maoyan'),
getMaoyanBoxOffice: () => api.get('/api/60s/maoyan'),
// 60秒读懂世界
get60sNews: () => api.get('/60s/60s'),
get60sNews: () => api.get('/api/60s/60s'),
// 必应壁纸
getBingWallpaper: () => api.get('/60s/bing-wallpaper'),
getBingWallpaper: () => api.get('/api/60s/bing-wallpaper'),
// 天气信息
getWeather: (city = '北京') => api.get(`/60s/weather?city=${encodeURIComponent(city)}`),
getWeather: (city = '北京') => api.get(`/api/60s/weather?city=${encodeURIComponent(city)}`),
};
// 健康检查
export const healthAPI = {
check: () => api.get('/health'),
check: () => api.get('/api/health'),
};
export default api;