From 09c3cfa0dc4fb6ace8be0ed1c9e10efaf442d83d Mon Sep 17 00:00:00 2001 From: moonrailgun Date: Sat, 13 Jan 2024 18:16:09 +0800 Subject: [PATCH] feat: add apprise notification #10 --- Dockerfile | 3 ++ package.json | 1 + pnpm-lock.yaml | 11 ++---- .../NotificationInfo/strategies/apprise.tsx | 26 ++++++++++++++ .../NotificationInfo/strategies/index.ts | 6 ++++ src/server/main.ts | 3 +- .../monitor/provider/__tests__/openai.spec.ts | 4 +-- .../provider/__tests__/apprise.spec.ts | 17 ++++++++++ .../model/notification/provider/apprise.ts | 34 +++++++++++++++++++ .../model/notification/provider/index.ts | 2 ++ src/server/utils/env.ts | 1 + src/server/utils/settings.ts | 3 -- 12 files changed, 96 insertions(+), 15 deletions(-) create mode 100644 src/client/components/modals/NotificationInfo/strategies/apprise.tsx create mode 100644 src/server/model/notification/provider/__tests__/apprise.spec.ts create mode 100644 src/server/model/notification/provider/apprise.ts delete mode 100644 src/server/utils/settings.ts diff --git a/Dockerfile b/Dockerfile index 9668844..2df236d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,6 +8,9 @@ COPY . . RUN apk add --update --no-cache python3 g++ make py3-pip +# Push client(only support pure text message) +RUN pip install apprise + RUN pnpm install --frozen-lockfile RUN pnpm build diff --git a/package.json b/package.json index f0fce15..8e16308 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "detect-browser": "^5.3.0", "dotenv": "^16.3.1", "eventemitter-strict": "^1.0.1", + "execa": "^5.1.1", "express": "^4.18.2", "express-async-errors": "^3.1.1", "express-validator": "^7.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7414397..eaeb7f4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -82,6 +82,9 @@ dependencies: eventemitter-strict: specifier: ^1.0.1 version: 1.0.1 + execa: + specifier: ^5.1.1 + version: 5.1.1 express: specifier: ^4.18.2 version: 4.18.2 @@ -5183,7 +5186,6 @@ packages: onetime: 5.1.2 signal-exit: 3.0.7 strip-final-newline: 2.0.0 - dev: true /execa@7.2.0: resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} @@ -5983,7 +5985,6 @@ packages: /human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} - dev: true /human-signals@4.3.1: resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} @@ -6911,7 +6912,6 @@ packages: /merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - dev: true /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} @@ -6961,7 +6961,6 @@ packages: /mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} - dev: true /mimic-fn@4.0.0: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} @@ -7346,7 +7345,6 @@ packages: engines: {node: '>=8'} dependencies: path-key: 3.1.1 - dev: true /npm-run-path@5.2.0: resolution: {integrity: sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==} @@ -7423,7 +7421,6 @@ packages: engines: {node: '>=6'} dependencies: mimic-fn: 2.1.0 - dev: true /onetime@6.0.0: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} @@ -9484,7 +9481,6 @@ packages: /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - dev: true /signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} @@ -9839,7 +9835,6 @@ packages: /strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} - dev: true /strip-final-newline@3.0.0: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} diff --git a/src/client/components/modals/NotificationInfo/strategies/apprise.tsx b/src/client/components/modals/NotificationInfo/strategies/apprise.tsx new file mode 100644 index 0000000..269a8a6 --- /dev/null +++ b/src/client/components/modals/NotificationInfo/strategies/apprise.tsx @@ -0,0 +1,26 @@ +import { Form, Input } from 'antd'; +import React from 'react'; + +export const NotificationApprise: React.FC = React.memo(() => { + return ( + <> + + + +
+ Read more:{' '} + + https://github.com/caronc/apprise/wiki#notification-services + +
+ + ); +}); +NotificationApprise.displayName = 'NotificationApprise'; diff --git a/src/client/components/modals/NotificationInfo/strategies/index.ts b/src/client/components/modals/NotificationInfo/strategies/index.ts index 916de13..91300de 100644 --- a/src/client/components/modals/NotificationInfo/strategies/index.ts +++ b/src/client/components/modals/NotificationInfo/strategies/index.ts @@ -1,6 +1,7 @@ import React from 'react'; import { NotificationSMTP } from './smtp'; import { NotificationTelegram } from './telegram'; +import { NotificationApprise } from './apprise'; interface NotificationStrategy { label: string; @@ -14,6 +15,11 @@ export const notificationStrategies: NotificationStrategy[] = [ name: 'smtp', form: NotificationSMTP, }, + { + label: 'Apprise(Support 90+ services)', + name: 'apprise', + form: NotificationApprise, + }, { label: 'Telegram', name: 'telegram', diff --git a/src/server/main.ts b/src/server/main.ts index 31775b4..494605e 100644 --- a/src/server/main.ts +++ b/src/server/main.ts @@ -19,7 +19,6 @@ import { initUdpServer } from './udp/server'; import { createServer } from 'http'; import { initSocketio } from './ws'; import { monitorManager } from './model/monitor'; -import { settings } from './utils/settings'; import { env } from './utils/env'; import cors from 'cors'; import { serverStatusRouter } from './router/serverStatus'; @@ -28,7 +27,7 @@ import { logger } from './utils/logger'; import { monitorRouter } from './router/monitor'; import { healthRouter } from './router/health'; -const port = settings.port; +const port = env.port; const app = express(); const httpServer = createServer(app); diff --git a/src/server/model/monitor/provider/__tests__/openai.spec.ts b/src/server/model/monitor/provider/__tests__/openai.spec.ts index e72e92f..b9f7740 100644 --- a/src/server/model/monitor/provider/__tests__/openai.spec.ts +++ b/src/server/model/monitor/provider/__tests__/openai.spec.ts @@ -1,10 +1,10 @@ import { describe, expect, test } from 'vitest'; import { getBillingCreditGrants } from '../openai'; -describe.runIf(!!process.env.OPENAI_SESS_KEY)('openai', () => { +describe.runIf(!!process.env.TEST_OPENAI_SESS_KEY)('openai', () => { test('getBillingCreditGrants should be ok', async () => { const res = await getBillingCreditGrants( - String(process.env.OPENAI_SESS_KEY) + String(process.env.TEST_OPENAI_SESS_KEY) ); expect(typeof res.allUSD).toBe('number'); diff --git a/src/server/model/notification/provider/__tests__/apprise.spec.ts b/src/server/model/notification/provider/__tests__/apprise.spec.ts new file mode 100644 index 0000000..b3faea7 --- /dev/null +++ b/src/server/model/notification/provider/__tests__/apprise.spec.ts @@ -0,0 +1,17 @@ +import { describe, test } from 'vitest'; +import { apprise } from '../apprise'; +import { token } from '../../token'; + +describe.runIf(!!process.env.TEST_APPRISE_URL)('apprise', () => { + test('apprise should be work', async () => { + await apprise.send( + { + payload: { + appriseUrl: process.env.TEST_APPRISE_URL, + }, + } as any, + 'test title', + [token.text('Foooo'), token.newline(), token.text('Baaaar')] + ); + }); +}); diff --git a/src/server/model/notification/provider/apprise.ts b/src/server/model/notification/provider/apprise.ts new file mode 100644 index 0000000..187eebd --- /dev/null +++ b/src/server/model/notification/provider/apprise.ts @@ -0,0 +1,34 @@ +import { NotificationProvider } from './type'; +import { baseContentTokenizer } from '../token'; +import execa from 'execa'; + +interface ApprisePayload { + appriseUrl: string; +} + +// Fork from https://github.com/louislam/uptime-kuma/blob/HEAD/server/notification-providers/smtp.js +export const apprise: NotificationProvider = { + send: async (notification, title, message) => { + const payload = notification.payload as unknown as ApprisePayload; + + const content = baseContentTokenizer.parse(message); + + const args = ['-vv', '-b', content, payload.appriseUrl]; + if (title) { + args.push('-t'); + args.push(title); + } + + const { stdout } = await execa('apprise', args); + + const output = stdout + ? stdout.toString() + : 'ERROR: maybe apprise not found'; + + if (output) { + if (output.includes('ERROR')) { + throw new Error(output); + } + } + }, +}; diff --git a/src/server/model/notification/provider/index.ts b/src/server/model/notification/provider/index.ts index 3e46aeb..08f4793 100644 --- a/src/server/model/notification/provider/index.ts +++ b/src/server/model/notification/provider/index.ts @@ -1,8 +1,10 @@ +import { apprise } from './apprise'; import { smtp } from './smtp'; import { telegram } from './telegram'; import type { NotificationProvider } from './type'; export const notificationProviders: Record = { smtp, + apprise, telegram, }; diff --git a/src/server/utils/env.ts b/src/server/utils/env.ts index 4e0007e..e76c33e 100644 --- a/src/server/utils/env.ts +++ b/src/server/utils/env.ts @@ -1,4 +1,5 @@ export const env = { + port: Number(process.env.PORT || 12345), allowRegister: checkEnvTrusty(process.env.ALLOW_REGISTER), allowOpenapi: checkEnvTrusty(process.env.ALLOW_OPENAPI), websiteId: process.env.WEBSITE_ID, diff --git a/src/server/utils/settings.ts b/src/server/utils/settings.ts deleted file mode 100644 index 632d16c..0000000 --- a/src/server/utils/settings.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const settings = { - port: Number(process.env.PORT || 12345), -};