diff --git a/src/api/1panel.ts b/src/api/1panel.ts index 39258bf..233e3e0 100644 --- a/src/api/1panel.ts +++ b/src/api/1panel.ts @@ -447,5 +447,266 @@ export async function updateSSLCertificate(params: UpdateSSLParams): Promise { + const response = await panelService.get>('/hosts/info') + return response.data.data +} + +/** + * 获取系统资源使用状态 + */ +export async function getSystemResourceInfo(): Promise { + const response = await panelService.get>('/dashboard/current') + return response.data.data +} + +/** + * 获取系统实时状态 + */ +export async function getRealtimeStatus(): Promise { + const response = await panelService.post>('/dashboard/current', { + ioOption: 'all', + netOption: 'all' + }) + return response.data.data +} + +// ==================== Nginx 配置 API ==================== + +/** + * Nginx 配置信息 + */ +export interface NginxConfig { + id: number + websiteId: number + operate: string + params?: Record +} + +/** + * 网站配置详情 + */ +export interface WebsiteConfig { + config: string +} + +/** + * 获取网站 Nginx 配置 + * @param websiteId 网站ID + */ +export async function getWebsiteNginxConfig(websiteId: number): Promise { + const response = await panelService.get>(`/websites/${websiteId}/config/nginx`) + return response.data.data.config +} + +/** + * 更新网站 Nginx 配置 + * @param websiteId 网站ID + * @param config 配置内容 + */ +export async function updateWebsiteNginxConfig(websiteId: number, config: string): Promise { + await panelService.post(`/websites/${websiteId}/config/nginx`, { config }) +} + +/** + * 重载 Nginx + */ +export async function reloadNginx(): Promise { + await panelService.post('/websites/nginx/update', { operate: 'reload' }) +} + +// ==================== 应用管理 API ==================== + +/** + * 已安装应用信息 + */ +export interface InstalledApp { + id: number + name: string + appKey: string + version: string + status: string + message: string + httpPort: number + httpsPort: number + path: string + canUpdate: boolean + createdAt: string + updatedAt: string +} + +/** + * 获取已安装应用列表 + */ +export async function getInstalledApps(): Promise { + const response = await panelService.post>('/apps/installed/search', { + page: 1, + pageSize: 100, + all: true + }) + return response.data.data.items || [] +} + +/** + * 操作应用 + * @param installId 安装ID + * @param operate 操作类型:start, stop, restart + */ +export async function operateApp(installId: number, operate: 'start' | 'stop' | 'restart'): Promise { + await panelService.post('/apps/installed/operate', { + installId, + operate + }) +} + +// ==================== 文件管理 API ==================== + +/** + * 文件信息 + */ +export interface FileInfo { + name: string + size: number + mode: string + modeNum: string + isDir: boolean + isSymlink: boolean + linkPath: string + path: string + extension: string + user: string + group: string + uid: string + gid: string + modTime: string +} + +/** + * 获取目录文件列表 + * @param path 目录路径 + */ +export async function getFileList(path: string): Promise { + const response = await panelService.post>('/files/search', { + path, + expand: true, + page: 1, + pageSize: 100 + }) + return response.data.data || [] +} + +/** + * 上传文件 + * @param path 目标路径 + * @param file 文件 + */ +export async function uploadFile(path: string, file: File): Promise { + const formData = new FormData() + formData.append('file', file) + formData.append('path', path) + + await panelService.post('/files/upload', formData, { + headers: { + 'Content-Type': 'multipart/form-data' + } + }) +} + +/** + * 解压文件 + * @param path 文件路径 + * @param dst 解压目标路径 + */ +export async function decompressFile(path: string, dst: string): Promise { + await panelService.post('/files/decompress', { + path, + dst, + type: path.endsWith('.tar.gz') ? 'tar.gz' : 'zip' + }) +} + // 导出 panelService 实例供其他地方使用 export default panelService + diff --git a/src/components.d.ts b/src/components.d.ts index a05a025..867fd90 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -20,6 +20,8 @@ declare module 'vue' { ACard: typeof import('ant-design-vue/es')['Card'] ACheckbox: typeof import('ant-design-vue/es')['Checkbox'] ACol: typeof import('ant-design-vue/es')['Col'] + ACollapse: typeof import('ant-design-vue/es')['Collapse'] + ACollapsePanel: typeof import('ant-design-vue/es')['CollapsePanel'] ADatePicker: typeof import('ant-design-vue/es')['DatePicker'] ADescriptions: typeof import('ant-design-vue/es')['Descriptions'] ADescriptionsItem: typeof import('ant-design-vue/es')['DescriptionsItem'] diff --git a/src/config/project.ts b/src/config/project.ts index c52e57f..fe64d2f 100644 --- a/src/config/project.ts +++ b/src/config/project.ts @@ -133,14 +133,22 @@ export const platformModule: ModuleConfig = { children: [ { key: 'platform-projects', label: '项目管理', path: '/platform/projects' }, { key: 'platform-menus', label: '菜单管理', path: '/platform/menus' }, - { key: 'platform-certificates', label: '证书管理', path: '/platform/certificates' } + { key: 'platform-servers', label: '服务器管理', path: '/platform/servers' }, + { key: 'platform-domains', label: '域名管理', path: '/platform/domains' }, + { key: 'platform-certificates', label: '证书管理', path: '/platform/certificates' }, + { key: 'platform-environments', label: '环境配置', path: '/platform/environments' }, + { key: 'platform-log-center', label: '日志中心', path: '/platform/log-center' } ] } ], routes: [ { path: 'platform/projects', name: 'PlatformProjects', component: '@/views/platform/projects/index.vue', title: '项目管理' }, { path: 'platform/menus', name: 'PlatformMenus', component: '@/views/platform/menus/index.vue', title: '菜单管理' }, - { path: 'platform/certificates', name: 'PlatformCertificates', component: '@/views/platform/certificates/index.vue', title: '证书管理' } + { path: 'platform/servers', name: 'PlatformServers', component: '@/views/platform/servers/index.vue', title: '服务器管理' }, + { path: 'platform/domains', name: 'PlatformDomains', component: '@/views/platform/domains/index.vue', title: '域名管理' }, + { path: 'platform/certificates', name: 'PlatformCertificates', component: '@/views/platform/certificates/index.vue', title: '证书管理' }, + { path: 'platform/environments', name: 'PlatformEnvironments', component: '@/views/platform/environments/index.vue', title: '环境配置' }, + { path: 'platform/log-center', name: 'PlatformLogCenter', component: '@/views/platform/log-center/index.vue', title: '日志中心' } ] } diff --git a/src/mock/platform.ts b/src/mock/platform.ts new file mode 100644 index 0000000..2db6712 --- /dev/null +++ b/src/mock/platform.ts @@ -0,0 +1,566 @@ +/** + * 平台管理模块 Mock 数据 + */ + +import type { + ServerInfo, + DeployRecord, + DeployConfig, + LogRecord, + DomainInfo, + EnvironmentInfo +} from '@/types/platform' + +// ==================== 服务器管理 Mock 数据 ==================== + +export const mockServers: ServerInfo[] = [ + { + id: 'server-001', + name: '生产服务器-主', + ip: '47.108.xxx.xxx', + internalIp: '172.16.0.1', + port: 22, + type: 'cloud', + status: 'online', + os: 'Ubuntu 22.04 LTS', + cpu: { cores: 8, usage: 35 }, + memory: { total: 16, used: 8.5, usage: 53 }, + disk: { total: 200, used: 85, usage: 42 }, + tags: ['生产', '主节点'], + sshUser: 'root', + sshPort: 22, + description: '主生产服务器,部署核心业务', + createdAt: '2024-01-01T00:00:00Z', + updatedAt: '2024-12-29T10:00:00Z', + panelUrl: 'https://panel.example.com', + panelPort: 8888 + }, + { + id: 'server-002', + name: '生产服务器-从', + ip: '47.108.xxx.xxx', + internalIp: '172.16.0.2', + port: 22, + type: 'cloud', + status: 'online', + os: 'Ubuntu 22.04 LTS', + cpu: { cores: 4, usage: 22 }, + memory: { total: 8, used: 3.2, usage: 40 }, + disk: { total: 100, used: 35, usage: 35 }, + tags: ['生产', '从节点'], + sshUser: 'root', + sshPort: 22, + description: '从生产服务器,负载均衡', + createdAt: '2024-01-15T00:00:00Z', + updatedAt: '2024-12-29T10:00:00Z' + }, + { + id: 'server-003', + name: '测试服务器', + ip: '192.168.1.100', + port: 22, + type: 'virtual', + status: 'online', + os: 'CentOS 7.9', + cpu: { cores: 4, usage: 15 }, + memory: { total: 8, used: 2.1, usage: 26 }, + disk: { total: 100, used: 25, usage: 25 }, + tags: ['测试'], + sshUser: 'root', + sshPort: 22, + description: '测试环境服务器', + createdAt: '2024-03-01T00:00:00Z', + updatedAt: '2024-12-28T15:00:00Z' + }, + { + id: 'server-004', + name: '开发服务器', + ip: '192.168.1.101', + port: 22, + type: 'virtual', + status: 'warning', + os: 'Ubuntu 20.04 LTS', + cpu: { cores: 2, usage: 85 }, + memory: { total: 4, used: 3.5, usage: 87 }, + disk: { total: 50, used: 42, usage: 84 }, + tags: ['开发'], + sshUser: 'dev', + sshPort: 22, + description: '开发环境服务器,资源告警', + createdAt: '2024-06-01T00:00:00Z', + updatedAt: '2024-12-29T09:00:00Z' + }, + { + id: 'server-005', + name: '备份服务器', + ip: '172.16.0.100', + port: 22, + type: 'physical', + status: 'offline', + os: 'Debian 11', + cpu: { cores: 4, usage: 0 }, + memory: { total: 16, used: 0, usage: 0 }, + disk: { total: 2000, used: 850, usage: 42 }, + tags: ['备份', '离线'], + sshUser: 'backup', + sshPort: 2222, + description: '数据备份服务器,当前离线维护中', + createdAt: '2024-02-01T00:00:00Z', + updatedAt: '2024-12-20T00:00:00Z' + } +] + +// ==================== 部署管理 Mock 数据 ==================== + +export const mockDeployRecords: DeployRecord[] = [ + { + id: 'deploy-001', + projectId: 'codePort', + projectName: 'CodePort 码头', + serverId: 'server-001', + serverName: '生产服务器-主', + serverIp: '47.108.xxx.xxx', + version: '1.2.0', + previousVersion: '1.1.5', + status: 'success', + type: 'manual', + operator: '管理员', + startTime: '2024-12-29T10:00:00Z', + endTime: '2024-12-29T10:05:32Z', + duration: 332, + files: [ + { name: 'dist.zip', size: 15728640, path: '/opt/1panel/www/sites/codeport/index' } + ], + config: { + preScript: 'pm2 stop codeport', + postScript: 'pm2 start codeport', + backup: true, + backupPath: '/opt/backups/codeport' + }, + createdAt: '2024-12-29T10:00:00Z' + }, + { + id: 'deploy-002', + projectId: 'codePort', + projectName: 'CodePort 码头', + serverId: 'server-002', + serverName: '生产服务器-从', + serverIp: '47.108.xxx.xxx', + version: '1.2.0', + previousVersion: '1.1.5', + status: 'success', + type: 'manual', + operator: '管理员', + startTime: '2024-12-29T10:06:00Z', + endTime: '2024-12-29T10:10:15Z', + duration: 255, + files: [ + { name: 'dist.zip', size: 15728640, path: '/opt/1panel/www/sites/codeport/index' } + ], + config: { + backup: true + }, + createdAt: '2024-12-29T10:06:00Z' + }, + { + id: 'deploy-003', + projectId: 'codePort', + projectName: 'CodePort 码头', + serverId: 'server-003', + serverName: '测试服务器', + serverIp: '192.168.1.100', + version: '1.2.1-beta', + previousVersion: '1.2.0', + status: 'deploying', + type: 'auto', + operator: 'CI/CD', + startTime: '2024-12-29T12:00:00Z', + files: [ + { name: 'dist.zip', size: 15890432, path: '/opt/apps/codeport' } + ], + logs: [ + '开始部署...', + '正在上传文件...', + '文件上传完成', + '正在解压文件...', + '解压完成', + '正在执行前置脚本...' + ], + createdAt: '2024-12-29T12:00:00Z' + }, + { + id: 'deploy-004', + projectId: 'codePort', + projectName: 'CodePort 码头', + serverId: 'server-001', + serverName: '生产服务器-主', + serverIp: '47.108.xxx.xxx', + version: '1.1.4', + previousVersion: '1.1.3', + status: 'failed', + type: 'manual', + operator: '张三', + startTime: '2024-12-28T15:00:00Z', + endTime: '2024-12-28T15:02:45Z', + duration: 165, + files: [ + { name: 'dist.zip', size: 14523648, path: '/opt/1panel/www/sites/codeport/index' } + ], + errorMessage: '前置脚本执行失败:pm2 进程不存在', + createdAt: '2024-12-28T15:00:00Z' + }, + { + id: 'deploy-005', + projectId: 'codePort', + projectName: 'CodePort 码头', + serverId: 'server-001', + serverName: '生产服务器-主', + serverIp: '47.108.xxx.xxx', + version: '1.1.3', + previousVersion: '1.1.5', + status: 'rollback', + type: 'manual', + operator: '管理员', + startTime: '2024-12-27T18:00:00Z', + endTime: '2024-12-27T18:03:00Z', + duration: 180, + files: [], + createdAt: '2024-12-27T18:00:00Z' + } +] + +export const mockDeployConfigs: DeployConfig[] = [ + { + id: 'config-001', + projectId: 'codePort', + serverId: 'server-001', + deployPath: '/opt/1panel/www/sites/codeport/index', + preScript: 'pm2 stop codeport || true', + postScript: 'pm2 start codeport', + backup: true, + backupPath: '/opt/backups/codeport', + backupCount: 5, + autoRestart: true, + healthCheck: { + enabled: true, + url: 'https://codeport.example.com/api/health', + timeout: 30, + retries: 3 + } + } +] + +// ==================== 日志中心 Mock 数据 ==================== + +export const mockLogs: LogRecord[] = [ + { + id: 'log-001', + type: 'operation', + level: 'info', + module: '项目管理', + action: '创建项目', + content: '创建了新项目:CodePort 码头', + operator: '管理员', + operatorIp: '192.168.1.1', + projectId: 'codePort', + projectName: 'CodePort 码头', + createdAt: '2024-12-29T12:00:00Z' + }, + { + id: 'log-002', + type: 'deploy', + level: 'info', + module: '部署管理', + action: '部署成功', + content: '项目 CodePort 码头 v1.2.0 部署成功', + operator: '管理员', + operatorIp: '192.168.1.1', + projectId: 'codePort', + projectName: 'CodePort 码头', + serverId: 'server-001', + serverName: '生产服务器-主', + duration: 332000, + createdAt: '2024-12-29T10:05:32Z' + }, + { + id: 'log-003', + type: 'error', + level: 'error', + module: '部署管理', + action: '部署失败', + content: '项目 CodePort 码头 v1.1.4 部署失败:前置脚本执行失败', + operator: '张三', + operatorIp: '192.168.1.2', + projectId: 'codePort', + projectName: 'CodePort 码头', + serverId: 'server-001', + serverName: '生产服务器-主', + createdAt: '2024-12-28T15:02:45Z', + metadata: { + errorCode: 'SCRIPT_FAILED', + errorDetail: 'pm2 进程不存在' + } + }, + { + id: 'log-004', + type: 'access', + level: 'info', + module: '用户认证', + action: '用户登录', + content: '用户 管理员 登录成功', + operator: '管理员', + operatorIp: '192.168.1.1', + createdAt: '2024-12-29T08:00:00Z' + }, + { + id: 'log-005', + type: 'system', + level: 'warn', + module: '服务器监控', + action: '资源告警', + content: '服务器 开发服务器 CPU 使用率超过 80%', + serverId: 'server-004', + serverName: '开发服务器', + createdAt: '2024-12-29T09:30:00Z' + }, + { + id: 'log-006', + type: 'operation', + level: 'info', + module: '证书管理', + action: '申请证书', + content: '为域名 codeport.example.com 申请了 SSL 证书', + operator: '管理员', + operatorIp: '192.168.1.1', + createdAt: '2024-12-28T10:00:00Z' + }, + { + id: 'log-007', + type: 'system', + level: 'error', + module: '服务器监控', + action: '服务器离线', + content: '服务器 备份服务器 已离线', + serverId: 'server-005', + serverName: '备份服务器', + createdAt: '2024-12-20T00:00:00Z' + }, + { + id: 'log-008', + type: 'deploy', + level: 'info', + module: '部署管理', + action: '版本回退', + content: '项目 CodePort 码头 从 v1.1.5 回退到 v1.1.3', + operator: '管理员', + operatorIp: '192.168.1.1', + projectId: 'codePort', + projectName: 'CodePort 码头', + serverId: 'server-001', + serverName: '生产服务器-主', + createdAt: '2024-12-27T18:03:00Z' + } +] + +// ==================== 域名管理 Mock 数据 ==================== + +export const mockDomains: DomainInfo[] = [ + { + id: 'domain-001', + domain: 'codeport.nanxiislet.com', + projectId: 'codePort', + projectName: 'CodePort 码头', + serverId: 'server-001', + serverName: '生产服务器-主', + serverIp: '47.108.xxx.xxx', + status: 'active', + dnsStatus: 'resolved', + dnsRecords: [ + { type: 'A', value: '47.108.xxx.xxx' } + ], + sslStatus: 'valid', + sslExpireDate: '2025-03-18', + certificateId: 'cert-001', + certificateName: 'codeport.nanxiislet.com', + nginxConfigPath: '/opt/1panel/www/sites/codeport/nginx.conf', + proxyPass: 'http://127.0.0.1:3000', + port: 443, + enableHttps: true, + forceHttps: true, + description: 'CodePort 主站域名', + createdAt: '2024-01-01T00:00:00Z', + updatedAt: '2024-12-29T10:00:00Z' + }, + { + id: 'domain-002', + domain: 'api.codeport.nanxiislet.com', + projectId: 'codePort', + projectName: 'CodePort 码头', + serverId: 'server-001', + serverName: '生产服务器-主', + serverIp: '47.108.xxx.xxx', + status: 'active', + dnsStatus: 'resolved', + dnsRecords: [ + { type: 'A', value: '47.108.xxx.xxx' } + ], + sslStatus: 'valid', + sslExpireDate: '2025-03-18', + certificateId: 'cert-002', + proxyPass: 'http://127.0.0.1:3001', + port: 443, + enableHttps: true, + forceHttps: true, + description: 'CodePort API 域名', + createdAt: '2024-01-01T00:00:00Z', + updatedAt: '2024-12-29T10:00:00Z' + }, + { + id: 'domain-003', + domain: 'admin.nanxiislet.com', + projectId: undefined, + projectName: undefined, + serverId: 'server-001', + serverName: '生产服务器-主', + serverIp: '47.108.xxx.xxx', + status: 'active', + dnsStatus: 'resolved', + sslStatus: 'expiring', + sslExpireDate: '2025-01-15', + certificateId: 'cert-003', + proxyPass: 'http://127.0.0.1:8080', + port: 443, + enableHttps: true, + forceHttps: true, + description: '管理后台', + createdAt: '2024-03-01T00:00:00Z', + updatedAt: '2024-12-29T10:00:00Z' + }, + { + id: 'domain-004', + domain: 'test.nanxiislet.com', + serverId: 'server-003', + serverName: '测试服务器', + serverIp: '192.168.1.100', + status: 'pending', + dnsStatus: 'unresolved', + sslStatus: 'none', + port: 80, + enableHttps: false, + forceHttps: false, + description: '测试域名', + createdAt: '2024-12-01T00:00:00Z', + updatedAt: '2024-12-29T10:00:00Z' + }, + { + id: 'domain-005', + domain: 'old.nanxiislet.com', + status: 'expired', + dnsStatus: 'unresolved', + sslStatus: 'expired', + sslExpireDate: '2024-06-01', + enableHttps: true, + forceHttps: false, + description: '已废弃的旧域名', + createdAt: '2023-01-01T00:00:00Z', + updatedAt: '2024-06-01T00:00:00Z' + } +] + +// ==================== 环境配置 Mock 数据 ==================== + +export const mockEnvironments: EnvironmentInfo[] = [ + { + id: 'env-001', + name: '开发环境', + type: 'development', + projectId: 'codePort', + projectName: 'CodePort 码头', + serverId: 'server-004', + serverName: '开发服务器', + description: '本地开发使用的环境配置', + variables: [ + { id: 'var-001', key: 'NODE_ENV', value: 'development', isSecret: false, createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z' }, + { id: 'var-002', key: 'API_BASE_URL', value: 'http://localhost:3001', isSecret: false, createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z' }, + { id: 'var-003', key: 'DATABASE_URL', value: 'mysql://dev:****@localhost:3306/codeport_dev', isSecret: true, createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z' }, + { id: 'var-004', key: 'REDIS_URL', value: 'redis://localhost:6379', isSecret: false, createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z' }, + { id: 'var-005', key: 'JWT_SECRET', value: '********', isSecret: true, createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z' } + ], + createdAt: '2024-01-01T00:00:00Z', + updatedAt: '2024-12-29T10:00:00Z' + }, + { + id: 'env-002', + name: '测试环境', + type: 'testing', + projectId: 'codePort', + projectName: 'CodePort 码头', + serverId: 'server-003', + serverName: '测试服务器', + description: '测试使用的环境配置', + variables: [ + { id: 'var-006', key: 'NODE_ENV', value: 'testing', isSecret: false, createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z' }, + { id: 'var-007', key: 'API_BASE_URL', value: 'http://test.nanxiislet.com/api', isSecret: false, createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z' }, + { id: 'var-008', key: 'DATABASE_URL', value: 'mysql://test:****@192.168.1.100:3306/codeport_test', isSecret: true, createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z' }, + { id: 'var-009', key: 'REDIS_URL', value: 'redis://192.168.1.100:6379', isSecret: false, createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z' }, + { id: 'var-010', key: 'JWT_SECRET', value: '********', isSecret: true, createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z' } + ], + createdAt: '2024-01-01T00:00:00Z', + updatedAt: '2024-12-29T10:00:00Z' + }, + { + id: 'env-003', + name: '生产环境', + type: 'production', + projectId: 'codePort', + projectName: 'CodePort 码头', + serverId: 'server-001', + serverName: '生产服务器-主', + description: '生产环境配置', + variables: [ + { id: 'var-011', key: 'NODE_ENV', value: 'production', isSecret: false, createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z' }, + { id: 'var-012', key: 'API_BASE_URL', value: 'https://api.codeport.nanxiislet.com', isSecret: false, createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z' }, + { id: 'var-013', key: 'DATABASE_URL', value: 'mysql://prod:****@rds.example.com:3306/codeport_prod', isSecret: true, createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z' }, + { id: 'var-014', key: 'REDIS_URL', value: 'redis://redis.example.com:6379', isSecret: false, createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z' }, + { id: 'var-015', key: 'JWT_SECRET', value: '********', isSecret: true, createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z' }, + { id: 'var-016', key: 'CDN_URL', value: 'https://cdn.nanxiislet.com', isSecret: false, createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z' }, + { id: 'var-017', key: 'OSS_BUCKET', value: 'nanxiislet-prod', isSecret: false, createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z' }, + { id: 'var-018', key: 'OSS_ACCESS_KEY', value: '********', isSecret: true, createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z' } + ], + createdAt: '2024-01-01T00:00:00Z', + updatedAt: '2024-12-29T10:00:00Z' + } +] + +// ==================== 工具函数 ==================== + +export function getServerById(id: string): ServerInfo | undefined { + return mockServers.find(s => s.id === id) +} + +export function getDeployRecordsByProject(projectId: string): DeployRecord[] { + return mockDeployRecords.filter(d => d.projectId === projectId) +} + +export function getLogsByFilter(filter: Partial<{ + type: string + level: string + projectId: string + serverId: string +}>): LogRecord[] { + return mockLogs.filter(log => { + if (filter.type && log.type !== filter.type) return false + if (filter.level && log.level !== filter.level) return false + if (filter.projectId && log.projectId !== filter.projectId) return false + if (filter.serverId && log.serverId !== filter.serverId) return false + return true + }) +} + +export function getDomainsByServer(serverId: string): DomainInfo[] { + return mockDomains.filter(d => d.serverId === serverId) +} + +export function getEnvironmentsByProject(projectId: string): EnvironmentInfo[] { + return mockEnvironments.filter(e => e.projectId === projectId) +} diff --git a/src/mock/projects.ts b/src/mock/projects.ts index bd38d4f..af1c511 100644 --- a/src/mock/projects.ts +++ b/src/mock/projects.ts @@ -20,6 +20,9 @@ export interface ProjectVersion { description: string // 版本描述 createdAt: string // 创建时间 createdBy: string // 创建人 + isCurrent?: boolean // 是否是当前运行版本 + // 版本关联的文件列表 + files?: { name: string; size: number; path?: string }[] // 版本快照数据 snapshot: { name: string @@ -151,11 +154,52 @@ export const mockProjects: PlatformProject[] = [ currentVersion: '1.0.0', versions: [ { - id: 'v1', + id: 'v1.2.0', + version: '1.2.0', + description: '新增了客服系统和会话管理模块', + createdAt: '2024-03-15T10:00:00Z', + createdBy: '管理员', + isCurrent: true, + files: [ + { name: 'dist.zip', size: 5242880, path: '/opt/1panel/www/sites/codeport/index' } + ], + snapshot: { + name: 'CodePort 码头', + shortName: 'CodePort', + logo: '码', + color: '#1890ff', + description: '人才外包平台管理后台', + baseUrl: 'http://localhost:5174', + menus: codePortMenus + } + }, + { + id: 'v1.1.0', + version: '1.1.0', + description: '优化了用户管理和项目列表', + createdAt: '2024-02-01T10:00:00Z', + createdBy: '管理员', + isCurrent: false, + files: [ + { name: 'dist.zip', size: 4718592, path: '/opt/1panel/www/sites/codeport/index' } + ], + snapshot: { + name: 'CodePort 码头', + shortName: 'CodePort', + logo: '码', + color: '#1890ff', + description: '人才外包平台管理后台', + baseUrl: 'http://localhost:5174', + menus: codePortMenus + } + }, + { + id: 'v1.0.0', version: '1.0.0', description: '初始版本', createdAt: '2024-01-01T00:00:00Z', createdBy: '管理员', + isCurrent: false, snapshot: { name: 'CodePort 码头', shortName: 'CodePort', diff --git a/src/router/index.ts b/src/router/index.ts index 70baec2..eb20c54 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -157,6 +157,42 @@ const routes: RouteRecordRaw[] = [ requiresAuth: true } }, + { + path: 'platform/servers', + name: 'PlatformServers', + component: () => import('@/views/platform/servers/index.vue'), + meta: { + title: '服务器管理', + requiresAuth: true + } + }, + { + path: 'platform/log-center', + name: 'PlatformLogCenter', + component: () => import('@/views/platform/log-center/index.vue'), + meta: { + title: '日志中心', + requiresAuth: true + } + }, + { + path: 'platform/domains', + name: 'PlatformDomains', + component: () => import('@/views/platform/domains/index.vue'), + meta: { + title: '域名管理', + requiresAuth: true + } + }, + { + path: 'platform/environments', + name: 'PlatformEnvironments', + component: () => import('@/views/platform/environments/index.vue'), + meta: { + title: '环境配置', + requiresAuth: true + } + }, // 系统设置 { diff --git a/src/stores/project.ts b/src/stores/project.ts index 3bb33a5..5db8de2 100644 --- a/src/stores/project.ts +++ b/src/stores/project.ts @@ -263,6 +263,36 @@ export const useProjectStore = defineStore('project', () => { return map } + /** + * 为指定版本添加文件 + */ + function addFilesToVersion( + projectId: string, + versionId: string, + files: { name: string; size: number }[] + ): boolean { + const project = projects.value.find(p => p.id === projectId) + if (!project || !project.versions) return false + + const version = project.versions.find(v => v.id === versionId) + if (!version) return false + + // 初始化 files 数组 + if (!version.files) { + version.files = [] + } + + // 添加新文件(避免重复) + for (const file of files) { + const exists = version.files.some(f => f.name === file.name) + if (!exists) { + version.files.push(file) + } + } + + return true + } + return { // 状态 projects, @@ -285,6 +315,7 @@ export const useProjectStore = defineStore('project', () => { rollbackToVersion, getProjectVersions, deleteVersion, + addFilesToVersion, // 菜单路由方法 getMenuRoutePath, getMenuRouteMap diff --git a/src/types/platform.ts b/src/types/platform.ts new file mode 100644 index 0000000..b83004b --- /dev/null +++ b/src/types/platform.ts @@ -0,0 +1,223 @@ +/** + * 平台管理模块类型定义 + */ + +// ==================== 服务器管理 ==================== + +export type ServerStatus = 'online' | 'offline' | 'warning' | 'maintenance' +export type ServerType = 'physical' | 'virtual' | 'cloud' + +export interface ServerInfo { + id: string + name: string + ip: string + internalIp?: string + port: number + type: ServerType + status: ServerStatus + os: string + cpu: { + cores: number + usage: number // 百分比 + } + memory: { + total: number // GB + used: number // GB + usage: number // 百分比 + } + disk: { + total: number // GB + used: number // GB + usage: number // 百分比 + } + tags: string[] + sshUser?: string + sshPort?: number + description?: string + createdAt: string + updatedAt: string + // 1Panel 信息 + panelUrl?: string + panelPort?: number +} + +// ==================== 部署管理 ==================== + +export type DeployStatus = 'pending' | 'deploying' | 'success' | 'failed' | 'rollback' +export type DeployType = 'manual' | 'auto' | 'webhook' + +export interface DeployRecord { + id: string + projectId: string + projectName: string + serverId: string + serverName: string + serverIp: string + version: string + previousVersion?: string + status: DeployStatus + type: DeployType + operator: string + startTime: string + endTime?: string + duration?: number // 秒 + logs?: string[] + files: { + name: string + size: number + path?: string + }[] + config?: { + preScript?: string + postScript?: string + backup: boolean + backupPath?: string + } + errorMessage?: string + createdAt: string +} + +export interface DeployConfig { + id: string + projectId: string + serverId: string + deployPath: string + preScript?: string + postScript?: string + backup: boolean + backupPath?: string + backupCount: number + autoRestart: boolean + healthCheck?: { + enabled: boolean + url: string + timeout: number + retries: number + } +} + +// ==================== 日志中心 ==================== + +export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'fatal' +export type LogType = 'operation' | 'deploy' | 'access' | 'error' | 'system' + +export interface LogRecord { + id: string + type: LogType + level: LogLevel + module: string + action: string + content: string + operator?: string + operatorIp?: string + projectId?: string + projectName?: string + serverId?: string + serverName?: string + requestId?: string + duration?: number + createdAt: string + metadata?: Record +} + +export interface LogFilter { + type?: LogType + level?: LogLevel + module?: string + operator?: string + projectId?: string + serverId?: string + keyword?: string + startTime?: string + endTime?: string +} + +// ==================== 域名管理 ==================== + +export type DomainStatus = 'active' | 'pending' | 'expired' | 'error' +export type DnsStatus = 'resolved' | 'unresolved' | 'checking' +export type SslStatus = 'valid' | 'expiring' | 'expired' | 'none' + +export interface DomainInfo { + id: string + domain: string + projectId?: string + projectName?: string + serverId?: string + serverName?: string + serverIp?: string + status: DomainStatus + dnsStatus: DnsStatus + dnsRecords?: { + type: string // A, CNAME, etc. + value: string + }[] + sslStatus: SslStatus + sslExpireDate?: string + certificateId?: string + certificateName?: string + nginxConfigPath?: string + proxyPass?: string + port?: number + enableHttps: boolean + forceHttps: boolean + description?: string + createdAt: string + updatedAt: string +} + +export interface NginxConfig { + serverName: string + listen: number + listenSsl?: number + root?: string + proxyPass?: string + sslCertificate?: string + sslCertificateKey?: string + locations?: { + path: string + proxyPass?: string + root?: string + index?: string + tryFiles?: string + }[] +} + +// ==================== 环境配置 ==================== + +export type EnvironmentType = 'development' | 'testing' | 'staging' | 'production' + +export interface EnvironmentInfo { + id: string + name: string + type: EnvironmentType + projectId?: string + projectName?: string + serverId?: string + serverName?: string + description?: string + variables: EnvironmentVariable[] + createdAt: string + updatedAt: string +} + +export interface EnvironmentVariable { + id: string + key: string + value: string + isSecret: boolean + description?: string + createdAt: string + updatedAt: string +} + +export interface EnvironmentCompare { + key: string + environments: { + envId: string + envName: string + value?: string + exists: boolean + }[] + isDifferent: boolean +} diff --git a/src/views/platform/domains/index.vue b/src/views/platform/domains/index.vue new file mode 100644 index 0000000..06a8cdd --- /dev/null +++ b/src/views/platform/domains/index.vue @@ -0,0 +1,857 @@ + + + + + diff --git a/src/views/platform/environments/index.vue b/src/views/platform/environments/index.vue new file mode 100644 index 0000000..7d45c58 --- /dev/null +++ b/src/views/platform/environments/index.vue @@ -0,0 +1,902 @@ + + + + + diff --git a/src/views/platform/log-center/index.vue b/src/views/platform/log-center/index.vue new file mode 100644 index 0000000..784dddc --- /dev/null +++ b/src/views/platform/log-center/index.vue @@ -0,0 +1,545 @@ + + + + + diff --git a/src/views/platform/projects/index.vue b/src/views/platform/projects/index.vue index d5f1ee8..fa3bc7b 100644 --- a/src/views/platform/projects/index.vue +++ b/src/views/platform/projects/index.vue @@ -136,112 +136,77 @@ - - + + - - 域名配置 - - - - - - - - - - - - - - - - - - -
对应主目录:/opt/1panel/www/sites/{{ formData.alias || 'xxx' }}/index
-
- - - 安全配置 - - - - 开启失败不会影响网站创建 - - - - - - + 访问配置 - - -
用于iframe嵌套的子项目地址
+ + + + {{ domain.domain }} + HTTPS + + +
选择已配置的域名,项目地址将自动生成
- + + +
根据绑定域名自动生成,用于 iframe 嵌套
+
+ + + 部署配置 + + + + + {{ server.name }} ({{ server.ip }}) + + +
选择前端文件部署的目标服务器
+
+ + + +
+ 对应 1Panel 目录:/opt/1panel/www/sites/{{ formData.domain || '域名' }}/index +
+
+ + 其他 @@ -252,32 +217,27 @@
- + - - - 保存版本 - - - 上传版本文件 - - + + 创建新版本 + @@ -303,15 +263,41 @@
{{ version.description }}
创建人:{{ version.createdBy }} + + | 文件:{{ version.files.length }} 个 +
+ + +
+ + +
+
+ + {{ file.name }} + {{ formatFileSize(file.size) }} +
+
+
+
+
+
+ + 上传文件 + - 回退到此版本 + 回退 - +
@@ -342,7 +328,7 @@ @@ -881,5 +844,43 @@ function handleVersionUpload() { .version-actions { display: flex; gap: 8px; + margin-top: 8px; +} + +.version-files-count { + margin-left: 8px; +} + +.version-files { + margin-top: 8px; + margin-bottom: 8px; +} + +.file-list { + display: flex; + flex-direction: column; + gap: 8px; +} + +.file-item { + display: flex; + align-items: center; + gap: 8px; + padding: 6px 10px; + background: #f5f5f5; + border-radius: 4px; + font-size: 13px; +} + +.file-name { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.file-size { + color: #8c8c8c; + font-size: 12px; } diff --git a/src/views/platform/servers/index.vue b/src/views/platform/servers/index.vue new file mode 100644 index 0000000..13b593b --- /dev/null +++ b/src/views/platform/servers/index.vue @@ -0,0 +1,770 @@ + + + + +