add base url

This commit is contained in:
Alireza Ahmadi
2024-02-26 19:01:01 +01:00
parent 30c9ed6aa7
commit c54d9c15bc
16 changed files with 80 additions and 30 deletions
+6 -1
View File
@@ -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)
if err == nil {
logger.Info("user ", loginUser, " login success") logger.Info("user ", loginUser, " login success")
} else {
logger.Warning("login failed: ", err)
}
jsonMsg(c, "", nil) jsonMsg(c, "", nil)
case "save": case "save":
+26
View File
@@ -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
View File
@@ -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
+3
View File
@@ -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>
+1 -1
View File
@@ -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
} }
+1 -1
View File
@@ -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')
} }
+1 -1
View File
@@ -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"
+1
View File
@@ -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",
+1
View File
@@ -86,6 +86,7 @@ export default {
sub: "سابسکریپشن", sub: "سابسکریپشن",
addr: "آدرس", addr: "آدرس",
port: "پورت", port: "پورت",
webPath: "مسیر پایه",
domain: "دامنه", domain: "دامنه",
sslKey: "مسیر فایل کلید", sslKey: "مسیر فایل کلید",
sslCert: "مسیر فایل گواهی", sslCert: "مسیر فایل گواهی",
+2
View File
@@ -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) {
+1 -1
View File
@@ -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,
}) })
+4 -4
View File
@@ -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()
} }
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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
+10 -7
View File
@@ -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)
}) })
+2 -1
View File
@@ -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,
}, },