add base url
This commit is contained in:
+7
-2
@@ -27,7 +27,8 @@ func NewAPIHandler(g *gin.RouterGroup) {
|
|||||||
|
|
||||||
func (a *APIHandler) initRouter(g *gin.RouterGroup) {
|
func (a *APIHandler) initRouter(g *gin.RouterGroup) {
|
||||||
g.Use(func(c *gin.Context) {
|
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)
|
checkLogin(c)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -62,7 +63,11 @@ func (a *APIHandler) postHandler(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = SetLoginUser(c, loginUser)
|
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)
|
jsonMsg(c, "", nil)
|
||||||
case "save":
|
case "save":
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ var defaultValueMap = map[string]string{
|
|||||||
"webSecret": common.Random(32),
|
"webSecret": common.Random(32),
|
||||||
"webCertFile": "",
|
"webCertFile": "",
|
||||||
"webKeyFile": "",
|
"webKeyFile": "",
|
||||||
|
"webPath": "/app/",
|
||||||
"sessionMaxAge": "0",
|
"sessionMaxAge": "0",
|
||||||
"timeLocation": "Asia/Tehran",
|
"timeLocation": "Asia/Tehran",
|
||||||
"subListen": "",
|
"subListen": "",
|
||||||
@@ -158,6 +159,20 @@ func (s *SettingService) GetKeyFile() (string, error) {
|
|||||||
return s.getString("webKeyFile")
|
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) {
|
func (s *SettingService) GetSecret() ([]byte, error) {
|
||||||
secret, err := s.getString("webSecret")
|
secret, err := s.getString("webSecret")
|
||||||
if secret == defaultValueMap["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
|
err = tx.Model(model.Setting{}).Where("key = ?", key).Update("value", obj).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
+18
-10
@@ -53,6 +53,11 @@ func (s *Server) initRouter() (*gin.Engine, error) {
|
|||||||
|
|
||||||
engine := gin.Default()
|
engine := gin.Default()
|
||||||
|
|
||||||
|
base_url, err := s.settingService.GetWebPath()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
webDomain, err := s.settingService.GetWebDomain()
|
webDomain, err := s.settingService.GetWebDomain()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -68,10 +73,11 @@ func (s *Server) initRouter() (*gin.Engine, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
engine.Use(gzip.Gzip(gzip.DefaultCompression))
|
engine.Use(gzip.Gzip(gzip.DefaultCompression))
|
||||||
assetsBasePath := "/assets/"
|
assetsBasePath := base_url + "assets/"
|
||||||
|
|
||||||
store := cookie.NewStore(secret)
|
store := cookie.NewStore(secret)
|
||||||
engine.Use(sessions.Sessions("session", store))
|
engine.Use(sessions.Sessions("session", store))
|
||||||
|
|
||||||
engine.Use(func(c *gin.Context) {
|
engine.Use(func(c *gin.Context) {
|
||||||
uri := c.Request.RequestURI
|
uri := c.Request.RequestURI
|
||||||
if strings.HasPrefix(uri, assetsBasePath) {
|
if strings.HasPrefix(uri, assetsBasePath) {
|
||||||
@@ -87,26 +93,28 @@ func (s *Server) initRouter() (*gin.Engine, error) {
|
|||||||
|
|
||||||
engine.StaticFS(assetsBasePath, http.FS(assetsFS))
|
engine.StaticFS(assetsBasePath, http.FS(assetsFS))
|
||||||
|
|
||||||
group_api := engine.Group("/api")
|
group_api := engine.Group(base_url + "api")
|
||||||
api.NewAPIHandler(group_api)
|
api.NewAPIHandler(group_api)
|
||||||
|
|
||||||
|
// Load the HTML template
|
||||||
|
engine.LoadHTMLFiles("backend/web/html/index.html")
|
||||||
|
|
||||||
// Serve index.html as the entry point
|
// Serve index.html as the entry point
|
||||||
// Handle all other routes by serving index.html
|
// Handle all other routes by serving index.html
|
||||||
engine.NoRoute(func(c *gin.Context) {
|
engine.NoRoute(func(c *gin.Context) {
|
||||||
if c.Request.URL.Path != "/login" && !api.IsLogin(c) {
|
if !strings.HasPrefix(c.Request.URL.Path, base_url) {
|
||||||
c.Redirect(http.StatusTemporaryRedirect, "/login")
|
c.String(404, "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if c.Request.URL.Path == "/login" && api.IsLogin(c) {
|
if c.Request.URL.Path != base_url+"login" && !api.IsLogin(c) {
|
||||||
c.Redirect(http.StatusTemporaryRedirect, "/")
|
c.Redirect(http.StatusTemporaryRedirect, base_url+"login")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data, err := content.ReadFile("html/index.html")
|
if c.Request.URL.Path == base_url+"login" && api.IsLogin(c) {
|
||||||
if err != nil {
|
c.Redirect(http.StatusTemporaryRedirect, base_url)
|
||||||
c.String(http.StatusInternalServerError, "Internal Server Error")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.Data(http.StatusOK, "text/html", data)
|
c.HTML(http.StatusOK, "index.html", gin.H{"BASE_URL": base_url})
|
||||||
})
|
})
|
||||||
|
|
||||||
return engine, nil
|
return engine, nil
|
||||||
|
|||||||
@@ -5,6 +5,9 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" href="assets/favicon.ico" />
|
<link rel="icon" href="assets/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<script>
|
||||||
|
window.BASE_URL = "{{ .BASE_URL }}"
|
||||||
|
</script>
|
||||||
<title>S-UI</title>
|
<title>S-UI</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ const reloadItems = computed({
|
|||||||
|
|
||||||
const reloadData = async () => {
|
const reloadData = async () => {
|
||||||
const request = [...new Set(reloadItems.value.map(r => r.split('-')[1]))]
|
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) {
|
if (data.success) {
|
||||||
tilesData.value = data.obj
|
tilesData.value = data.obj
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ const menu = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
const logout = async () => {
|
const logout = async () => {
|
||||||
const response = await HttpUtil.get('/api/logout')
|
const response = await HttpUtil.get('api/logout')
|
||||||
if(response.success){
|
if(response.success){
|
||||||
router.push('/login')
|
router.push('/login')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
async loadData(limit: number) {
|
async loadData(limit: number) {
|
||||||
this.loading = true
|
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) {
|
if (data.success && data.obj) {
|
||||||
const obj = <any[]>data.obj
|
const obj = <any[]>data.obj
|
||||||
const l = String(i18n.global.locale) == 'fa' ? "fa-IR" : "en-US"
|
const l = String(i18n.global.locale) == 'fa' ? "fa-IR" : "en-US"
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ export default {
|
|||||||
sub: "Subscription",
|
sub: "Subscription",
|
||||||
addr: "Address",
|
addr: "Address",
|
||||||
port: "Port",
|
port: "Port",
|
||||||
|
webPath: "Base URI",
|
||||||
domain: "Domain",
|
domain: "Domain",
|
||||||
sslKey: "SSL Key Path",
|
sslKey: "SSL Key Path",
|
||||||
sslCert: "SSL Certificate Path",
|
sslCert: "SSL Certificate Path",
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ export default {
|
|||||||
sub: "سابسکریپشن",
|
sub: "سابسکریپشن",
|
||||||
addr: "آدرس",
|
addr: "آدرس",
|
||||||
port: "پورت",
|
port: "پورت",
|
||||||
|
webPath: "مسیر پایه",
|
||||||
domain: "دامنه",
|
domain: "دامنه",
|
||||||
sslKey: "مسیر فایل کلید",
|
sslKey: "مسیر فایل کلید",
|
||||||
sslCert: "مسیر فایل گواهی",
|
sslCert: "مسیر فایل گواهی",
|
||||||
|
|||||||
@@ -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.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
|
||||||
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'
|
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'
|
||||||
|
|
||||||
|
axios.defaults.baseURL = "./"
|
||||||
|
|
||||||
axios.interceptors.request.use(
|
axios.interceptors.request.use(
|
||||||
(config) => {
|
(config) => {
|
||||||
if (config.data instanceof FormData) {
|
if (config.data instanceof FormData) {
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ const routes = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(process.env.BASE_URL),
|
history: createWebHistory((window as any).BASE_URL),
|
||||||
routes,
|
routes,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const Data = defineStore('Data', {
|
|||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
async loadData() {
|
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) {
|
if(msg.success) {
|
||||||
this.lastLoad = Math.floor((new Date()).getTime()/1000)
|
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)),
|
config: JSON.stringify(FindDiff.Config(this.config,this.oldData.config)),
|
||||||
clients: JSON.stringify(FindDiff.Clients(this.clients,this.oldData.clients)),
|
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) {
|
if(msg.success) {
|
||||||
this.loadData()
|
this.loadData()
|
||||||
}
|
}
|
||||||
@@ -46,7 +46,7 @@ const Data = defineStore('Data', {
|
|||||||
config: JSON.stringify([{key: "inbounds", action: "del", index: index, obj: null}]),
|
config: JSON.stringify([{key: "inbounds", action: "del", index: index, obj: null}]),
|
||||||
clients: JSON.stringify(FindDiff.Clients(this.clients,this.oldData.clients)),
|
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) {
|
if(msg.success) {
|
||||||
this.loadData()
|
this.loadData()
|
||||||
}
|
}
|
||||||
@@ -56,7 +56,7 @@ const Data = defineStore('Data', {
|
|||||||
config: JSON.stringify(FindDiff.Config(this.config,this.oldData.config)),
|
config: JSON.stringify(FindDiff.Config(this.config,this.oldData.config)),
|
||||||
clients:JSON.stringify([{key: "clients", action: "del", index: id, obj: null}]),
|
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) {
|
if(msg.success) {
|
||||||
this.loadData()
|
this.loadData()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@
|
|||||||
<v-tooltip activator="parent" dir="ltr" location="bottom" v-if="Object.hasOwn(item,'users')">
|
<v-tooltip activator="parent" dir="ltr" location="bottom" v-if="Object.hasOwn(item,'users')">
|
||||||
<span v-for="u in findInbounsUsers(item)">{{ u }}<br /></span>
|
<span v-for="u in findInbounsUsers(item)">{{ u }}<br /></span>
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
{{ Object.hasOwn(item,'users') ? item.users.length : '-' }}
|
{{ Array.isArray(item.users) ? item.users.length : '-' }}
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
<v-row>
|
<v-row>
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ const router = useRouter()
|
|||||||
const login = async () => {
|
const login = async () => {
|
||||||
if (username.value == '' || password.value == '') return
|
if (username.value == '' || password.value == '') return
|
||||||
loading.value=true
|
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){
|
if(response.success){
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
loading.value=false
|
loading.value=false
|
||||||
|
|||||||
@@ -32,6 +32,9 @@
|
|||||||
<v-col cols="12" sm="6" md="4">
|
<v-col cols="12" sm="6" md="4">
|
||||||
<v-text-field v-model="webPort" :label="$t('setting.port')" hide-details></v-text-field>
|
<v-text-field v-model="webPort" :label="$t('setting.port')" hide-details></v-text-field>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
<v-col cols="12" sm="6" md="4">
|
||||||
|
<v-text-field v-model="settings.webPath" :label="$t('setting.webPath')" hide-details></v-text-field>
|
||||||
|
</v-col>
|
||||||
<v-col cols="12" sm="6" md="4">
|
<v-col cols="12" sm="6" md="4">
|
||||||
<v-text-field v-model="settings.webDomain" :label="$t('setting.domain')" hide-details></v-text-field>
|
<v-text-field v-model="settings.webDomain" :label="$t('setting.domain')" hide-details></v-text-field>
|
||||||
</v-col>
|
</v-col>
|
||||||
@@ -143,6 +146,7 @@ const settings = ref({
|
|||||||
webPort: "2095",
|
webPort: "2095",
|
||||||
webCertFile: "",
|
webCertFile: "",
|
||||||
webKeyFile: "",
|
webKeyFile: "",
|
||||||
|
webPath: "/app/",
|
||||||
sessionMaxAge: "0",
|
sessionMaxAge: "0",
|
||||||
timeLocation: "Asia/Tehran",
|
timeLocation: "Asia/Tehran",
|
||||||
subListen: "",
|
subListen: "",
|
||||||
@@ -166,7 +170,7 @@ const changeLocale = (l: any) => {
|
|||||||
|
|
||||||
const loadData = async () => {
|
const loadData = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
const msg = await HttpUtils.get('/api/setting')
|
const msg = await HttpUtils.get('api/setting')
|
||||||
loading.value = false
|
loading.value = false
|
||||||
if (msg.success) {
|
if (msg.success) {
|
||||||
settings.value = msg.obj
|
settings.value = msg.obj
|
||||||
@@ -179,7 +183,7 @@ const saveChanges = async () => {
|
|||||||
const diff = {
|
const diff = {
|
||||||
settings: JSON.stringify(FindDiff.Settings(settings.value,oldSettings.value)),
|
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) {
|
if (msg.success) {
|
||||||
loadData()
|
loadData()
|
||||||
}
|
}
|
||||||
@@ -190,17 +194,17 @@ const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
|
|||||||
|
|
||||||
const restartApp = async () => {
|
const restartApp = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
const msg = await HttpUtils.post('/api/restartApp',{})
|
const msg = await HttpUtils.post('api/restartApp',{})
|
||||||
if (msg.success) {
|
if (msg.success) {
|
||||||
const isTLS = settings.value.webCertFile !== "" || settings.value.webKeyFile !== ""
|
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)
|
await sleep(3000)
|
||||||
window.location.replace(url)
|
window.location.replace(url)
|
||||||
}
|
}
|
||||||
loading.value = false
|
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 (!host || host.length == 0) host = window.location.hostname
|
||||||
if (!port || port.length == 0) port = window.location.port
|
if (!port || port.length == 0) port = window.location.port
|
||||||
|
|
||||||
@@ -212,7 +216,7 @@ const buildURL = (host: string, port: string, isTLS: boolean ) => {
|
|||||||
port = `:${port}`
|
port = `:${port}`
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${protocol}//${host}${port}/settings`
|
return `${protocol}//${host}${port}${path}settings`
|
||||||
}
|
}
|
||||||
|
|
||||||
const subEncode = computed({
|
const subEncode = computed({
|
||||||
@@ -245,7 +249,6 @@ const subUpdates = computed({
|
|||||||
set: (v:number) => { settings.value.subUpdates = v.toString() }
|
set: (v:number) => { settings.value.subUpdates = v.toString() }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const stateChange = computed(() => {
|
const stateChange = computed(() => {
|
||||||
return !FindDiff.deepCompare(settings.value,oldSettings.value)
|
return !FindDiff.deepCompare(settings.value,oldSettings.value)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { defineConfig } from 'vite'
|
|||||||
import { fileURLToPath, URL } from 'node:url'
|
import { fileURLToPath, URL } from 'node:url'
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
base: '',
|
||||||
plugins: [
|
plugins: [
|
||||||
vue({
|
vue({
|
||||||
template: { transformAssetUrls },
|
template: { transformAssetUrls },
|
||||||
@@ -40,7 +41,7 @@ export default defineConfig({
|
|||||||
server: {
|
server: {
|
||||||
port: 3000,
|
port: 3000,
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/app/api': {
|
||||||
target: 'http://localhost:2095',
|
target: 'http://localhost:2095',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user