feat:未配置 supabase 能正常启动项目

This commit is contained in:
hzm
2026-02-09 08:15:18 +08:00
parent 10200b8c8b
commit 7ceb43e7a6
2 changed files with 75 additions and 10 deletions

View File

@@ -1,11 +1,47 @@
import { createClient } from '@supabase/supabase-js'; import { createClient } from '@supabase/supabase-js';
// Supabase 配置
// 注意:此处使用 publishable key可安全在客户端使用
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL || ''; const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL || '';
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || ''; const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || '';
export const isSupabaseConfigured = Boolean(supabaseUrl && supabaseAnonKey);
export const supabase = createClient(supabaseUrl, supabaseAnonKey, { const createNoopChannel = () => {
const channel = {
on: () => channel,
subscribe: () => channel
};
return channel;
};
const createNoopTable = () => {
return {
select: () => ({
eq: () => ({
maybeSingle: async () => ({ data: null, error: { message: 'Supabase not configured' } })
})
}),
insert: async () => ({ data: null, error: { message: 'Supabase not configured' } }),
upsert: () => ({
select: async () => ({ data: null, error: { message: 'Supabase not configured' } })
})
};
};
const createNoopSupabase = () => ({
auth: {
getSession: async () => ({ data: { session: null }, error: null }),
onAuthStateChange: () => ({
data: { subscription: { unsubscribe: () => { } } }
}),
signInWithOtp: async () => ({ data: null, error: { message: 'Supabase not configured' } }),
verifyOtp: async () => ({ data: null, error: { message: 'Supabase not configured' } }),
signOut: async () => ({ error: null })
},
from: () => createNoopTable(),
channel: () => createNoopChannel(),
removeChannel: () => { }
});
export const supabase = isSupabaseConfigured ? createClient(supabaseUrl, supabaseAnonKey, {
auth: { auth: {
// 启用自动刷新 token // 启用自动刷新 token
autoRefreshToken: true, autoRefreshToken: true,
@@ -14,4 +50,4 @@ export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
// 检测 URL 中的 session用于邮箱验证回调 // 检测 URL 中的 session用于邮箱验证回调
detectSessionInUrl: true detectSessionInUrl: true
} }
}); }) : createNoopSupabase();

View File

@@ -11,7 +11,7 @@ import Announcement from "./components/Announcement";
import { DatePicker, DonateTabs, NumericInput, Stat } from "./components/Common"; import { DatePicker, DonateTabs, NumericInput, Stat } from "./components/Common";
import { ChevronIcon, CloseIcon, CloudIcon, DragIcon, ExitIcon, GridIcon, ListIcon, LoginIcon, LogoutIcon, MailIcon, PlusIcon, RefreshIcon, SettingsIcon, SortIcon, StarIcon, TrashIcon, UpdateIcon, UserIcon } from "./components/Icons"; import { ChevronIcon, CloseIcon, CloudIcon, DragIcon, ExitIcon, GridIcon, ListIcon, LoginIcon, LogoutIcon, MailIcon, PlusIcon, RefreshIcon, SettingsIcon, SortIcon, StarIcon, TrashIcon, UpdateIcon, UserIcon } from "./components/Icons";
import githubImg from "./assets/github.svg"; import githubImg from "./assets/github.svg";
import { supabase } from './lib/supabase'; import { supabase, isSupabaseConfigured } from './lib/supabase';
import { fetchFundData, fetchLatestRelease, fetchShanghaiIndexDate, fetchSmartFundNetValue, searchFunds, submitFeedback } from './api/fund'; import { fetchFundData, fetchLatestRelease, fetchShanghaiIndexDate, fetchSmartFundNetValue, searchFunds, submitFeedback } from './api/fund';
import packageJson from '../package.json'; import packageJson from '../package.json';
@@ -2280,6 +2280,15 @@ export default function HomePage() {
}, 3000); }, 3000);
}; };
const handleOpenLogin = () => {
setUserMenuOpen(false);
if (!isSupabaseConfigured) {
showToast('未配置 Supabase无法登录', 'error');
return;
}
setLoginModalOpen(true);
};
const [updateModalOpen, setUpdateModalOpen] = useState(false); const [updateModalOpen, setUpdateModalOpen] = useState(false);
const [cloudConfigModal, setCloudConfigModal] = useState({ open: false, userId: null }); const [cloudConfigModal, setCloudConfigModal] = useState({ open: false, userId: null });
const syncDebounceRef = useRef(null); const syncDebounceRef = useRef(null);
@@ -2514,6 +2523,11 @@ export default function HomePage() {
// 初始化认证状态监听 // 初始化认证状态监听
useEffect(() => { useEffect(() => {
if (!isSupabaseConfigured) {
setUser(null);
setUserMenuOpen(false);
return;
}
const clearAuthState = () => { const clearAuthState = () => {
setUser(null); setUser(null);
setUserMenuOpen(false); setUserMenuOpen(false);
@@ -2580,7 +2594,7 @@ export default function HomePage() {
}, []); }, []);
useEffect(() => { useEffect(() => {
if (!user?.id) return; if (!isSupabaseConfigured || !user?.id) return;
const channel = supabase const channel = supabase
.channel(`user-configs-${user.id}`) .channel(`user-configs-${user.id}`)
.on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'user_configs', filter: `user_id=eq.${user.id}` }, async (payload) => { .on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'user_configs', filter: `user_id=eq.${user.id}` }, async (payload) => {
@@ -2607,6 +2621,10 @@ export default function HomePage() {
e.preventDefault(); e.preventDefault();
setLoginError(''); setLoginError('');
setLoginSuccess(''); setLoginSuccess('');
if (!isSupabaseConfigured) {
showToast('未配置 Supabase无法登录', 'error');
return;
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!loginEmail.trim()) { if (!loginEmail.trim()) {
@@ -2647,6 +2665,10 @@ export default function HomePage() {
setLoginError('请输入邮箱中的验证码'); setLoginError('请输入邮箱中的验证码');
return; return;
} }
if (!isSupabaseConfigured) {
showToast('未配置 Supabase无法登录', 'error');
return;
}
try { try {
setLoginLoading(true); setLoginLoading(true);
const { data, error } = await supabase.auth.verifyOtp({ const { data, error } = await supabase.auth.verifyOtp({
@@ -2672,6 +2694,16 @@ export default function HomePage() {
// 登出 // 登出
const handleLogout = async () => { const handleLogout = async () => {
isLoggingOutRef.current = true; isLoggingOutRef.current = true;
if (!isSupabaseConfigured) {
setLoginModalOpen(false);
setLoginError('');
setLoginSuccess('');
setLoginEmail('');
setLoginOtp('');
setUserMenuOpen(false);
setUser(null);
return;
}
try { try {
const { data: { session } } = await supabase.auth.getSession(); const { data: { session } } = await supabase.auth.getSession();
if (session) { if (session) {
@@ -3599,10 +3631,7 @@ export default function HomePage() {
<> <>
<button <button
className="user-menu-item" className="user-menu-item"
onClick={() => { onClick={handleOpenLogin}
setUserMenuOpen(false);
setLoginModalOpen(true);
}}
> >
<LoginIcon width="16" height="16" /> <LoginIcon width="16" height="16" />
<span>登录</span> <span>登录</span>