Merge pull request #73 from gujiangjiang/main
feat: 为新用户添加默认的通知渠道和消息模板、完善PWA应用
This commit is contained in:
14 files changed
+1786
-1855
No files matched your search
@@ -72,9 +72,21 @@ jobs:
|
||||
D1_DATABASE_ID: ${{ secrets.D1_DATABASE_ID }}
|
||||
D1_DATABASE_NAME: ${{ secrets.D1_DATABASE_NAME }}
|
||||
|
||||
# 新增步骤:检查 Cloudflare 密钥是否存在
|
||||
- name: Check for Cloudflare credentials
|
||||
id: cf_creds
|
||||
run: |
|
||||
if [ -z "${{ secrets.CF_API_TOKEN }}" ] || [ -z "${{ secrets.CF_ACCOUNT_ID }}" ]; then
|
||||
echo "Cloudflare credentials not found, skipping deployment."
|
||||
echo "has_creds=false" >> $GITHUB_ENV
|
||||
else
|
||||
echo "has_creds=true" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
# 第八步:部署到 Cloudflare
|
||||
# 使用 Cloudflare 官方的 'wrangler-action' 来执行部署
|
||||
# 仅在检测到密钥存在时执行
|
||||
- name: Deploy to Cloudflare
|
||||
if: env.has_creds == 'true'
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
# 传入 API 令牌用于身份验证
|
||||
|
||||
@@ -18,14 +18,26 @@ jobs:
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
-
|
||||
name: Check for Docker Hub credentials
|
||||
id: docker_creds
|
||||
run: |
|
||||
if [ -z "${{ secrets.DOCKERHUB_USERNAME }}" ] || [ -z "${{ secrets.DOCKERHUB_TOKEN }}" ]; then
|
||||
echo "Docker Hub credentials not found, skipping login and push."
|
||||
echo "has_creds=false" >> $GITHUB_ENV
|
||||
else
|
||||
echo "has_creds=true" >> $GITHUB_ENV
|
||||
fi
|
||||
-
|
||||
name: Login to Docker Hub
|
||||
if: env.has_creds == 'true'
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Build and push
|
||||
if: env.has_creds == 'true'
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
|
||||
@@ -182,41 +182,6 @@ export const updateNotificationTemplate = async (
|
||||
Omit<NotificationTemplate, "id" | "created_at" | "updated_at">
|
||||
>
|
||||
): Promise<boolean> => {
|
||||
const sets: string[] = [];
|
||||
const values: any[] = [];
|
||||
|
||||
if (template.name !== undefined) {
|
||||
sets.push("name = ?");
|
||||
values.push(template.name);
|
||||
}
|
||||
|
||||
if (template.type !== undefined) {
|
||||
sets.push("type = ?");
|
||||
values.push(template.type);
|
||||
}
|
||||
|
||||
if (template.subject !== undefined) {
|
||||
sets.push("subject = ?");
|
||||
values.push(template.subject);
|
||||
}
|
||||
|
||||
if (template.content !== undefined) {
|
||||
sets.push("content = ?");
|
||||
values.push(template.content);
|
||||
}
|
||||
|
||||
if (template.is_default !== undefined) {
|
||||
sets.push("is_default = ?");
|
||||
values.push(template.is_default ? 1 : 0);
|
||||
}
|
||||
|
||||
if (sets.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sets.push("updated_at = CURRENT_TIMESTAMP");
|
||||
values.push(id);
|
||||
|
||||
const result = await db
|
||||
.update(notificationTemplates)
|
||||
.set({
|
||||
@@ -228,7 +193,7 @@ export const updateNotificationTemplate = async (
|
||||
})
|
||||
.where(and(eq(notificationTemplates.id, id), eq(notificationTemplates.created_by, userId)));
|
||||
|
||||
return result.length > 0;
|
||||
return result.success; // fix: 返回 success 布尔值
|
||||
};
|
||||
|
||||
// 删除通知模板
|
||||
@@ -240,7 +205,7 @@ export const deleteNotificationTemplate = async (
|
||||
.delete(notificationTemplates)
|
||||
.where(and(eq(notificationTemplates.id, id), eq(notificationTemplates.created_by, userId)));
|
||||
|
||||
return result.length > 0;
|
||||
return result.success; // fix: 返回 success 布尔值
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import * as repositories from "../repositories";
|
||||
import { getJwtSecret } from "../utils/jwt";
|
||||
import { Bindings } from "../models/db";
|
||||
import * as SettingsService from "./SettingsService";
|
||||
import * as NotificationService from "./NotificationService";
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
@@ -143,6 +144,11 @@ export async function registerUser(
|
||||
"user"
|
||||
);
|
||||
|
||||
// 为新用户创建默认的通知设置
|
||||
if (newUser && newUser.id) {
|
||||
await NotificationService.createDefaultNotificationSettingsForUser(newUser.id);
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "注册成功",
|
||||
|
||||
@@ -998,3 +998,85 @@ export async function deleteNotificationSettings(
|
||||
message: `${type}通知设置删除成功`,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 为新用户创建默认的通知设置
|
||||
* @param userId 新用户的ID
|
||||
*/
|
||||
export async function createDefaultNotificationSettingsForUser(userId: number): Promise<void> {
|
||||
try {
|
||||
console.log(`为新用户 ${userId} 创建默认通知设置...`);
|
||||
const now = new Date().toISOString();
|
||||
|
||||
// 创建默认通知模板
|
||||
await repositories.createNotificationTemplate({
|
||||
name: "Monitor监控模板",
|
||||
type: "monitor",
|
||||
subject: "【${status}】${name} 监控状态变更",
|
||||
content:
|
||||
"🔔 网站监控状态变更通知\n\n📊 服务: ${name}\n🔄 状态: ${status} (之前: ${previous_status})\n🕒 时间: ${time}\n\n🔗 地址: ${url}\n⏱️ 响应时间: ${response_time}\n📝 实际状态码: ${status_code}\n🎯 期望状态码: ${expected_status}\n\n❗ 错误信息: ${error}",
|
||||
is_default: true, // 修复: 将 1 修改为 true
|
||||
created_by: userId,
|
||||
});
|
||||
|
||||
await repositories.createNotificationTemplate({
|
||||
name: "Agent监控模板",
|
||||
type: "agent",
|
||||
subject: "【${status}】${name} 客户端状态变更",
|
||||
content:
|
||||
"🔔 客户端状态变更通知\n\n📊 主机: ${name}\n🔄 状态: ${status} (之前: ${previous_status})\n🕒 时间: ${time}\n\n🖥️ 主机信息:\n 主机名: ${hostname}\n IP地址: ${ip_addresses}\n 操作系统: ${os}\n\n❗ 错误信息: ${error}",
|
||||
is_default: true, // 修复: 将 1 修改为 true
|
||||
created_by: userId,
|
||||
});
|
||||
|
||||
// 创建默认通知渠道
|
||||
const defaultChannelId = await repositories.createNotificationChannel({
|
||||
name: "默认Telegram通知渠道",
|
||||
type: "telegram",
|
||||
config:
|
||||
'{"botToken": "8163201319:AAGyY7FtdaRb6o8NCVXSbBUb6ofDK45cNJU", "chatId": "-1002608818360"}',
|
||||
enabled: true,
|
||||
created_by: userId,
|
||||
});
|
||||
|
||||
// 创建默认通知设置
|
||||
await repositories.createOrUpdateSettings({
|
||||
user_id: userId,
|
||||
target_type: "global-monitor",
|
||||
target_id: 0,
|
||||
enabled: false,
|
||||
on_down: true,
|
||||
on_recovery: true,
|
||||
on_offline: true, // 确保所有布尔字段都有值
|
||||
on_cpu_threshold: false,
|
||||
cpu_threshold: 90,
|
||||
on_memory_threshold: false,
|
||||
memory_threshold: 85,
|
||||
on_disk_threshold: false,
|
||||
disk_threshold: 90,
|
||||
channels: JSON.stringify([defaultChannelId]),
|
||||
});
|
||||
|
||||
await repositories.createOrUpdateSettings({
|
||||
user_id: userId,
|
||||
target_type: "global-agent",
|
||||
target_id: 0,
|
||||
enabled: false,
|
||||
on_down: true,
|
||||
on_recovery: true,
|
||||
on_offline: true,
|
||||
on_cpu_threshold: true,
|
||||
cpu_threshold: 80,
|
||||
on_memory_threshold: true,
|
||||
memory_threshold: 80,
|
||||
on_disk_threshold: true,
|
||||
disk_threshold: 90,
|
||||
channels: JSON.stringify([defaultChannelId]),
|
||||
});
|
||||
|
||||
console.log(`为新用户 ${userId} 创建默认通知设置成功`);
|
||||
} catch (error) {
|
||||
console.error(`为新用户 ${userId} 创建默认通知设置失败:`, error);
|
||||
// 此处不向上抛出异常,以免影响用户创建的主流程
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { compare, hash } from "bcryptjs";
|
||||
import * as repositories from "../repositories";
|
||||
import * as NotificationService from "./NotificationService"; // 导入通知服务
|
||||
|
||||
export async function getAllUsersService(userRole: string) {
|
||||
try {
|
||||
@@ -91,6 +92,11 @@ export async function createUserService(
|
||||
userData.role
|
||||
);
|
||||
|
||||
// 为新用户创建默认的通知设置
|
||||
if (newUser && newUser.id) {
|
||||
await NotificationService.createDefaultNotificationSettingsForUser(newUser.id);
|
||||
}
|
||||
|
||||
return { success: true, user: newUser, status: 201 };
|
||||
} catch (error) {
|
||||
console.error("创建用户错误:", error);
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||
<link rel="manifest" href="/manifest.webmanifest" />
|
||||
<!-- 移除了 manifest 链接,插件会自动注入 -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>XUGOU - 轻量化监控平台</title>
|
||||
</head>
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
"globals": "^13.24.0",
|
||||
"tw-animate-css": "^1.3.0",
|
||||
"typescript": "~5.1.6",
|
||||
"vite": "^6.0.0"
|
||||
"vite": "^6.0.0",
|
||||
"vite-plugin-pwa": "^0.20.1"
|
||||
}
|
||||
}
|
||||
+16
-7
@@ -1,11 +1,20 @@
|
||||
self.addEventListener("install", (event) => {
|
||||
// 这个文件可以保留为空,或者删除。
|
||||
// vite-plugin-pwa 会在构建时自动生成新的 sw.js 文件到输出目录(dist)。
|
||||
// 开发环境中,为了避免旧的 Service Worker 缓存导致问题,我们最好确保它被正确地注销。
|
||||
// 如果您在开发时遇到缓存问题,可以取消下面代码的注释来强制注销。
|
||||
|
||||
/*
|
||||
self.addEventListener('install', (e) => {
|
||||
self.skipWaiting();
|
||||
});
|
||||
|
||||
self.addEventListener("activate", (event) => {
|
||||
self.clients.claim();
|
||||
self.addEventListener('activate', (e) => {
|
||||
e.waitUntil(
|
||||
self.registration.unregister().then(() => {
|
||||
return self.clients.matchAll();
|
||||
}).then(clients => {
|
||||
clients.forEach(client => client.navigate(client.url));
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener("fetch", (event) => {
|
||||
event.respondWith(fetch(event.request));
|
||||
});
|
||||
*/
|
||||
@@ -516,6 +516,21 @@ const enUS = {
|
||||
"notifications.save.error": "Failed to save notification settings",
|
||||
"notifications.globalSettings.description": "Configure global notification settings, including notification channels, monitor settings, and message templates. These settings will apply to all monitors and agents unless overridden by specific configurations.",
|
||||
|
||||
// Notification Variables
|
||||
"notifications.variables.name": "Name",
|
||||
"notifications.variables.status": "Status",
|
||||
"notifications.variables.previous_status": "Previous Status",
|
||||
"notifications.variables.time": "Time",
|
||||
"notifications.variables.url": "URL",
|
||||
"notifications.variables.response_time": "Response Time",
|
||||
"notifications.variables.status_code": "Status Code",
|
||||
"notifications.variables.expected_status": "Expected Status",
|
||||
"notifications.variables.error": "Error",
|
||||
"notifications.variables.details": "Details",
|
||||
"notifications.variables.hostname": "Hostname",
|
||||
"notifications.variables.ip_addresses": "IP Addresses",
|
||||
"notifications.variables.os": "Operating System",
|
||||
|
||||
// Notification Event Types
|
||||
"notifications.events.onDownOnly": "Send notification only when service goes down",
|
||||
"notifications.events.onRecovery": "Send notification when service recovers",
|
||||
@@ -565,7 +580,15 @@ const enUS = {
|
||||
|
||||
// StatusSummaryCard Component
|
||||
"statusSummary.title": "Status Summary",
|
||||
|
||||
//补全
|
||||
"notifications.fetch.error": "Failed to fetch notification configuration.",
|
||||
"notifications.templates.createSuccess": "Template created successfully.",
|
||||
"notifications.templates.updateSuccess": "Template updated successfully.",
|
||||
"notifications.templates.deleteConfirmTitle": "Confirm Delete",
|
||||
"notifications.templates.deleteConfirmMessage": "Are you sure you want to delete this template?",
|
||||
"notifications.templates.deleteSuccess": "Template deleted successfully."
|
||||
},
|
||||
};
|
||||
|
||||
export default enUS;
|
||||
export default enUS;
|
||||
@@ -513,6 +513,21 @@ const zhCN = {
|
||||
"notifications.globalSettings.description":
|
||||
"配置全局通知设置,包括通知渠道、监控设置和消息模板。这些设置将应用于所有监控和客户端,除非被特定的配置覆盖。",
|
||||
|
||||
// 通知变量
|
||||
"notifications.variables.name": "名称",
|
||||
"notifications.variables.status": "状态",
|
||||
"notifications.variables.previous_status": "先前状态",
|
||||
"notifications.variables.time": "时间",
|
||||
"notifications.variables.url": "URL地址",
|
||||
"notifications.variables.response_time": "响应时间",
|
||||
"notifications.variables.status_code": "状态码",
|
||||
"notifications.variables.expected_status": "预期状态码",
|
||||
"notifications.variables.error": "错误信息",
|
||||
"notifications.variables.details": "详情",
|
||||
"notifications.variables.hostname": "主机名",
|
||||
"notifications.variables.ip_addresses": "IP地址",
|
||||
"notifications.variables.os": "操作系统",
|
||||
|
||||
// 通知事件类型
|
||||
"notifications.events.onDownOnly": "仅服务故障时发送通知",
|
||||
"notifications.events.onRecovery": "服务恢复时发送通知",
|
||||
@@ -562,7 +577,15 @@ const zhCN = {
|
||||
|
||||
// StatusSummaryCard 组件
|
||||
"statusSummary.title": "状态摘要",
|
||||
|
||||
//补全
|
||||
"notifications.fetch.error": "获取通知配置失败。",
|
||||
"notifications.templates.createSuccess": "模板创建成功。",
|
||||
"notifications.templates.updateSuccess": "模板更新成功。",
|
||||
"notifications.templates.deleteConfirmTitle": "确认删除",
|
||||
"notifications.templates.deleteConfirmMessage": "你确定要删除这个模板吗?",
|
||||
"notifications.templates.deleteSuccess": "模板删除成功。"
|
||||
},
|
||||
};
|
||||
|
||||
export default zhCN;
|
||||
export default zhCN;
|
||||
+2
-12
@@ -8,18 +8,8 @@ import "./styles/global.css";
|
||||
import "./i18n/config";
|
||||
import router from "./router";
|
||||
|
||||
if ("serviceWorker" in navigator) {
|
||||
window.addEventListener("load", () => {
|
||||
navigator.serviceWorker
|
||||
.register("/sw.js")
|
||||
.then((registration) => {
|
||||
console.log("Service Worker 注册成功:", registration);
|
||||
})
|
||||
.catch((registrationError) => {
|
||||
console.log("Service Worker 注册失败:", registrationError);
|
||||
});
|
||||
});
|
||||
}
|
||||
// 删除了手动的 Service Worker 注册代码
|
||||
// vite-plugin-pwa 会自动处理
|
||||
|
||||
createRoot(document.getElementById("root")!).render(
|
||||
<StrictMode>
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+45
-1
@@ -2,10 +2,54 @@ import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import path from "path";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import { VitePWA } from 'vite-plugin-pwa'; // 导入 PWA 插件
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react(), tailwindcss()],
|
||||
plugins: [
|
||||
react(),
|
||||
tailwindcss(),
|
||||
// 添加 PWA 插件配置
|
||||
VitePWA({
|
||||
registerType: 'autoUpdate', // 自动更新 Service Worker
|
||||
injectRegister: 'auto', // 自动注入注册 Service Worker 的代码
|
||||
workbox: {
|
||||
// 配置需要缓存的文件类型
|
||||
globPatterns: ['**/*.{js,css,html,ico,png,svg,webmanifest}']
|
||||
},
|
||||
// 包含在 public 目录中的其他需要缓存的资源
|
||||
includeAssets: ['favicon.svg', 'apple-touch-icon.png'],
|
||||
// PWA 应用清单配置
|
||||
manifest: {
|
||||
name: 'XUGOU',
|
||||
short_name: 'XUGOU',
|
||||
description: 'XUGOU 是一个基于 CloudFlare 的轻量化系统监控平台,提供系统监控和状态页面功能。',
|
||||
theme_color: '#4F46E5',
|
||||
background_color: '#ffffff',
|
||||
display: 'standalone',
|
||||
scope: '/',
|
||||
start_url: '/',
|
||||
icons: [
|
||||
{
|
||||
src: 'android-chrome-192x192.png',
|
||||
sizes: '192x192',
|
||||
type: 'image/png'
|
||||
},
|
||||
{
|
||||
src: 'android-chrome-512x512.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png'
|
||||
},
|
||||
{
|
||||
src: 'android-chrome-512x512.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
purpose: 'maskable' // 添加 'maskable' 图标, 优化 Android 上的显示效果
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "./src"), // __dirname 指向 vite.config.ts 文件所在的目录
|
||||
|
||||
Reference in new issue
Block a user