initial commit

This commit is contained in:
Alireza Ahmadi
2024-02-13 01:17:03 +01:00
commit f40b27fd8b
136 changed files with 16023 additions and 0 deletions
+154
View File
@@ -0,0 +1,154 @@
package sub
import (
"context"
"crypto/tls"
"io"
"net"
"net/http"
"s-ui/config"
"s-ui/logger"
"s-ui/middleware"
"s-ui/network"
"s-ui/service"
"strconv"
"github.com/gin-gonic/gin"
)
type Server struct {
httpServer *http.Server
listener net.Listener
ctx context.Context
cancel context.CancelFunc
service.SettingService
}
func NewServer() *Server {
ctx, cancel := context.WithCancel(context.Background())
return &Server{
ctx: ctx,
cancel: cancel,
}
}
func (s *Server) initRouter() (*gin.Engine, error) {
if config.IsDebug() {
gin.SetMode(gin.DebugMode)
} else {
gin.DefaultWriter = io.Discard
gin.DefaultErrorWriter = io.Discard
gin.SetMode(gin.ReleaseMode)
}
engine := gin.Default()
subPath, err := s.SettingService.GetSubPath()
if err != nil {
return nil, err
}
subDomain, err := s.SettingService.GetSubDomain()
if err != nil {
return nil, err
}
if subDomain != "" {
engine.Use(middleware.DomainValidator(subDomain))
}
g := engine.Group(subPath)
NewSubHandler(g)
return engine, nil
}
func (s *Server) Start() (err error) {
//This is an anonymous function, no function name
defer func() {
if err != nil {
s.Stop()
}
}()
engine, err := s.initRouter()
if err != nil {
return err
}
certFile, err := s.SettingService.GetSubCertFile()
if err != nil {
return err
}
keyFile, err := s.SettingService.GetSubKeyFile()
if err != nil {
return err
}
listen, err := s.SettingService.GetSubListen()
if err != nil {
return err
}
port, err := s.SettingService.GetSubPort()
if err != nil {
return err
}
listenAddr := net.JoinHostPort(listen, strconv.Itoa(port))
listener, err := net.Listen("tcp", listenAddr)
if err != nil {
return err
}
if certFile != "" || keyFile != "" {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
listener.Close()
return err
}
c := &tls.Config{
Certificates: []tls.Certificate{cert},
}
listener = network.NewAutoHttpsListener(listener)
listener = tls.NewListener(listener, c)
}
if certFile != "" || keyFile != "" {
logger.Info("Sub server run https on", listener.Addr())
} else {
logger.Info("Sub server run http on", listener.Addr())
}
s.listener = listener
s.httpServer = &http.Server{
Handler: engine,
}
go func() {
s.httpServer.Serve(listener)
}()
return nil
}
func (s *Server) Stop() error {
s.cancel()
var err error
if s.httpServer != nil {
err = s.httpServer.Shutdown(s.ctx)
if err != nil {
return err
}
}
if s.listener != nil {
err = s.listener.Close()
if err != nil {
return err
}
}
return nil
}
func (s *Server) GetCtx() context.Context {
return s.ctx
}
+39
View File
@@ -0,0 +1,39 @@
package sub
import (
"s-ui/logger"
"s-ui/service"
"github.com/gin-gonic/gin"
)
type SubHandler struct {
service.SettingService
SubService
}
func NewSubHandler(g *gin.RouterGroup) {
a := &SubHandler{}
a.initRouter(g)
}
func (s *SubHandler) initRouter(g *gin.RouterGroup) {
g.GET("/:subid", s.subs)
}
func (s *SubHandler) subs(c *gin.Context) {
subId := c.Param("subid")
result, headers, err := s.SubService.GetSubs(subId)
if err != nil || result == nil {
logger.Error(err)
c.String(400, "Error!")
} else {
// Add headers
c.Writer.Header().Set("Subscription-Userinfo", headers[0])
c.Writer.Header().Set("Profile-Update-Interval", headers[1])
c.Writer.Header().Set("Profile-Title", headers[2])
c.String(200, *result)
}
}
+181
View File
@@ -0,0 +1,181 @@
package sub
import (
"crypto/tls"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"s-ui/database"
"s-ui/database/model"
"s-ui/logger"
"s-ui/service"
"strings"
"time"
)
type SubService struct {
service.SettingService
}
type Link struct {
Type string `json:"type"`
Remark string `json:"remark"`
Uri string `json:"uri"`
}
func (s *SubService) GetSubs(subId string) (*string, []string, error) {
var err error
db := database.GetDB()
client := &model.Client{}
err = db.Model(model.Client{}).Where("enable = true and name = ?", subId).First(client).Error
if err != nil {
return nil, nil, err
}
links := []Link{}
err = json.Unmarshal([]byte(client.Links), &links)
if err != nil {
return nil, nil, err
}
clientInfo := ""
subShowInfo, _ := s.SettingService.GetSubShowInfo()
if subShowInfo {
clientInfo = s.getClientInfo(client)
}
var result string
for _, link := range links {
switch link.Type {
case "external":
result += fmt.Sprintln(link.Uri)
case "sub":
result += s.getExternalSub(link.Uri)
case "local":
result += fmt.Sprintln(s.addClientInfo(link.Uri, clientInfo))
}
}
var headers []string
updateInterval, _ := s.SettingService.GetSubUpdates()
headers = append(headers, fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", client.Up, client.Down, client.Volume, client.Expiry))
headers = append(headers, fmt.Sprintf("%d", updateInterval))
headers = append(headers, subId)
subEncode, _ := s.SettingService.GetSubEncode()
if subEncode {
result = base64.StdEncoding.EncodeToString([]byte(result))
}
return &result, headers, nil
}
func (s *SubService) getClientInfo(c *model.Client) string {
now := time.Now().Unix()
var result []string
if vol := c.Volume - (c.Up + c.Down); vol > 0 {
result = append(result, fmt.Sprintf("%s%s", s.formatTraffic(vol), "📊"))
}
if c.Expiry > 0 {
result = append(result, fmt.Sprintf("%d%s⏳", (c.Expiry-now)/86400, "Days"))
}
if len(result) > 0 {
return " " + strings.Join(result, " ")
} else {
return " ♾"
}
}
func (s *SubService) addClientInfo(uri string, clientInfo string) string {
protocol := strings.Split(uri, "://")
if len(protocol) < 2 {
return uri
}
switch protocol[0] {
case "vmess":
var vmessJson map[string]interface{}
config, err := base64.StdEncoding.DecodeString(protocol[1])
if err != nil {
logger.Warning("sub: Error decoding vmess content:", err)
return uri
}
err = json.Unmarshal(config, &vmessJson)
if err != nil {
logger.Warning("sub: Error decoding vmess content:", err)
return uri
}
vmessJson["ps"] = vmessJson["ps"].(string) + clientInfo
result, err := json.MarshalIndent(vmessJson, "", " ")
if err != nil {
logger.Warning("sub: Error decoding vmess + clientInfo content:", err)
return uri
}
return "vmess://" + base64.StdEncoding.EncodeToString(result)
default:
return uri + clientInfo
}
}
func (s *SubService) getExternalSub(url string) string {
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: tr}
// Make the HTTP request
response, err := client.Get(url)
if err != nil {
logger.Warning("sub: Error making HTTP request:", err)
return ""
}
defer response.Body.Close()
// Read the response body
body, err := io.ReadAll(response.Body)
if err != nil {
logger.Warning("sub: Error reading response body:", err)
return ""
}
// Check if the content is Base64 encoded
isBase64 := s.isBase64Encoded(string(body))
if isBase64 {
// Decode Base64 content
decodedText, err := base64.StdEncoding.DecodeString(string(body))
if err != nil {
logger.Warning("sub: Error decoding Base64 content:", err)
return ""
}
return string(decodedText)
} else {
return string(body)
}
}
// Function to check if a string is Base64 encoded
func (s *SubService) isBase64Encoded(str string) bool {
_, err := base64.StdEncoding.DecodeString(str)
return err == nil
}
func (s *SubService) formatTraffic(trafficBytes int64) string {
if trafficBytes < 1024 {
return fmt.Sprintf("%.2fB", float64(trafficBytes)/float64(1))
} else if trafficBytes < (1024 * 1024) {
return fmt.Sprintf("%.2fKB", float64(trafficBytes)/float64(1024))
} else if trafficBytes < (1024 * 1024 * 1024) {
return fmt.Sprintf("%.2fMB", float64(trafficBytes)/float64(1024*1024))
} else if trafficBytes < (1024 * 1024 * 1024 * 1024) {
return fmt.Sprintf("%.2fGB", float64(trafficBytes)/float64(1024*1024*1024))
} else if trafficBytes < (1024 * 1024 * 1024 * 1024 * 1024) {
return fmt.Sprintf("%.2fTB", float64(trafficBytes)/float64(1024*1024*1024*1024))
} else {
return fmt.Sprintf("%.2fEB", float64(trafficBytes)/float64(1024*1024*1024*1024*1024))
}
}