diff --git a/api/config/history.go b/api/config/history.go new file mode 100644 index 00000000..83083d6f --- /dev/null +++ b/api/config/history.go @@ -0,0 +1,13 @@ +package config + +import ( + "github.com/0xJacky/Nginx-UI/model" + "github.com/gin-gonic/gin" + "github.com/uozi-tech/cosy" +) + +func GetConfigHistory(c *gin.Context) { + cosy.Core[model.ConfigBackup](c). + SetEqual("filepath"). + PagingList() +} diff --git a/api/config/modify.go b/api/config/modify.go index e2eda895..153c1964 100644 --- a/api/config/modify.go +++ b/api/config/modify.go @@ -47,6 +47,12 @@ func EditConfig(c *gin.Context) { return } + err = config.CheckAndCreateHistory(absPath, content) + if err != nil { + cosy.ErrHandler(c, err) + return + } + if content != "" && content != string(origContent) { err = os.WriteFile(absPath, []byte(content), 0644) if err != nil { diff --git a/api/config/router.go b/api/config/router.go index 2d9b9d0f..b840d560 100644 --- a/api/config/router.go +++ b/api/config/router.go @@ -18,4 +18,6 @@ func InitRouter(r *gin.RouterGroup) { o.POST("config_mkdir", Mkdir) o.POST("config_rename", Rename) } + + r.GET("config_histories", GetConfigHistory) } diff --git a/app/components.d.ts b/app/components.d.ts index 05c858f8..cc410b95 100644 --- a/app/components.d.ts +++ b/app/components.d.ts @@ -77,6 +77,8 @@ declare module 'vue' { ChartUsageProgressLine: typeof import('./src/components/Chart/UsageProgressLine.vue')['default'] ChatGPTChatGPT: typeof import('./src/components/ChatGPT/ChatGPT.vue')['default'] CodeEditorCodeEditor: typeof import('./src/components/CodeEditor/CodeEditor.vue')['default'] + ConfigHistoryConfigHistory: typeof import('./src/components/ConfigHistory/ConfigHistory.vue')['default'] + ConfigHistoryDiffViewer: typeof import('./src/components/ConfigHistory/DiffViewer.vue')['default'] EnvGroupTabsEnvGroupTabs: typeof import('./src/components/EnvGroupTabs/EnvGroupTabs.vue')['default'] EnvIndicatorEnvIndicator: typeof import('./src/components/EnvIndicator/EnvIndicator.vue')['default'] FooterToolbarFooterToolBar: typeof import('./src/components/FooterToolbar/FooterToolBar.vue')['default'] diff --git a/app/src/api/config.ts b/app/src/api/config.ts index f1876c7d..4a4abe00 100644 --- a/app/src/api/config.ts +++ b/app/src/api/config.ts @@ -1,7 +1,14 @@ +import type { GetListResponse } from '@/api/curd' import type { ChatComplicationMessage } from '@/api/openai' import Curd from '@/api/curd' import http from '@/lib/http' +export interface ModelBase { + id: number + created_at: string + updated_at: string +} + export interface Config { name: string content: string @@ -13,6 +20,12 @@ export interface Config { dir: string } +export interface ConfigBackup extends ModelBase { + name: string + filepath: string + content: string +} + class ConfigCurd extends Curd { constructor() { super('/configs') @@ -34,6 +47,10 @@ class ConfigCurd extends Curd { sync_node_ids: syncNodeIds, }) } + + get_history(filepath: string) { + return http.get>('/config_histories', { params: { filepath } }) + } } const config: ConfigCurd = new ConfigCurd() diff --git a/app/src/components/ConfigHistory/ConfigHistory.vue b/app/src/components/ConfigHistory/ConfigHistory.vue new file mode 100644 index 00000000..1f1e1e38 --- /dev/null +++ b/app/src/components/ConfigHistory/ConfigHistory.vue @@ -0,0 +1,188 @@ + + + + + diff --git a/app/src/components/ConfigHistory/DiffViewer.vue b/app/src/components/ConfigHistory/DiffViewer.vue new file mode 100644 index 00000000..6dc14c94 --- /dev/null +++ b/app/src/components/ConfigHistory/DiffViewer.vue @@ -0,0 +1,415 @@ + + + + + diff --git a/app/src/components/ConfigHistory/index.ts b/app/src/components/ConfigHistory/index.ts new file mode 100644 index 00000000..bd89b8b4 --- /dev/null +++ b/app/src/components/ConfigHistory/index.ts @@ -0,0 +1,5 @@ +import ConfigHistory from './ConfigHistory.vue' +import DiffViewer from './DiffViewer.vue' + +export { ConfigHistory, DiffViewer } +export default ConfigHistory diff --git a/app/src/views/config/ConfigEditor.vue b/app/src/views/config/ConfigEditor.vue index 8051c257..6fc1f95b 100644 --- a/app/src/views/config/ConfigEditor.vue +++ b/app/src/views/config/ConfigEditor.vue @@ -6,6 +6,7 @@ import config from '@/api/config' import ngx from '@/api/ngx' import ChatGPT from '@/components/ChatGPT/ChatGPT.vue' import CodeEditor from '@/components/CodeEditor/CodeEditor.vue' +import { ConfigHistory } from '@/components/ConfigHistory' import FooterToolBar from '@/components/FooterToolbar/FooterToolBar.vue' import NodeSelector from '@/components/NodeSelector/NodeSelector.vue' import { useBreadcrumbs } from '@/composables/useBreadcrumbs' @@ -13,7 +14,7 @@ import { formatDateTime } from '@/lib/helper' import { useSettingsStore } from '@/pinia' import ConfigName from '@/views/config/components/ConfigName.vue' import InspectConfig from '@/views/config/InspectConfig.vue' -import { InfoCircleOutlined } from '@ant-design/icons-vue' +import { HistoryOutlined, InfoCircleOutlined } from '@ant-design/icons-vue' import { message } from 'ant-design-vue' import _ from 'lodash' @@ -28,6 +29,7 @@ const origName = ref('') const addMode = computed(() => !route.params.name) const errors = ref({}) +const showHistory = ref(false) const basePath = computed(() => { if (route.query.basePath) return _.trim(route?.query?.basePath?.toString(), '/') @@ -192,6 +194,10 @@ function goBack() { }, }) } + +function openHistory() { + showHistory.value = true +} diff --git a/app/src/views/site/site_edit/SiteEdit.vue b/app/src/views/site/site_edit/SiteEdit.vue index 495da040..70534c14 100644 --- a/app/src/views/site/site_edit/SiteEdit.vue +++ b/app/src/views/site/site_edit/SiteEdit.vue @@ -9,9 +9,11 @@ import config from '@/api/config' import ngx from '@/api/ngx' import site from '@/api/site' import CodeEditor from '@/components/CodeEditor/CodeEditor.vue' +import { ConfigHistory } from '@/components/ConfigHistory' import FooterToolBar from '@/components/FooterToolbar/FooterToolBar.vue' import NgxConfigEditor from '@/views/site/ngx_conf/NgxConfigEditor.vue' import RightSettings from '@/views/site/site_edit/RightSettings.vue' +import { HistoryOutlined } from '@ant-design/icons-vue' import { message } from 'ant-design-vue' const route = useRoute() @@ -40,6 +42,8 @@ const data = ref({}) as Ref const historyChatgptRecord = ref([]) as Ref const loading = ref(true) +const showHistory = ref(false) + onMounted(init) const advanceMode = computed({ @@ -156,6 +160,10 @@ async function save() { }) } +function openHistory() { + showHistory.value = true +} + provide('save_config', save) provide('configText', configText) provide('ngx_config', ngx_config) @@ -192,23 +200,35 @@ provide('data', data) @@ -273,6 +293,12 @@ provide('data', data) + + diff --git a/app/src/views/stream/StreamEdit.vue b/app/src/views/stream/StreamEdit.vue index 46f34f04..7f8debe5 100644 --- a/app/src/views/stream/StreamEdit.vue +++ b/app/src/views/stream/StreamEdit.vue @@ -9,9 +9,11 @@ import config from '@/api/config' import ngx from '@/api/ngx' import stream from '@/api/stream' import CodeEditor from '@/components/CodeEditor/CodeEditor.vue' +import { ConfigHistory } from '@/components/ConfigHistory' import FooterToolBar from '@/components/FooterToolbar/FooterToolBar.vue' import NgxConfigEditor from '@/views/site/ngx_conf/NgxConfigEditor.vue' import RightSettings from '@/views/stream/components/RightSettings.vue' +import { HistoryOutlined } from '@ant-design/icons-vue' import { message } from 'ant-design-vue' const route = useRoute() @@ -39,6 +41,8 @@ const parseErrorStatus = ref(false) const parseErrorMessage = ref('') const data = ref({} as Stream) +const showHistory = ref(false) + init() const advanceMode = computed({ @@ -144,6 +148,10 @@ async function save() { }) } +function openHistory() { + showHistory.value = true +} + provide('save_config', save) provide('configText', configText) provide('ngx_config', ngxConfig) @@ -179,22 +187,34 @@ provide('data', data) @@ -256,16 +276,19 @@ provide('data', data) + + - -