Skip to content

Commit b69ad08

Browse files
committed
fix: reverse proxy for sse
1 parent 50b5afc commit b69ad08

File tree

22 files changed

+1048
-806
lines changed

22 files changed

+1048
-806
lines changed

api/system/router.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ func InitSelfCheckRouter(r *gin.RouterGroup) {
3131
g := r.Group("self_check", authIfInstalled)
3232
g.GET("", middleware.Proxy(), SelfCheck)
3333
g.POST("/:name/fix", middleware.Proxy(), SelfCheckFix)
34+
g.GET("sse", middleware.Proxy(), CheckSSE)
3435
g.GET("websocket", middleware.ProxyWs(), CheckWebSocket)
3536
}
3637

api/system/self_check.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package system
22

33
import (
4+
"io"
5+
"net/http"
6+
47
"github.com/gorilla/websocket"
58
"github.com/uozi-tech/cosy/logger"
6-
"net/http"
9+
10+
"time"
711

812
"github.com/0xJacky/Nginx-UI/internal/self_check"
913
"github.com/gin-gonic/gin"
@@ -39,3 +43,19 @@ func CheckWebSocket(c *gin.Context) {
3943
return
4044
}
4145
}
46+
47+
func CheckSSE(c *gin.Context) {
48+
notify := c.Writer.CloseNotify()
49+
for i := 0; i < 10; i++ {
50+
select {
51+
case <-notify:
52+
return
53+
default:
54+
c.Stream(func(w io.Writer) bool {
55+
c.SSEvent("message", time.Now())
56+
return false
57+
})
58+
time.Sleep(time.Second * 2)
59+
}
60+
}
61+
}

app/src/components/SelfCheck/tasks/frontend/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import type { FrontendTask } from '../types'
22
import HttpsCheckTask from './https-check'
3+
import SSETask from './sse'
34
import WebsocketTask from './websocket'
4-
55
// Collection of all frontend tasks
66
const frontendTasks: Record<string, FrontendTask> = {
77
'Frontend-Websocket': WebsocketTask,
8+
'Frontend-SSE': SSETask,
89
'Frontend-HttpsCheck': HttpsCheckTask,
910
// Add more frontend tasks here
1011
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import type { ReportStatusType } from '@/api/self_check'
2+
import type { FrontendTask } from '../types'
3+
import { ReportStatus } from '@/api/self_check'
4+
import { useSSE } from '@/composables/useSSE'
5+
6+
/**
7+
* SSE Task
8+
*
9+
* Checks if the application is able to connect to the backend through the Server-Sent Events protocol
10+
*/
11+
const SSETask: FrontendTask = {
12+
key: 'sse',
13+
name: () => 'SSE',
14+
description: () => $gettext('Support communication with the backend through the Server-Sent Events protocol. '
15+
+ 'If your Nginx UI is being used via an Nginx reverse proxy, '
16+
+ 'please refer to this link to write the corresponding configuration file: '
17+
+ 'https://nginxui.com/guide/nginx-proxy-example.html'),
18+
check: async (): Promise<ReportStatusType> => {
19+
try {
20+
const connected = await new Promise<boolean>(resolve => {
21+
const { connect, disconnect } = useSSE()
22+
23+
// Use the connect method from useSSE
24+
connect({
25+
url: '/api/self_check/sse',
26+
onMessage: () => {
27+
resolve(true)
28+
},
29+
onError: () => {
30+
resolve(false)
31+
disconnect()
32+
},
33+
reconnectInterval: 0,
34+
})
35+
36+
// Set a timeout for the connection attempt
37+
setTimeout(() => {
38+
resolve(false)
39+
disconnect()
40+
}, 5000)
41+
})
42+
43+
if (connected) {
44+
return ReportStatus.Success
45+
}
46+
else {
47+
return ReportStatus.Error
48+
}
49+
}
50+
catch (error) {
51+
console.error(error)
52+
return ReportStatus.Error
53+
}
54+
},
55+
}
56+
57+
export default SSETask

app/src/composables/useSSE.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import type { SSEvent } from 'sse.js'
22
import { urlJoin } from '@/lib/helper'
3-
import { useUserStore } from '@/pinia'
3+
import { useSettingsStore, useUserStore } from '@/pinia'
4+
import { storeToRefs } from 'pinia'
45
import { SSE } from 'sse.js'
56

67
const userStore = useUserStore()
78
const { token } = storeToRefs(userStore)
9+
const settings = useSettingsStore()
810

911
export interface SSEOptions {
1012
url: string
@@ -40,10 +42,16 @@ export function useSSE() {
4042

4143
const fullUrl = urlJoin(window.location.pathname, url)
4244

45+
const headers = {
46+
Authorization: token.value,
47+
}
48+
49+
if (settings.environment.id) {
50+
headers['X-Node-ID'] = settings.environment.id.toString()
51+
}
52+
4353
const sse = new SSE(fullUrl, {
44-
headers: {
45-
Authorization: token.value,
46-
},
54+
headers,
4755
})
4856

4957
// Handle messages
@@ -86,9 +94,11 @@ export function useSSE() {
8694
}
8795

8896
// Automatically disconnect when the component is unmounted
89-
onUnmounted(() => {
90-
disconnect()
91-
})
97+
if (getCurrentInstance()) {
98+
onUnmounted(() => {
99+
disconnect()
100+
})
101+
}
92102

93103
return {
94104
connect,

0 commit comments

Comments
 (0)