mirror of
https://github.com/netfun2000/rttys_zhaojh329.git
synced 2026-02-27 09:53:24 +08:00
Write the HTTP proxy error page to a file
Signed-off-by: Jianhui Zhao <zhaojh329@gmail.com>
This commit is contained in:
@@ -26,6 +26,7 @@ jobs:
|
||||
cd ui
|
||||
npm install
|
||||
npm run build
|
||||
mv dist ../assets
|
||||
cd ..
|
||||
- name: build
|
||||
env:
|
||||
|
||||
@@ -82,6 +82,7 @@ jobs:
|
||||
cd ui
|
||||
npm install
|
||||
npm run build
|
||||
mv dist ../assets
|
||||
cd ..
|
||||
|
||||
- name: Build rttys release
|
||||
|
||||
@@ -12,3 +12,5 @@
|
||||
|
||||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||
.glide/
|
||||
|
||||
assets/dist
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ RUN npm install && npm run build
|
||||
FROM golang:latest AS rttys
|
||||
WORKDIR /rttys-build
|
||||
COPY . .
|
||||
COPY --from=ui /rttys-ui/dist ui/dist
|
||||
COPY --from=ui /rttys-ui/dist assets/dist
|
||||
RUN CGO_ENABLED=0 \
|
||||
GitCommit=$(git log --pretty=format:"%h" -1) \
|
||||
BuildTime=$(date +%FT%T%z) \
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -24,9 +23,6 @@ import (
|
||||
|
||||
const httpSessionExpire = 30 * time.Minute
|
||||
|
||||
//go:embed ui/dist
|
||||
var staticFs embed.FS
|
||||
|
||||
func (srv *RttyServer) ListenAPI() error {
|
||||
cfg := &srv.cfg
|
||||
|
||||
@@ -34,7 +30,7 @@ func (srv *RttyServer) ListenAPI() error {
|
||||
|
||||
r := gin.New()
|
||||
|
||||
fs, err := fs.Sub(staticFs, "ui/dist")
|
||||
fs, err := fs.Sub(staticFs, "assets/dist")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>RTTY</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
background-color: #555;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.error-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 60vh;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
margin-bottom: 2rem;
|
||||
animation: fadeIn 0.8s ease-in-out;
|
||||
}
|
||||
|
||||
.error-icon svg {
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
fill: #f56565;
|
||||
}
|
||||
|
||||
.error-content {
|
||||
max-width: 700px;
|
||||
animation: slideUp 0.8s ease-out 0.2s both;
|
||||
}
|
||||
|
||||
.error-title {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 600;
|
||||
color: #7a8fb0;
|
||||
margin-bottom: 1rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
font-size: 1rem;
|
||||
color: #b6c1d3;
|
||||
margin-bottom: 2rem;
|
||||
line-height: 1.6;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="error-container">
|
||||
<div class="error-icon">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="error-content">
|
||||
<h2 class="error-title" id="errorTitle"></h2>
|
||||
<p class="error-message" id="errorMessage"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const translations = {
|
||||
en: {
|
||||
'Device Unavailable': 'Device Unavailable',
|
||||
'Invalid Request': 'Invalid Request',
|
||||
'Unauthorized Access': 'Unauthorized Access',
|
||||
'Device offline message': 'The device is currently offline. Please check the device status and try again.',
|
||||
'Invalid request message': 'The request is invalid or malformed',
|
||||
'Unauthorized request message': 'You are not authorized to access this resource. Please check your session and try again.'
|
||||
},
|
||||
'zh-CN': {
|
||||
'Device Unavailable': '设备不可用',
|
||||
'Invalid Request': '无效请求',
|
||||
'Unauthorized Access': '未授权访问',
|
||||
'Device offline message': '设备当前离线,请检查设备状态后重试。',
|
||||
'Invalid request message': '请求无效或格式错误',
|
||||
'Unauthorized request message': '您无权访问此资源。请检查您的会话并重试。'
|
||||
}
|
||||
};
|
||||
|
||||
function t(key, lang) {
|
||||
return translations[lang][key] || translations.en[key] || key;
|
||||
}
|
||||
|
||||
function updateContent() {
|
||||
const errorType = '{{.}}';
|
||||
const lang = navigator.language === 'zh-CN' ? 'zh-CN' : 'en';
|
||||
|
||||
let title = '', message = '';
|
||||
|
||||
switch (errorType) {
|
||||
case 'offline':
|
||||
title = t('Device Unavailable', lang);
|
||||
message = t('Device offline message', lang);
|
||||
break;
|
||||
case 'invalid':
|
||||
title = t('Invalid Request', lang);
|
||||
message = t('Invalid request message', lang);
|
||||
break;
|
||||
case 'unauthorized':
|
||||
title = t('Unauthorized Access', lang);
|
||||
message = t('Unauthorized request message', lang);
|
||||
break;
|
||||
}
|
||||
|
||||
document.getElementById('errorTitle').textContent = title;
|
||||
document.getElementById('errorMessage').textContent = message;
|
||||
|
||||
// Update page title
|
||||
if (title) {
|
||||
document.title = title + ' - RTTY';
|
||||
} else {
|
||||
document.title = 'Error - RTTY';
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize page on load
|
||||
document.addEventListener('DOMContentLoaded', updateContent);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -21,6 +21,11 @@ ARCH="$1" # Pass architecture as an argument, e.g., amd64 or arm64
|
||||
exit 1;
|
||||
}
|
||||
|
||||
[ -d assets/dist ] || {
|
||||
echo "Please build ui first"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Build directory
|
||||
BUILD_DIR="build-deb"
|
||||
INSTALL_DIR="$BUILD_DIR/usr"
|
||||
|
||||
@@ -10,6 +10,11 @@ BuildTime=$(date +%FT%T%z)
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ -d assets/dist ] || {
|
||||
echo "Please build ui first"
|
||||
exit 1
|
||||
}
|
||||
|
||||
generate() {
|
||||
local os="$1"
|
||||
local arch="$2"
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Author: Jianhui Zhao <zhaojh329@gmail.com>
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import "embed"
|
||||
|
||||
//go:embed assets
|
||||
var staticFs embed.FS
|
||||
@@ -7,10 +7,13 @@ package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -373,170 +376,30 @@ func (rw *HttpProxyWriter) WriteRequest(req *http.Request) {
|
||||
req.Write(rw)
|
||||
}
|
||||
|
||||
func generateErrorHTML(errorType string) string {
|
||||
return fmt.Sprintf(
|
||||
`<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>RTTY</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
background-color: #555;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.error-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 60vh;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
margin-bottom: 2rem;
|
||||
animation: fadeIn 0.8s ease-in-out;
|
||||
}
|
||||
|
||||
.error-icon svg {
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
fill: #f56565;
|
||||
}
|
||||
|
||||
.error-content {
|
||||
max-width: 700px;
|
||||
animation: slideUp 0.8s ease-out 0.2s both;
|
||||
}
|
||||
|
||||
.error-title {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 600;
|
||||
color: #7a8fb0;
|
||||
margin-bottom: 1rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
font-size: 1rem;
|
||||
color: #b6c1d3;
|
||||
margin-bottom: 2rem;
|
||||
line-height: 1.6;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="error-container">
|
||||
<div class="error-icon">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="error-content">
|
||||
<h2 class="error-title" id="errorTitle"></h2>
|
||||
<p class="error-message" id="errorMessage"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const translations = {
|
||||
en: {
|
||||
'Device Unavailable': 'Device Unavailable',
|
||||
'Invalid Request': 'Invalid Request',
|
||||
'Unauthorized Access': 'Unauthorized Access',
|
||||
'Device offline message': 'The device is currently offline. Please check the device status and try again.',
|
||||
'Invalid request message': 'The request is invalid or malformed',
|
||||
'Unauthorized request message': 'You are not authorized to access this resource. Please check your session and try again.'
|
||||
},
|
||||
'zh-CN': {
|
||||
'Device Unavailable': '设备不可用',
|
||||
'Invalid Request': '无效请求',
|
||||
'Unauthorized Access': '未授权访问',
|
||||
'Device offline message': '设备当前离线,请检查设备状态后重试。',
|
||||
'Invalid request message': '请求无效或格式错误',
|
||||
'Unauthorized request message': '您无权访问此资源。请检查您的会话并重试。'
|
||||
}
|
||||
};
|
||||
|
||||
function t(key, lang) {
|
||||
return translations[lang][key] || translations.en[key] || key;
|
||||
}
|
||||
|
||||
function updateContent() {
|
||||
const errorType = '%s';
|
||||
const lang = navigator.language === 'zh-CN' ? 'zh-CN' : 'en';
|
||||
|
||||
let title = '', message = '';
|
||||
|
||||
switch (errorType) {
|
||||
case 'offline':
|
||||
title = t('Device Unavailable', lang);
|
||||
message = t('Device offline message', lang);
|
||||
break;
|
||||
case 'invalid':
|
||||
title = t('Invalid Request', lang);
|
||||
message = t('Invalid request message', lang);
|
||||
break;
|
||||
case 'unauthorized':
|
||||
title = t('Unauthorized Access', lang);
|
||||
message = t('Unauthorized request message', lang);
|
||||
break;
|
||||
}
|
||||
|
||||
document.getElementById('errorTitle').textContent = title;
|
||||
document.getElementById('errorMessage').textContent = message;
|
||||
|
||||
// Update page title
|
||||
if (title) {
|
||||
document.title = title + ' - RTTY';
|
||||
} else {
|
||||
document.title = 'Error - RTTY';
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize page on load
|
||||
document.addEventListener('DOMContentLoaded', updateContent);
|
||||
</script>
|
||||
</body>
|
||||
</html>`, errorType)
|
||||
}
|
||||
|
||||
func sendHTTPErrorResponse(conn net.Conn, errorType string) {
|
||||
htmlContent := generateErrorHTML(errorType)
|
||||
fs, _ := fs.Sub(staticFs, "assets")
|
||||
|
||||
f, err := fs.Open("http-proxy-err.html")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
content, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
content = bytes.ReplaceAll(content, []byte("{{.}}"), []byte(errorType))
|
||||
|
||||
response := "HTTP/1.1 200 OK\r\n"
|
||||
response += "Content-Type: text/html; charset=utf-8\r\n"
|
||||
response += fmt.Sprintf("Content-Length: %d\r\n", len(htmlContent))
|
||||
response += fmt.Sprintf("Content-Length: %d\r\n", len(content))
|
||||
response += "Connection: close\r\n"
|
||||
response += "\r\n"
|
||||
response += htmlContent
|
||||
|
||||
conn.Write([]byte(response))
|
||||
conn.Write(content)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user