diff --git a/api/config/folder.go b/api/config/folder.go new file mode 100644 index 00000000..31af2e62 --- /dev/null +++ b/api/config/folder.go @@ -0,0 +1,36 @@ +package config + +import ( + "github.com/0xJacky/Nginx-UI/api" + "github.com/0xJacky/Nginx-UI/internal/helper" + "github.com/0xJacky/Nginx-UI/internal/nginx" + "github.com/gin-gonic/gin" + "net/http" + "os" +) + +func Mkdir(c *gin.Context) { + var json struct { + BasePath string `json:"base_path"` + FolderName string `json:"folder_name"` + } + if !api.BindAndValid(c, &json) { + return + } + fullPath := nginx.GetConfPath(json.BasePath, json.FolderName) + if !helper.IsUnderDirectory(fullPath, nginx.GetConfPath()) { + c.JSON(http.StatusForbidden, gin.H{ + "message": "You are not allowed to create a folder " + + "outside of the nginx configuration directory", + }) + return + } + err := os.Mkdir(fullPath, 0755) + if err != nil { + api.ErrHandler(c, err) + return + } + c.JSON(http.StatusOK, gin.H{ + "message": "ok", + }) +} diff --git a/api/config/rename.go b/api/config/rename.go new file mode 100644 index 00000000..b9bdcd4f --- /dev/null +++ b/api/config/rename.go @@ -0,0 +1,68 @@ +package config + +import ( + "github.com/0xJacky/Nginx-UI/api" + "github.com/0xJacky/Nginx-UI/internal/helper" + "github.com/0xJacky/Nginx-UI/internal/nginx" + "github.com/0xJacky/Nginx-UI/query" + "github.com/gin-gonic/gin" + "net/http" + "os" +) + +func Rename(c *gin.Context) { + var json struct { + BasePath string `json:"base_path"` + OrigName string `json:"orig_name"` + NewName string `json:"new_name"` + } + if !api.BindAndValid(c, &json) { + return + } + if json.OrigName == json.OrigName { + c.JSON(http.StatusOK, gin.H{ + "message": "ok", + }) + return + } + origFullPath := nginx.GetConfPath(json.BasePath, json.OrigName) + newFullPath := nginx.GetConfPath(json.BasePath, json.NewName) + if !helper.IsUnderDirectory(origFullPath, nginx.GetConfPath()) || + !helper.IsUnderDirectory(newFullPath, nginx.GetConfPath()) { + c.JSON(http.StatusForbidden, gin.H{ + "message": "you are not allowed to rename a file " + + "outside of the nginx config path", + }) + return + } + + stat, err := os.Stat(origFullPath) + if err != nil { + api.ErrHandler(c, err) + return + } + + if helper.FileExists(newFullPath) { + c.JSON(http.StatusNotAcceptable, gin.H{ + "message": "target file already exists", + }) + return + } + + err = os.Rename(origFullPath, newFullPath) + if err != nil { + api.ErrHandler(c, err) + return + } + + if !stat.IsDir() { + // update ChatGPT records + g := query.ChatGPTLog + _, _ = g.Where(g.Name.Eq(newFullPath)).Delete() + _, _ = g.Where(g.Name.Eq(origFullPath)).Update(g.Name, newFullPath) + } + + c.JSON(http.StatusOK, gin.H{ + "message": "ok", + }) +} diff --git a/api/config/router.go b/api/config/router.go index bac3775b..76fb66be 100644 --- a/api/config/router.go +++ b/api/config/router.go @@ -3,9 +3,12 @@ package config import "github.com/gin-gonic/gin" func InitRouter(r *gin.RouterGroup) { + r.GET("config_base_path", GetBasePath) + r.GET("configs", GetConfigs) r.GET("config/*name", GetConfig) r.POST("config", AddConfig) r.POST("config/*name", EditConfig) - r.GET("config_base_path", GetBasePath) + r.POST("config_mkdir", Mkdir) + r.POST("config_rename", Rename) } diff --git a/app/src/api/config.ts b/app/src/api/config.ts index 2416e658..98af72b9 100644 --- a/app/src/api/config.ts +++ b/app/src/api/config.ts @@ -18,6 +18,14 @@ class ConfigCurd extends Curd { get_base_path() { return http.get('/config_base_path') } + + mkdir(basePath: string, name: string) { + return http.post('/config_mkdir', { base_path: basePath, folder_name: name }) + } + + rename(basePath: string, origName: string, newName: string) { + return http.post('/config_rename', { base_path: basePath, orig_name: origName, new_name: newName }) + } } const config: ConfigCurd = new ConfigCurd() diff --git a/app/src/components/OTP/useOTPModal.ts b/app/src/components/OTP/useOTPModal.ts index 9e6e8cf3..7f0325b2 100644 --- a/app/src/components/OTP/useOTPModal.ts +++ b/app/src/components/OTP/useOTPModal.ts @@ -3,6 +3,7 @@ import { Modal, message } from 'ant-design-vue' import { useCookies } from '@vueuse/integrations/useCookies' import OTPAuthorization from '@/components/OTP/OTPAuthorization.vue' import otp from '@/api/otp' +import { useUserStore } from '@/pinia' export interface OTPModalProps { onOk?: (secureSessionId: string) => void @@ -12,6 +13,7 @@ export interface OTPModalProps { const useOTPModal = () => { const refOTPAuthorization = ref() const randomId = Math.random().toString(36).substring(2, 8) + const { secureSessionId } = storeToRefs(useUserStore()) const injectStyles = () => { const style = document.createElement('style') @@ -36,6 +38,7 @@ const useOTPModal = () => { const ssid = cookies.get('secure_session_id') if (ssid) { onOk?.(ssid) + secureSessionId.value = ssid return } @@ -55,6 +58,7 @@ const useOTPModal = () => { cookies.set('secure_session_id', r.session_id, { maxAge: 60 * 3 }) onOk?.(r.session_id) close() + secureSessionId.value = r.session_id }).catch(async () => { refOTPAuthorization.value?.clearInput() await message.error($gettext('Invalid passcode or recovery code')) diff --git a/app/src/lib/http/index.ts b/app/src/lib/http/index.ts index 0ede1d91..628af67c 100644 --- a/app/src/lib/http/index.ts +++ b/app/src/lib/http/index.ts @@ -9,7 +9,7 @@ import router from '@/routes' const user = useUserStore() const settings = useSettingsStore() -const { token } = storeToRefs(user) +const { token, secureSessionId } = storeToRefs(user) const instance = axios.create({ baseURL: import.meta.env.VITE_API_ROOT, @@ -28,7 +28,7 @@ const instance = axios.create({ instance.interceptors.request.use( config => { NProgress.start() - if (token) { + if (token.value) { // eslint-disable-next-line @typescript-eslint/no-explicit-any (config.headers as any).Authorization = token.value } @@ -38,6 +38,11 @@ instance.interceptors.request.use( (config.headers as any)['X-Node-ID'] = settings.environment.id } + if (secureSessionId.value) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (config.headers as any)['X-Secure-Session-ID'] = secureSessionId.value + } + return config }, err => { diff --git a/app/src/pinia/moudule/user.ts b/app/src/pinia/moudule/user.ts index c495d9b6..8f21c803 100644 --- a/app/src/pinia/moudule/user.ts +++ b/app/src/pinia/moudule/user.ts @@ -4,6 +4,7 @@ export const useUserStore = defineStore('user', { state: () => ({ token: '', unreadCount: 0, + secureSessionId: '', }), getters: { is_login(state): boolean { diff --git a/app/src/views/config/Config.vue b/app/src/views/config/Config.vue index 61a22496..19c048b4 100644 --- a/app/src/views/config/Config.vue +++ b/app/src/views/config/Config.vue @@ -1,10 +1,13 @@