feat: Add application domain and file domain configuration

This commit is contained in:
shiyu
2025-08-31 12:38:21 +08:00
parent 3f3f192d53
commit b50f19bcb4
9 changed files with 30 additions and 12 deletions

View File

@@ -20,6 +20,8 @@ export interface SystemStatus {
title: string;
logo: string;
is_initialized: boolean;
app_domain?: string;
file_domain?: string;
}
export async function status() {

View File

@@ -51,7 +51,7 @@ export const vfsApi = {
streamUrl: (path: string) => `${API_BASE_URL}/fs/stream/${encodeURI(path.replace(/^\/+/, ''))}`,
stat: (path: string) => request(`/fs/stat/${encodeURI(path.replace(/^\/+/, ''))}`),
getTempLinkToken: (path: string, expiresIn: number = 3600) =>
request<{token: string}>(`/fs/temp-link/${encodeURI(path.replace(/^\/+/, ''))}?expires_in=${expiresIn}`),
request<{token: string, path: string, url: string}>(`/fs/temp-link/${encodeURI(path.replace(/^\/+/, ''))}?expires_in=${expiresIn}`),
getTempPublicUrl: (token: string) => `${API_BASE_URL}/fs/public/${token}`,
uploadStream: (fullPath: string, file: File, overwrite: boolean = true, onProgress?: (loaded: number, total: number) => void) => {
const enc = encodeURI(fullPath.replace(/^\/+/, ''));

View File

@@ -2,8 +2,10 @@ import React, { useEffect, useState } from 'react';
import { vfsApi } from '../../api/client';
import type { AppComponentProps } from '../types';
import { Spin, Result, Button } from 'antd';
import { useSystemStatus } from '../../contexts/SystemContext';
export const OfficeViewerApp: React.FC<AppComponentProps> = ({ filePath, onRequestClose }) => {
const systemStatus = useSystemStatus();
const [url, setUrl] = useState<string>();
const [loading, setLoading] = useState(true);
const [err, setErr] = useState<string>();
@@ -17,8 +19,8 @@ export const OfficeViewerApp: React.FC<AppComponentProps> = ({ filePath, onReque
vfsApi.getTempLinkToken(filePath.replace(/^\/+/, ''))
.then(res => {
if (cancelled) return;
// 注意vfsApi.getTempPublicUrl 返回的是相对路径,我们需要构建完整的 URL
const fullUrl = new URL(vfsApi.getTempPublicUrl(res.token), window.location.origin).href;
const baseUrl = systemStatus?.file_domain || window.location.origin;
const fullUrl = new URL(res.url, baseUrl).href;
const officeUrl = `https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(fullUrl)}`;
setUrl(officeUrl);
})

View File

@@ -29,8 +29,7 @@ export const DirectLinkModal = memo(function DirectLinkModal({ entry, path, open
try {
const fullPath = (path === '/' ? '' : path) + '/' + entry.name;
const res = await vfsApi.getTempLinkToken(fullPath, expiresIn);
const tempLink = `${window.location.origin}/api/fs/public/${res.token}`;
setLink(tempLink);
setLink(res.url);
} catch (e: any) {
message.error(e.message || '生成链接失败');
} finally {

View File

@@ -3,6 +3,7 @@ import { Modal, Form, Input, Radio, InputNumber, message, Button, Typography } f
import { CopyOutlined } from '@ant-design/icons';
import type { VfsEntry, ShareInfoWithPassword } from '../../../../api/client';
import { shareApi } from '../../../../api/share';
import { useSystemStatus } from '../../../../contexts/SystemContext';
interface ShareModalProps {
entries: VfsEntry[];
@@ -13,6 +14,7 @@ interface ShareModalProps {
}
export const ShareModal = memo(function ShareModal({ entries, path, open, onOk, onCancel }: ShareModalProps) {
const systemStatus = useSystemStatus();
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
const [accessType, setAccessType] = useState('public');
@@ -66,7 +68,8 @@ export const ShareModal = memo(function ShareModal({ entries, path, open, onOk,
message.success('已复制到剪贴板');
};
const shareUrl = createdShare ? `${window.location.origin}/share/${createdShare.token}` : '';
const baseUrl = systemStatus?.app_domain || window.location.origin;
const shareUrl = createdShare ? new URL(`/share/${createdShare.token}`, baseUrl).href : '';
const renderForm = () => (
<Form form={form} layout="vertical" initialValues={{ name: defaultName, accessType: 'public', expiresInDays: 7 }}>

View File

@@ -4,8 +4,10 @@ import PageCard from '../components/PageCard';
import { shareApi, type ShareInfo } from '../api/share';
import { format, parseISO } from 'date-fns';
import { LinkOutlined, CopyOutlined, DeleteOutlined } from '@ant-design/icons';
import { useSystemStatus } from '../contexts/SystemContext';
const SharePage = memo(function SharePage() {
const systemStatus = useSystemStatus();
const [loading, setLoading] = useState(false);
const [data, setData] = useState<ShareInfo[]>([]);
@@ -24,7 +26,8 @@ const SharePage = memo(function SharePage() {
useEffect(() => { fetchList(); }, [fetchList]);
const doCopy = (rec: ShareInfo) => {
const shareUrl = `${window.location.origin}/share/${rec.token}`;
const baseUrl = systemStatus?.app_domain || window.location.origin;
const shareUrl = new URL(`/share/${rec.token}`, baseUrl).href;
navigator.clipboard.writeText(shareUrl);
message.success('链接已复制');
};

View File

@@ -2,13 +2,13 @@ import { Form, Input, Button, message, Tabs, Space, Card } from 'antd';
import { useEffect, useState } from 'react';
import PageCard from '../../components/PageCard';
import { getAllConfig, setConfig } from '../../api/config';
import { API_BASE_URL } from '../../api/client';
import { AppstoreOutlined, RobotOutlined } from '@ant-design/icons';
const APP_CONFIG_KEYS = [
const APP_CONFIG_KEYS: {key: string, label: string, default?: string}[] = [
{ key: 'APP_NAME', label: '应用名称' },
{ key: 'APP_LOGO', label: 'LOGO地址' },
{ key: 'SERVER_URL', label: '服务端URL', default: API_BASE_URL },
{ key: 'APP_DOMAIN', label: '应用域名' },
{ key: 'FILE_DOMAIN', label: '文件域名' },
];
const VISION_CONFIG_KEYS = [