diff --git a/backend/api/api.go b/backend/api/api.go
index f0299a1..ec1032b 100644
--- a/backend/api/api.go
+++ b/backend/api/api.go
@@ -27,7 +27,8 @@ func NewAPIHandler(g *gin.RouterGroup) {
func (a *APIHandler) initRouter(g *gin.RouterGroup) {
g.Use(func(c *gin.Context) {
- if c.Request.URL.Path != "/api/login" && c.Request.URL.Path != "/api/logout" {
+ path := c.Request.URL.Path
+ if !strings.HasSuffix(path, "login") && !strings.HasSuffix(path, "logout") {
checkLogin(c)
}
})
@@ -62,7 +63,11 @@ func (a *APIHandler) postHandler(c *gin.Context) {
}
err = SetLoginUser(c, loginUser)
- logger.Info("user ", loginUser, " login success")
+ if err == nil {
+ logger.Info("user ", loginUser, " login success")
+ } else {
+ logger.Warning("login failed: ", err)
+ }
jsonMsg(c, "", nil)
case "save":
diff --git a/backend/service/setting.go b/backend/service/setting.go
index 108984a..4edc3f4 100644
--- a/backend/service/setting.go
+++ b/backend/service/setting.go
@@ -21,6 +21,7 @@ var defaultValueMap = map[string]string{
"webSecret": common.Random(32),
"webCertFile": "",
"webKeyFile": "",
+ "webPath": "/app/",
"sessionMaxAge": "0",
"timeLocation": "Asia/Tehran",
"subListen": "",
@@ -158,6 +159,20 @@ func (s *SettingService) GetKeyFile() (string, error) {
return s.getString("webKeyFile")
}
+func (s *SettingService) GetWebPath() (string, error) {
+ webPath, err := s.getString("webPath")
+ if err != nil {
+ return "", err
+ }
+ if !strings.HasPrefix(webPath, "/") {
+ webPath = "/" + webPath
+ }
+ if !strings.HasSuffix(webPath, "/") {
+ webPath += "/"
+ }
+ return webPath, nil
+}
+
func (s *SettingService) GetSecret() ([]byte, error) {
secret, err := s.getString("webSecret")
if secret == defaultValueMap["webSecret"] {
@@ -278,6 +293,17 @@ func (s *SettingService) Save(tx *gorm.DB, changes []model.Changes) error {
}
}
+ // Correct Pathes start and ends with `/`
+ if key == "webPath" ||
+ key == "subPath" {
+ if !strings.HasPrefix(obj, "/") {
+ obj = "/" + obj
+ }
+ if !strings.HasSuffix(obj, "/") {
+ obj += "/"
+ }
+ }
+
err = tx.Model(model.Setting{}).Where("key = ?", key).Update("value", obj).Error
if err != nil {
return err
diff --git a/backend/web/web.go b/backend/web/web.go
index 24a046d..dc6ba03 100644
--- a/backend/web/web.go
+++ b/backend/web/web.go
@@ -53,6 +53,11 @@ func (s *Server) initRouter() (*gin.Engine, error) {
engine := gin.Default()
+ base_url, err := s.settingService.GetWebPath()
+ if err != nil {
+ return nil, err
+ }
+
webDomain, err := s.settingService.GetWebDomain()
if err != nil {
return nil, err
@@ -68,10 +73,11 @@ func (s *Server) initRouter() (*gin.Engine, error) {
}
engine.Use(gzip.Gzip(gzip.DefaultCompression))
- assetsBasePath := "/assets/"
+ assetsBasePath := base_url + "assets/"
store := cookie.NewStore(secret)
engine.Use(sessions.Sessions("session", store))
+
engine.Use(func(c *gin.Context) {
uri := c.Request.RequestURI
if strings.HasPrefix(uri, assetsBasePath) {
@@ -87,26 +93,28 @@ func (s *Server) initRouter() (*gin.Engine, error) {
engine.StaticFS(assetsBasePath, http.FS(assetsFS))
- group_api := engine.Group("/api")
+ group_api := engine.Group(base_url + "api")
api.NewAPIHandler(group_api)
+ // Load the HTML template
+ engine.LoadHTMLFiles("backend/web/html/index.html")
+
// Serve index.html as the entry point
// Handle all other routes by serving index.html
engine.NoRoute(func(c *gin.Context) {
- if c.Request.URL.Path != "/login" && !api.IsLogin(c) {
- c.Redirect(http.StatusTemporaryRedirect, "/login")
+ if !strings.HasPrefix(c.Request.URL.Path, base_url) {
+ c.String(404, "")
return
}
- if c.Request.URL.Path == "/login" && api.IsLogin(c) {
- c.Redirect(http.StatusTemporaryRedirect, "/")
+ if c.Request.URL.Path != base_url+"login" && !api.IsLogin(c) {
+ c.Redirect(http.StatusTemporaryRedirect, base_url+"login")
return
}
- data, err := content.ReadFile("html/index.html")
- if err != nil {
- c.String(http.StatusInternalServerError, "Internal Server Error")
+ if c.Request.URL.Path == base_url+"login" && api.IsLogin(c) {
+ c.Redirect(http.StatusTemporaryRedirect, base_url)
return
}
- c.Data(http.StatusOK, "text/html", data)
+ c.HTML(http.StatusOK, "index.html", gin.H{"BASE_URL": base_url})
})
return engine, nil
diff --git a/frontend/index.html b/frontend/index.html
index 971aff7..308cd5b 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -5,6 +5,9 @@
+
S-UI
diff --git a/frontend/src/components/Main.vue b/frontend/src/components/Main.vue
index ab2c733..67be341 100644
--- a/frontend/src/components/Main.vue
+++ b/frontend/src/components/Main.vue
@@ -184,7 +184,7 @@ const reloadItems = computed({
const reloadData = async () => {
const request = [...new Set(reloadItems.value.map(r => r.split('-')[1]))]
- const data = await HttpUtils.get('/api/status',{ r: request.join(',')})
+ const data = await HttpUtils.get('api/status',{ r: request.join(',')})
if (data.success) {
tilesData.value = data.obj
}
diff --git a/frontend/src/layouts/default/Drawer.vue b/frontend/src/layouts/default/Drawer.vue
index 35e9b2f..6c0c07d 100644
--- a/frontend/src/layouts/default/Drawer.vue
+++ b/frontend/src/layouts/default/Drawer.vue
@@ -59,7 +59,7 @@ const menu = [
]
const logout = async () => {
- const response = await HttpUtil.get('/api/logout')
+ const response = await HttpUtil.get('api/logout')
if(response.success){
router.push('/login')
}
diff --git a/frontend/src/layouts/modals/Stats.vue b/frontend/src/layouts/modals/Stats.vue
index be2787e..de171cd 100644
--- a/frontend/src/layouts/modals/Stats.vue
+++ b/frontend/src/layouts/modals/Stats.vue
@@ -101,7 +101,7 @@ export default {
methods: {
async loadData(limit: number) {
this.loading = true
- const data = await HttpUtils.get('/api/stats', { resource: this.resource, tag: this.tag, limit: limit })
+ const data = await HttpUtils.get('api/stats', { resource: this.resource, tag: this.tag, limit: limit })
if (data.success && data.obj) {
const obj = data.obj
const l = String(i18n.global.locale) == 'fa' ? "fa-IR" : "en-US"
diff --git a/frontend/src/locales/en.ts b/frontend/src/locales/en.ts
index 23dc07f..9231783 100644
--- a/frontend/src/locales/en.ts
+++ b/frontend/src/locales/en.ts
@@ -86,6 +86,7 @@ export default {
sub: "Subscription",
addr: "Address",
port: "Port",
+ webPath: "Base URI",
domain: "Domain",
sslKey: "SSL Key Path",
sslCert: "SSL Certificate Path",
diff --git a/frontend/src/locales/fa.ts b/frontend/src/locales/fa.ts
index ce7111d..4e5012c 100644
--- a/frontend/src/locales/fa.ts
+++ b/frontend/src/locales/fa.ts
@@ -86,6 +86,7 @@ export default {
sub: "سابسکریپشن",
addr: "آدرس",
port: "پورت",
+ webPath: "مسیر پایه",
domain: "دامنه",
sslKey: "مسیر فایل کلید",
sslCert: "مسیر فایل گواهی",
diff --git a/frontend/src/plugins/api.ts b/frontend/src/plugins/api.ts
index 138b275..f6c7a73 100644
--- a/frontend/src/plugins/api.ts
+++ b/frontend/src/plugins/api.ts
@@ -3,6 +3,8 @@ import axios from 'axios'
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'
+axios.defaults.baseURL = "./"
+
axios.interceptors.request.use(
(config) => {
if (config.data instanceof FormData) {
diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts
index 2ca4327..4cc6da7 100644
--- a/frontend/src/router/index.ts
+++ b/frontend/src/router/index.ts
@@ -54,7 +54,7 @@ const routes = [
]
const router = createRouter({
- history: createWebHistory(process.env.BASE_URL),
+ history: createWebHistory((window as any).BASE_URL),
routes,
})
diff --git a/frontend/src/store/modules/data.ts b/frontend/src/store/modules/data.ts
index 3deaaa4..ab99ffa 100644
--- a/frontend/src/store/modules/data.ts
+++ b/frontend/src/store/modules/data.ts
@@ -15,7 +15,7 @@ const Data = defineStore('Data', {
}),
actions: {
async loadData() {
- const msg = await HttpUtils.get('/api/load', this.lastLoad >0 ? {lu: this.lastLoad} : {} )
+ const msg = await HttpUtils.get('api/load', this.lastLoad >0 ? {lu: this.lastLoad} : {} )
if(msg.success) {
this.lastLoad = Math.floor((new Date()).getTime()/1000)
@@ -36,7 +36,7 @@ const Data = defineStore('Data', {
config: JSON.stringify(FindDiff.Config(this.config,this.oldData.config)),
clients: JSON.stringify(FindDiff.Clients(this.clients,this.oldData.clients)),
}
- const msg = await HttpUtils.post('/api/save',diff)
+ const msg = await HttpUtils.post('api/save',diff)
if(msg.success) {
this.loadData()
}
@@ -46,7 +46,7 @@ const Data = defineStore('Data', {
config: JSON.stringify([{key: "inbounds", action: "del", index: index, obj: null}]),
clients: JSON.stringify(FindDiff.Clients(this.clients,this.oldData.clients)),
}
- const msg = await HttpUtils.post('/api/save',diff)
+ const msg = await HttpUtils.post('api/save',diff)
if(msg.success) {
this.loadData()
}
@@ -56,7 +56,7 @@ const Data = defineStore('Data', {
config: JSON.stringify(FindDiff.Config(this.config,this.oldData.config)),
clients:JSON.stringify([{key: "clients", action: "del", index: id, obj: null}]),
}
- const msg = await HttpUtils.post('/api/save',diff)
+ const msg = await HttpUtils.post('api/save',diff)
if(msg.success) {
this.loadData()
}
diff --git a/frontend/src/views/Inbounds.vue b/frontend/src/views/Inbounds.vue
index 3bfbd4e..1b89c7d 100644
--- a/frontend/src/views/Inbounds.vue
+++ b/frontend/src/views/Inbounds.vue
@@ -53,7 +53,7 @@
{{ u }}
- {{ Object.hasOwn(item,'users') ? item.users.length : '-' }}
+ {{ Array.isArray(item.users) ? item.users.length : '-' }}
diff --git a/frontend/src/views/Login.vue b/frontend/src/views/Login.vue
index 4af7b78..9d497b8 100644
--- a/frontend/src/views/Login.vue
+++ b/frontend/src/views/Login.vue
@@ -63,7 +63,7 @@ const router = useRouter()
const login = async () => {
if (username.value == '' || password.value == '') return
loading.value=true
- const response = await HttpUtil.post('/api/login',{user: username.value, pass: password.value})
+ const response = await HttpUtil.post('api/login',{user: username.value, pass: password.value})
if(response.success){
setTimeout(() => {
loading.value=false
diff --git a/frontend/src/views/Settings.vue b/frontend/src/views/Settings.vue
index 2a9a05c..3d9ca5b 100644
--- a/frontend/src/views/Settings.vue
+++ b/frontend/src/views/Settings.vue
@@ -32,6 +32,9 @@
+
+
+
@@ -143,6 +146,7 @@ const settings = ref({
webPort: "2095",
webCertFile: "",
webKeyFile: "",
+ webPath: "/app/",
sessionMaxAge: "0",
timeLocation: "Asia/Tehran",
subListen: "",
@@ -166,7 +170,7 @@ const changeLocale = (l: any) => {
const loadData = async () => {
loading.value = true
- const msg = await HttpUtils.get('/api/setting')
+ const msg = await HttpUtils.get('api/setting')
loading.value = false
if (msg.success) {
settings.value = msg.obj
@@ -179,7 +183,7 @@ const saveChanges = async () => {
const diff = {
settings: JSON.stringify(FindDiff.Settings(settings.value,oldSettings.value)),
}
- const msg = await HttpUtils.post('/api/save', diff)
+ const msg = await HttpUtils.post('api/save', diff)
if (msg.success) {
loadData()
}
@@ -190,17 +194,17 @@ const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
const restartApp = async () => {
loading.value = true
- const msg = await HttpUtils.post('/api/restartApp',{})
+ const msg = await HttpUtils.post('api/restartApp',{})
if (msg.success) {
const isTLS = settings.value.webCertFile !== "" || settings.value.webKeyFile !== ""
- const url = buildURL(settings.value.webDomain,settings.value.webPort.toString(),isTLS)
+ const url = buildURL(settings.value.webDomain,settings.value.webPort.toString(),isTLS, settings.value.webPath)
await sleep(3000)
window.location.replace(url)
}
loading.value = false
}
-const buildURL = (host: string, port: string, isTLS: boolean ) => {
+const buildURL = (host: string, port: string, isTLS: boolean, path: string) => {
if (!host || host.length == 0) host = window.location.hostname
if (!port || port.length == 0) port = window.location.port
@@ -212,7 +216,7 @@ const buildURL = (host: string, port: string, isTLS: boolean ) => {
port = `:${port}`
}
- return `${protocol}//${host}${port}/settings`
+ return `${protocol}//${host}${port}${path}settings`
}
const subEncode = computed({
@@ -245,7 +249,6 @@ const subUpdates = computed({
set: (v:number) => { settings.value.subUpdates = v.toString() }
})
-
const stateChange = computed(() => {
return !FindDiff.deepCompare(settings.value,oldSettings.value)
})
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index c42e359..7019bdf 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -7,6 +7,7 @@ import { defineConfig } from 'vite'
import { fileURLToPath, URL } from 'node:url'
export default defineConfig({
+ base: '',
plugins: [
vue({
template: { transformAssetUrls },
@@ -40,7 +41,7 @@ export default defineConfig({
server: {
port: 3000,
proxy: {
- '/api': {
+ '/app/api': {
target: 'http://localhost:2095',
changeOrigin: true,
},