feat: sync streams

This commit is contained in:
Jacky 2025-02-25 15:53:08 +08:00
parent 082ccc18bc
commit 34fa4eb204
No known key found for this signature in database
GPG key ID: 215C21B10DF38B4D
27 changed files with 788 additions and 557 deletions

View file

@ -1,12 +1,13 @@
package streams
import (
"net/http"
"github.com/0xJacky/Nginx-UI/api"
"github.com/0xJacky/Nginx-UI/internal/nginx"
"github.com/0xJacky/Nginx-UI/query"
"github.com/gin-gonic/gin"
"github.com/uozi-tech/cosy"
"net/http"
)
func AdvancedEdit(c *gin.Context) {
@ -21,7 +22,7 @@ func AdvancedEdit(c *gin.Context) {
name := c.Param("name")
path := nginx.GetConfPath("streams-available", name)
s := query.Site
s := query.Stream
_, err := s.Where(s.Path.Eq(path)).FirstOrCreate()
if err != nil {
@ -39,5 +40,4 @@ func AdvancedEdit(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "ok",
})
}

View file

@ -6,6 +6,7 @@ func InitRouter(r *gin.RouterGroup) {
r.GET("streams", GetStreams)
r.GET("streams/:name", GetStream)
r.POST("streams/:name", SaveStream)
r.POST("streams/:name/rename", RenameStream)
r.POST("streams/:name/enable", EnableStream)
r.POST("streams/:name/disable", DisableStream)
r.POST("streams/:name/advance", AdvancedEdit)

View file

@ -1,18 +1,19 @@
package streams
import (
"github.com/0xJacky/Nginx-UI/api"
"github.com/0xJacky/Nginx-UI/internal/config"
"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"
"github.com/sashabaranov/go-openai"
"github.com/uozi-tech/cosy"
"net/http"
"os"
"strings"
"time"
"github.com/0xJacky/Nginx-UI/api"
"github.com/0xJacky/Nginx-UI/internal/config"
"github.com/0xJacky/Nginx-UI/internal/nginx"
"github.com/0xJacky/Nginx-UI/internal/stream"
"github.com/0xJacky/Nginx-UI/query"
"github.com/gin-gonic/gin"
"github.com/sashabaranov/go-openai"
"github.com/uozi-tech/cosy"
)
type Stream struct {
@ -24,6 +25,7 @@ type Stream struct {
ChatGPTMessages []openai.ChatCompletionMessage `json:"chatgpt_messages,omitempty"`
Tokenized *nginx.NgxConfig `json:"tokenized,omitempty"`
Filepath string `json:"filepath"`
SyncNodeIDs []uint64 `json:"sync_node_ids" gorm:"serializer:json"`
}
func GetStreams(c *gin.Context) {
@ -32,14 +34,12 @@ func GetStreams(c *gin.Context) {
sort := c.DefaultQuery("sort", "desc")
configFiles, err := os.ReadDir(nginx.GetConfPath("streams-available"))
if err != nil {
api.ErrHandler(c, err)
return
}
enabledConfig, err := os.ReadDir(nginx.GetConfPath("streams-enabled"))
if err != nil {
api.ErrHandler(c, err)
return
@ -77,15 +77,8 @@ func GetStreams(c *gin.Context) {
}
func GetStream(c *gin.Context) {
rewriteName, ok := c.Get("rewriteConfigFileName")
name := c.Param("name")
// for modify filename
if ok {
name = rewriteName.(string)
}
path := nginx.GetConfPath("streams-available", name)
file, err := os.Stat(path)
if os.IsNotExist(err) {
@ -114,14 +107,13 @@ func GetStream(c *gin.Context) {
}
s := query.Stream
stream, err := s.Where(s.Path.Eq(path)).FirstOrInit()
streamModel, err := s.Where(s.Path.Eq(path)).FirstOrCreate()
if err != nil {
api.ErrHandler(c, err)
return
}
if stream.Advanced {
if streamModel.Advanced {
origContent, err := os.ReadFile(path)
if err != nil {
api.ErrHandler(c, err)
@ -130,12 +122,13 @@ func GetStream(c *gin.Context) {
c.JSON(http.StatusOK, Stream{
ModifiedAt: file.ModTime(),
Advanced: stream.Advanced,
Advanced: streamModel.Advanced,
Enabled: enabled,
Name: name,
Config: string(origContent),
ChatGPTMessages: chatgpt.Content,
Filepath: path,
SyncNodeIDs: streamModel.SyncNodeIDs,
})
return
}
@ -149,29 +142,23 @@ func GetStream(c *gin.Context) {
c.JSON(http.StatusOK, Stream{
ModifiedAt: file.ModTime(),
Advanced: stream.Advanced,
Advanced: streamModel.Advanced,
Enabled: enabled,
Name: name,
Config: nginxConfig.FmtCode(),
Tokenized: nginxConfig,
ChatGPTMessages: chatgpt.Content,
Filepath: path,
SyncNodeIDs: streamModel.SyncNodeIDs,
})
}
func SaveStream(c *gin.Context) {
name := c.Param("name")
if name == "" {
c.JSON(http.StatusNotAcceptable, gin.H{
"message": "param name is empty",
})
return
}
var json struct {
Name string `json:"name" binding:"required"`
Content string `json:"content" binding:"required"`
SyncNodeIDs []uint64 `json:"sync_node_ids"`
Overwrite bool `json:"overwrite"`
}
@ -179,177 +166,62 @@ func SaveStream(c *gin.Context) {
return
}
path := nginx.GetConfPath("streams-available", name)
if !json.Overwrite && helper.FileExists(path) {
c.JSON(http.StatusNotAcceptable, gin.H{
"message": "File exists",
})
return
}
err := os.WriteFile(path, []byte(json.Content), 0644)
err := stream.Save(name, json.Content, json.Overwrite, json.SyncNodeIDs)
if err != nil {
api.ErrHandler(c, err)
return
}
enabledConfigFilePath := nginx.GetConfPath("streams-enabled", name)
// rename the config file if needed
if name != json.Name {
newPath := nginx.GetConfPath("streams-available", json.Name)
s := query.Stream
_, err = s.Where(s.Path.Eq(path)).Update(s.Path, newPath)
// check if dst file exists, do not rename
if helper.FileExists(newPath) {
c.JSON(http.StatusNotAcceptable, gin.H{
"message": "File exists",
})
return
}
// recreate a soft link
if helper.FileExists(enabledConfigFilePath) {
_ = os.Remove(enabledConfigFilePath)
enabledConfigFilePath = nginx.GetConfPath("streams-enabled", json.Name)
err = os.Symlink(newPath, enabledConfigFilePath)
if err != nil {
api.ErrHandler(c, err)
return
}
}
err = os.Rename(path, newPath)
if err != nil {
api.ErrHandler(c, err)
return
}
name = json.Name
c.Set("rewriteConfigFileName", name)
}
enabledConfigFilePath = nginx.GetConfPath("streams-enabled", name)
if helper.FileExists(enabledConfigFilePath) {
// Test nginx configuration
output := nginx.TestConf()
if nginx.GetLogLevel(output) > nginx.Warn {
c.JSON(http.StatusInternalServerError, gin.H{
"message": output,
})
return
}
output = nginx.Reload()
if nginx.GetLogLevel(output) > nginx.Warn {
c.JSON(http.StatusInternalServerError, gin.H{
"message": output,
})
return
}
}
GetStream(c)
}
func EnableStream(c *gin.Context) {
configFilePath := nginx.GetConfPath("streams-available", c.Param("name"))
enabledConfigFilePath := nginx.GetConfPath("streams-enabled", c.Param("name"))
_, err := os.Stat(configFilePath)
err := stream.Enable(c.Param("name"))
if err != nil {
api.ErrHandler(c, err)
return
}
if _, err = os.Stat(enabledConfigFilePath); os.IsNotExist(err) {
err = os.Symlink(configFilePath, enabledConfigFilePath)
if err != nil {
api.ErrHandler(c, err)
return
}
}
// Test nginx config, if not pass, then disable the stream.
output := nginx.TestConf()
if nginx.GetLogLevel(output) > nginx.Warn {
_ = os.Remove(enabledConfigFilePath)
c.JSON(http.StatusInternalServerError, gin.H{
"message": output,
})
return
}
output = nginx.Reload()
if nginx.GetLogLevel(output) > nginx.Warn {
c.JSON(http.StatusInternalServerError, gin.H{
"message": output,
})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "ok",
})
}
func DisableStream(c *gin.Context) {
enabledConfigFilePath := nginx.GetConfPath("streams-enabled", c.Param("name"))
_, err := os.Stat(enabledConfigFilePath)
err := stream.Disable(c.Param("name"))
if err != nil {
api.ErrHandler(c, err)
return
}
err = os.Remove(enabledConfigFilePath)
if err != nil {
api.ErrHandler(c, err)
return
}
output := nginx.Reload()
if nginx.GetLogLevel(output) > nginx.Warn {
c.JSON(http.StatusInternalServerError, gin.H{
"message": output,
})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "ok",
})
}
func DeleteStream(c *gin.Context) {
var err error
name := c.Param("name")
availablePath := nginx.GetConfPath("streams-available", name)
enabledPath := nginx.GetConfPath("streams-enabled", name)
if _, err = os.Stat(availablePath); os.IsNotExist(err) {
c.JSON(http.StatusNotFound, gin.H{
"message": "stream not found",
})
return
}
if _, err = os.Stat(enabledPath); err == nil {
c.JSON(http.StatusNotAcceptable, gin.H{
"message": "stream is enabled",
})
return
}
if err = os.Remove(availablePath); err != nil {
err := stream.Delete(c.Param("name"))
if err != nil {
api.ErrHandler(c, err)
return
}
c.JSON(http.StatusOK, gin.H{
"message": "ok",
})
}
func RenameStream(c *gin.Context) {
oldName := c.Param("name")
var json struct {
NewName string `json:"new_name"`
}
if !cosy.BindAndValid(c, &json) {
return
}
err := stream.Rename(oldName, json.NewName)
if err != nil {
api.ErrHandler(c, err)
return
}

View file

@ -12,6 +12,7 @@ export interface Stream {
config: string
chatgpt_messages: ChatComplicationMessage[]
tokenized?: NgxConfig
sync_node_ids: number[]
}
class StreamCurd extends Curd<Stream> {
@ -31,6 +32,10 @@ class StreamCurd extends Curd<Stream> {
advance_mode(name: string, data: { advanced: boolean }) {
return http.post(`${this.baseUrl}/${name}/advance`, data)
}
rename(name: string, newName: string) {
return http.post(`${this.baseUrl}/${name}/rename`, { new_name: newName })
}
}
const stream = new StreamCurd('/streams')

View file

@ -32,6 +32,12 @@ function newSSE() {
s.onmessage = (e: SSEvent) => {
data.value = JSON.parse(e.data)
nextTick(() => {
data_map.value = data.value.reduce((acc, node) => {
acc[node.id] = node
return acc
}, {} as Record<number, Environment>)
})
}
// reconnect

View file

@ -0,0 +1,5 @@
export default {
40401: () => $gettext('Stream not found'),
50001: () => $gettext('Destination file already exists'),
50002: () => $gettext('Stream is enabled'),
}

View file

@ -3,6 +3,7 @@ export default {
40303: () => $gettext('User banned'),
40304: () => $gettext('Invalid otp code'),
40305: () => $gettext('Invalid recovery code'),
40306: () => $gettext('Legacy recovery code not allowed since totp is not enabled'),
50000: () => $gettext('WebAuthn settings are not configured'),
50001: () => $gettext('User not enabled otp as 2fa'),
50002: () => $gettext('Otp or recovery code empty'),

View file

@ -91,7 +91,7 @@ export const routes: RouteRecordRaw[] = [
},
},
{
path: 'stream/:name',
path: 'streams/:name',
name: 'Edit Stream',
component: () => import('@/views/stream/StreamEdit.vue'),
meta: {

View file

@ -23,7 +23,7 @@ watch(route, () => {
name.value = route.params?.name?.toString() ?? ''
})
const ngx_config: NgxConfig = reactive({
const ngxConfig: NgxConfig = reactive({
name: '',
upstreams: [],
servers: [],
@ -31,81 +31,81 @@ const ngx_config: NgxConfig = reactive({
const enabled = ref(false)
const configText = ref('')
const advance_mode_ref = ref(false)
const advanceModeRef = ref(false)
const saving = ref(false)
const filename = ref('')
const filepath = ref('')
const parse_error_status = ref(false)
const parse_error_message = ref('')
const data = ref({})
const parseErrorStatus = ref(false)
const parseErrorMessage = ref('')
const data = ref<Stream>({} as Stream)
init()
const advance_mode = computed({
const advanceMode = computed({
get() {
return advance_mode_ref.value || parse_error_status.value
return advanceModeRef.value || parseErrorStatus.value
},
set(v: boolean) {
advance_mode_ref.value = v
advanceModeRef.value = v
},
})
const history_chatgpt_record = ref([]) as Ref<ChatComplicationMessage[]>
const historyChatgptRecord = ref([]) as Ref<ChatComplicationMessage[]>
function handle_response(r: Stream) {
function handleResponse(r: Stream) {
if (r.advanced)
advance_mode.value = true
advanceMode.value = true
if (r.advanced)
advance_mode.value = true
advanceMode.value = true
parse_error_status.value = false
parse_error_message.value = ''
parseErrorStatus.value = false
parseErrorMessage.value = ''
filename.value = r.name
filepath.value = r.filepath
configText.value = r.config
enabled.value = r.enabled
history_chatgpt_record.value = r.chatgpt_messages
historyChatgptRecord.value = r.chatgpt_messages
data.value = r
Object.assign(ngx_config, r.tokenized)
Object.assign(ngxConfig, r.tokenized)
}
function init() {
if (name.value) {
stream.get(name.value).then(r => {
handle_response(r)
}).catch(handle_parse_error)
handleResponse(r)
}).catch(handleParseError)
}
else {
history_chatgpt_record.value = []
historyChatgptRecord.value = []
}
}
function handle_parse_error(e: { error?: string, message: string }) {
function handleParseError(e: { error?: string, message: string }) {
console.error(e)
parse_error_status.value = true
parse_error_message.value = e.message
parseErrorStatus.value = true
parseErrorMessage.value = e.message
config.get(`streams-available/${name.value}`).then(r => {
configText.value = r.content
})
}
function on_mode_change(advanced: CheckedType) {
function onModeChange(advanced: CheckedType) {
stream.advance_mode(name.value, { advanced: advanced as boolean }).then(() => {
advance_mode.value = advanced as boolean
advanceMode.value = advanced as boolean
if (advanced) {
build_config()
buildConfig()
}
else {
return ngx.tokenize_config(configText.value).then(r => {
Object.assign(ngx_config, r)
}).catch(handle_parse_error)
Object.assign(ngxConfig, r)
}).catch(handleParseError)
}
})
}
async function build_config() {
return ngx.build_config(ngx_config).then(r => {
async function buildConfig() {
return ngx.build_config(ngxConfig).then(r => {
configText.value = r.content
})
}
@ -113,9 +113,9 @@ async function build_config() {
async function save() {
saving.value = true
if (!advance_mode.value) {
if (!advanceMode.value) {
try {
await build_config()
await buildConfig()
}
catch {
saving.value = false
@ -129,22 +129,23 @@ async function save() {
name: filename.value || name.value,
content: configText.value,
overwrite: true,
sync_node_ids: data.value?.sync_node_ids,
}).then(r => {
handle_response(r)
handleResponse(r)
router.push({
path: `/stream/${filename.value}`,
path: `/streams/${filename.value}`,
query: route.query,
})
message.success($gettext('Saved successfully'))
}).catch(handle_parse_error).finally(() => {
}).catch(handleParseError).finally(() => {
saving.value = false
})
}
provide('save_config', save)
provide('configText', configText)
provide('ngx_config', ngx_config)
provide('history_chatgpt_record', history_chatgpt_record)
provide('ngx_config', ngxConfig)
provide('history_chatgpt_record', historyChatgptRecord)
provide('enabled', enabled)
provide('name', name)
provide('filename', filename)
@ -180,12 +181,12 @@ provide('data', data)
<div class="switch">
<ASwitch
size="small"
:disabled="parse_error_status"
:checked="advance_mode"
@change="on_mode_change"
:disabled="parseErrorStatus"
:checked="advanceMode"
@change="onModeChange"
/>
</div>
<template v-if="advance_mode">
<template v-if="advanceMode">
<div>{{ $gettext('Advance Mode') }}</div>
</template>
<template v-else>
@ -196,16 +197,16 @@ provide('data', data)
<Transition name="slide-fade">
<div
v-if="advance_mode"
v-if="advanceMode"
key="advance"
>
<div
v-if="parse_error_status"
v-if="parseErrorStatus"
class="parse-error-alert-wrapper"
>
<AAlert
:message="$gettext('Nginx Configuration Parse Error')"
:description="parse_error_message"
:description="parseErrorMessage"
type="error"
show-icon
/>

View file

@ -18,7 +18,6 @@ const columns: Column[] = [{
type: input,
},
search: true,
width: 200,
}, {
title: () => $gettext('Status'),
dataIndex: 'enabled',
@ -38,17 +37,18 @@ const columns: Column[] = [{
},
sorter: true,
pithy: true,
width: 100,
width: 200,
}, {
title: () => $gettext('Updated at'),
dataIndex: 'modified_at',
customRender: datetime,
sorter: true,
pithy: true,
width: 200,
}, {
title: () => $gettext('Action'),
dataIndex: 'action',
width: 120,
width: 250,
fixed: 'right',
}]
@ -132,7 +132,7 @@ function handleAddStream() {
disable-view
:scroll-x="800"
@click-edit="r => $router.push({
path: `/stream/${r}`,
path: `/streams/${r}`,
})"
>
<template #actions="{ record }">

View file

@ -0,0 +1,61 @@
<script setup lang="ts">
import stream from '@/api/stream'
import { message } from 'ant-design-vue'
const props = defineProps<{
name: string
}>()
const router = useRouter()
const modify = ref(false)
const buffer = ref('')
const loading = ref(false)
onMounted(() => {
buffer.value = props.name
})
function clickModify() {
modify.value = true
}
function save() {
loading.value = true
stream.rename(props.name, buffer.value).then(() => {
modify.value = false
message.success($gettext('Renamed successfully'))
router.push({
path: `/streams/${buffer.value}`,
})
}).finally(() => {
loading.value = false
})
}
</script>
<template>
<div v-if="!modify" class="flex items-center">
<div class="mr-2">
{{ buffer }}
</div>
<div>
<AButton type="link" size="small" @click="clickModify">
{{ $gettext('Rename') }}
</AButton>
</div>
</div>
<div v-else>
<AInput v-model:value="buffer">
<template #suffix>
<AButton :disabled="buffer === name" type="link" size="small" :loading @click="save">
{{ $gettext('Save') }}
</AButton>
</template>
</AInput>
</div>
</template>
<style scoped lang="less">
</style>

View file

@ -1,120 +0,0 @@
<script setup lang="ts">
import type { Ref } from 'vue'
import stream from '@/api/stream'
import NodeSelector from '@/components/NodeSelector/NodeSelector.vue'
import { InfoCircleOutlined } from '@ant-design/icons-vue'
import { Modal, notification } from 'ant-design-vue'
const node_map = reactive({})
const target = ref([])
const overwrite = ref(false)
const enabled = ref(false)
const name = inject('name') as Ref<string>
const [modal, ContextHolder] = Modal.useModal()
function deploy() {
modal.confirm({
title: () => $ngettext('Do you want to deploy this file to remote server?', 'Do you want to deploy this file to remote servers?', target.value.length),
mask: false,
centered: true,
okText: $gettext('OK'),
cancelText: $gettext('Cancel'),
onOk() {
target.value.forEach(id => {
const node_name = node_map[id]
// get source content
stream.get(name.value).then(r => {
stream.save(name.value, {
name: name.value,
content: r.config,
overwrite: overwrite.value,
}, { headers: { 'X-Node-ID': id } }).then(async () => {
notification.success({
message: $gettext('Deploy successfully'),
description:
$gettext('Deploy %{conf_name} to %{node_name} successfully', { conf_name: name.value, node_name }),
})
if (enabled.value) {
stream.enable(name.value).then(() => {
notification.success({
message: $gettext('Enable successfully'),
description:
$gettext('Enable %{conf_name} in %{node_name} successfully', { conf_name: name.value, node_name }),
})
}).catch(e => {
notification.error({
message: $gettext('Enable %{conf_name} in %{node_name} failed', {
conf_name: name.value,
node_name,
}),
description: $gettext(e?.message ?? 'Server error'),
})
})
}
}).catch(e => {
notification.error({
message: $gettext('Deploy %{conf_name} to %{node_name} failed', {
conf_name: name.value,
node_name,
}),
description: $gettext(e?.message ?? 'Server error'),
})
})
})
})
},
})
}
</script>
<template>
<ContextHolder />
<NodeSelector
v-model:target="target"
hidden-local
:map="node_map"
/>
<div class="node-deploy-control">
<ACheckbox v-model:checked="enabled">
{{ $gettext('Enable') }}
</ACheckbox>
<div class="overwrite">
<ACheckbox v-model:checked="overwrite">
{{ $gettext('Overwrite') }}
</ACheckbox>
<ATooltip placement="bottom">
<template #title>
{{ $gettext('Overwrite exist file') }}
</template>
<InfoCircleOutlined />
</ATooltip>
</div>
<AButton
:disabled="target.length === 0"
type="primary"
ghost
@click="deploy"
>
{{ $gettext('Deploy') }}
</AButton>
</div>
</template>
<style scoped lang="less">
.overwrite {
margin-right: 15px;
span {
color: #9b9b9b;
}
}
.node-deploy-control {
display: flex;
justify-content: flex-end;
margin-top: 10px;
align-items: center;
}
</style>

View file

@ -5,18 +5,18 @@ import type { CheckedType } from '@/types'
import type { Ref } from 'vue'
import stream from '@/api/stream'
import ChatGPT from '@/components/ChatGPT/ChatGPT.vue'
import NodeSelector from '@/components/NodeSelector/NodeSelector.vue'
import { formatDateTime } from '@/lib/helper'
import { useSettingsStore } from '@/pinia'
import Deploy from '@/views/stream/components/Deploy.vue'
import { message, Modal } from 'ant-design-vue'
import ConfigName from './ConfigName.vue'
const settings = useSettingsStore()
const configText = inject('configText') as Ref<string>
const enabled = inject('enabled') as Ref<boolean>
const name = inject('name') as Ref<string>
const history_chatgpt_record = inject('history_chatgpt_record') as Ref<ChatComplicationMessage[]>
const filename = inject('filename') as Ref<string>
const historyChatgptRecord = inject('history_chatgpt_record') as Ref<ChatComplicationMessage[]>
const filepath = inject('filepath') as Ref<string>
const data = inject('data') as Ref<Stream>
@ -42,7 +42,7 @@ function disable() {
})
}
function on_change_enabled(checked: CheckedType) {
function onChangeEnabled(checked: CheckedType) {
modal.confirm({
title: checked ? $gettext('Do you want to enable this stream?') : $gettext('Do you want to disable this stream?'),
mask: false,
@ -76,11 +76,11 @@ function on_change_enabled(checked: CheckedType) {
<AFormItem :label="$gettext('Enabled')">
<ASwitch
:checked="enabled"
@change="on_change_enabled"
@change="onChangeEnabled"
/>
</AFormItem>
<AFormItem :label="$gettext('Name')">
<AInput v-model:value="filename" />
<ConfigName :name="name" />
</AFormItem>
<AFormItem :label="$gettext('Updated at')">
{{ formatDateTime(data.modified_at) }}
@ -89,16 +89,20 @@ function on_change_enabled(checked: CheckedType) {
<ACollapsePanel
v-if="!settings.is_remote"
key="2"
:header="$gettext('Deploy')"
:header="$gettext('Sync')"
>
<Deploy />
<NodeSelector
v-model:target="data.sync_node_ids"
class="mb-4"
hidden-local
/>
</ACollapsePanel>
<ACollapsePanel
key="3"
header="ChatGPT"
>
<ChatGPT
v-model:history-messages="history_chatgpt_record"
v-model:history-messages="historyChatgptRecord"
:content="configText"
:path="filepath"
/>

View file

@ -1,35 +1,22 @@
<script setup lang="ts">
import stream from '@/api/stream'
import NodeSelector from '@/components/NodeSelector/NodeSelector.vue'
import gettext from '@/gettext'
import { useSettingsStore } from '@/pinia'
import { Form, message, notification } from 'ant-design-vue'
import { Form, message } from 'ant-design-vue'
const props = defineProps<{
visible: boolean
name: string
}>()
const emit = defineEmits(['update:visible', 'duplicated'])
const emit = defineEmits(['duplicated'])
const settings = useSettingsStore()
const show = computed({
get() {
return props.visible
},
set(v) {
emit('update:visible', v)
},
})
const visible = defineModel<boolean>('visible')
interface Model {
name: string // site name
target: number[] // ids of deploy targets
}
const modelRef: Model = reactive({ name: '', target: [] })
const modelRef: Model = reactive({ name: '' })
const rulesRef = reactive({
name: [
@ -39,73 +26,29 @@ const rulesRef = reactive({
+ 'this will be used as the filename of the new configuration!'),
},
],
target: [
{
required: true,
message: () => $gettext('Please select at least one node!'),
},
],
})
const { validate, validateInfos, clearValidate } = Form.useForm(modelRef, rulesRef)
const loading = ref(false)
const node_map: Record<number, string> = reactive({})
function onSubmit() {
validate().then(async () => {
loading.value = true
modelRef.target.forEach(id => {
if (id === 0) {
stream.duplicate(props.name, { name: modelRef.name }).then(() => {
message.success($gettext('Duplicate to local successfully'))
show.value = false
visible.value = false
emit('duplicated')
}).catch(e => {
message.error($gettext(e?.message ?? 'Server error'))
})
}
else {
// get source content
stream.get(props.name).then(r => {
stream.save(modelRef.name, {
name: modelRef.name,
content: r.config,
}, { headers: { 'X-Node-ID': id } }).then(() => {
notification.success({
message: $gettext('Duplicate successfully'),
description:
$gettext('Duplicate %{conf_name} to %{node_name} successfully', { conf_name: props.name, node_name: node_map[id] }),
})
}).catch(e => {
notification.error({
message: $gettext('Duplicate failed'),
description: $gettext(e?.message ?? 'Server error'),
})
})
if (r.enabled) {
stream.enable(modelRef.name, { headers: { 'X-Node-ID': id } }).then(() => {
notification.success({
message: $gettext('Enabled successfully'),
})
})
}
})
}
})
}).finally(() => {
loading.value = false
})
})
}
watch(() => props.visible, v => {
watch(visible, v => {
if (v) {
modelRef.name = props.name // default with source name
modelRef.target = [0]
nextTick(() => clearValidate())
}
})
@ -117,7 +60,7 @@ watch(() => gettext.current, () => {
<template>
<AModal
v-model:open="show"
v-model:open="visible"
:title="$gettext('Duplicate')"
:confirm-loading="loading"
:mask="false"
@ -130,16 +73,6 @@ watch(() => gettext.current, () => {
>
<AInput v-model:value="modelRef.name" />
</AFormItem>
<AFormItem
v-if="!settings.is_remote"
:label="$gettext('Target')"
v-bind="validateInfos.target"
>
<NodeSelector
v-model:target="modelRef.target"
v-model:map="node_map"
/>
</AFormItem>
</AForm>
</AModal>
</template>

View file

@ -18,7 +18,7 @@ func main() {
// specify the output directory (default: "./query")
// ### if you want to query without context constrain, set mode gen.WithoutContext ###
g := gen.NewGenerator(gen.Config{
OutPath: "../../query",
OutPath: "query",
Mode: gen.WithoutContext | gen.WithDefaultQuery,
//if you want the nullable field generation property to be pointer type, set FieldNullable true
FieldNullable: true,

1
go.mod
View file

@ -100,7 +100,6 @@ require (
github.com/civo/civogo v0.3.94 // indirect
github.com/cloudflare/cloudflare-go v0.115.0 // indirect
github.com/cloudwego/base64x v0.1.5 // indirect
github.com/cpu/goacmedns v0.1.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect

117
go.sum
View file

@ -39,8 +39,6 @@ cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRY
cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM=
cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I=
cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY=
cloud.google.com/go v0.118.2 h1:bKXO7RXMFDkniAAvvuMrAPtQ/VHrs9e7J5UT3yrGdTY=
cloud.google.com/go v0.118.2/go.mod h1:CFO4UPEPi8oV21xoezZCrd3d81K4fFkDTEJu4R8K+9M=
cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4=
cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw=
cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E=
@ -180,8 +178,6 @@ cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvj
cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA=
cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs=
cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU=
cloud.google.com/go/compute v1.33.0 h1:abGcwWokP7/bBpvRjUKlgchrZXYgRpwcKZIlNUHWf6Y=
cloud.google.com/go/compute v1.33.0/go.mod h1:Z8NErRhrWA3RmVWczlAPJjZcRTlqZB1pcpD0MaIc1ug=
cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU=
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
@ -618,13 +614,10 @@ github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 h1:1mvYtZfWQAnwNah/C+Z+Jb9rQH95LPE2vlmMuWAHJk8=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1/go.mod h1:75I/mXtme1JyWFtz8GocPHVFyH421IBoZErnO16dd0k=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 h1:F0gBpfdPLGsw+nsgk6aqqkZS1jiixa5WwFe3fk/T3Ys=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1 h1:Bk5uOhSAenHyR5P61D/NzeQCv+4fEVV8mOkJ82NqpWw=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1/go.mod h1:QZ4pw3or1WPmRBxf0cHd1tknzrT54WPBOQoGutCPvSU=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 h1:lpOxwrQ919lCZoNCd69rVt8u1eLZuMORrGXqy8sNf3c=
@ -667,8 +660,6 @@ github.com/Azure/go-autorest/tracing v0.6.1 h1:YUMSrC/CeD1ZnnXcNYU4a/fzsO35u2Fsf
github.com/Azure/go-autorest/tracing v0.6.1/go.mod h1:/3EgjbsjraOqiicERAeu3m7/z0x1TzjQGAwDrJrXGkc=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 h1:H5xDQaE3XowWfhZRUpnfC+rGZMEVoSiji+b+/HFAPU4=
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.0 h1:MUkXAnvvDHgvPItl0nBj0hgk0f7hnnQbGm0h0+YxbN4=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.0/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@ -705,8 +696,6 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.84 h1:8IpC2i1mtsuUt13cbZtVCtQRSjzuMvLiDrbOJcaS+Z4=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.84/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.88 h1:87jNTxliGqU2yB3H09xCd4U3cZCmR4AkOMqWgaluo5Q=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.88/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
@ -723,66 +712,36 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
github.com/aws/aws-sdk-go-v2 v1.36.0 h1:b1wM5CcE65Ujwn565qcwgtOTT1aT4ADOHHgglKjG7fk=
github.com/aws/aws-sdk-go-v2 v1.36.0/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM=
github.com/aws/aws-sdk-go-v2 v1.36.2 h1:Ub6I4lq/71+tPb/atswvToaLGVMxKZvjYDVOWEExOcU=
github.com/aws/aws-sdk-go-v2 v1.36.2/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
github.com/aws/aws-sdk-go-v2/config v1.29.5 h1:4lS2IB+wwkj5J43Tq/AwvnscBerBJtQQ6YS7puzCI1k=
github.com/aws/aws-sdk-go-v2/config v1.29.5/go.mod h1:SNzldMlDVbN6nWxM7XsUiNXPSa1LWlqiXtvh/1PrJGg=
github.com/aws/aws-sdk-go-v2/config v1.29.7 h1:71nqi6gUbAUiEQkypHQcNVSFJVUFANpSeUNShiwWX2M=
github.com/aws/aws-sdk-go-v2/config v1.29.7/go.mod h1:yqJQ3nh2HWw/uxd56bicyvmDW4KSc+4wN6lL8pYjynU=
github.com/aws/aws-sdk-go-v2/credentials v1.17.58 h1:/d7FUpAPU8Lf2KUdjniQvfNdlMID0Sd9pS23FJ3SS9Y=
github.com/aws/aws-sdk-go-v2/credentials v1.17.58/go.mod h1:aVYW33Ow10CyMQGFgC0ptMRIqJWvJ4nxZb0sUiuQT/A=
github.com/aws/aws-sdk-go-v2/credentials v1.17.60 h1:1dq+ELaT5ogfmqtV1eocq8SpOK1NRsuUfmhQtD/XAh4=
github.com/aws/aws-sdk-go-v2/credentials v1.17.60/go.mod h1:HDes+fn/xo9VeszXqjBVkxOo/aUy8Mc6QqKvZk32GlE=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 h1:7lOW8NUwE9UZekS1DYoiPdVAqZ6A+LheHWb+mHbNOq8=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27/go.mod h1:w1BASFIPOPUae7AgaH4SbjNbfdkxuggLyGfNFTn8ITY=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.29 h1:JO8pydejFKmGcUNiiwt75dzLHRWthkwApIvPoyUtXEg=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.29/go.mod h1:adxZ9i9DRmB8zAT0pO0yGnsmu0geomp5a3uq5XpgOJ8=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 h1:lWm9ucLSRFiI4dQQafLrEOmEDGry3Swrz0BIRdiHJqQ=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31/go.mod h1:Huu6GG0YTfbPphQkDSo4dEGmQRTKb9k9G7RdtyQWxuI=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.33 h1:knLyPMw3r3JsU8MFHWctE4/e2qWbPaxDYLlohPvnY8c=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.33/go.mod h1:EBp2HQ3f+XCB+5J+IoEbGhoV7CpJbnrsd4asNXmTL0A=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 h1:ACxDklUKKXb48+eg5ROZXi1vDgfMyfIA/WyvqHcHI0o=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31/go.mod h1:yadnfsDwqXeVaohbGc/RaD287PuyRw2wugkh5ZL2J6k=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.33 h1:K0+Ne08zqti8J9jwENxZ5NoUyBnaFDTu3apwQJWrwwA=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.33/go.mod h1:K97stwwzaWzmqxO8yLGHhClbVW1tC6VT1pDLk1pGrq4=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 h1:O+8vD2rGjfihBewr5bT+QUfYUHIxCVgG61LHoT59shM=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12/go.mod h1:usVdWJaosa66NMvmCrr08NcWDBRv4E6+YFG2pUdw1Lk=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.14 h1:2scbY6//jy/s8+5vGrk7l1+UtHl0h9A4MjOO2k/TM2E=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.14/go.mod h1:bRpZPHZpSe5YRHmPfK3h1M7UBFCn2szHzyx0rw04zro=
github.com/aws/aws-sdk-go-v2/service/lightsail v1.42.14 h1:GH3vnPsdH2sTkZRBPnAeMqwkJXdwPNrEh9nI+DEdD0o=
github.com/aws/aws-sdk-go-v2/service/lightsail v1.42.14/go.mod h1:fHFrxpH3kA2iK2NBg/jj3jxgVVfNS7WaKYC5axxr/PY=
github.com/aws/aws-sdk-go-v2/service/lightsail v1.42.16 h1:Wg+SyAmJFupMcHW9CHn2QK0M5nksu8JeXWVJIRVL8Nk=
github.com/aws/aws-sdk-go-v2/service/lightsail v1.42.16/go.mod h1:t2tzigPR3e5R46iVnpIQrfVbA9AIuy5VLYqyk3gffjg=
github.com/aws/aws-sdk-go-v2/service/route53 v1.48.6 h1:O7L9iEodiF07vJoXShMrw2XyeAqZhLUIXqWEitCq6EE=
github.com/aws/aws-sdk-go-v2/service/route53 v1.48.6/go.mod h1:E93uWfli9RToQzVA7+bYnynKOFcYOhNWqhY1hWSMZRc=
github.com/aws/aws-sdk-go-v2/service/route53 v1.48.8 h1:abeu0IVRqYXSts7Tl1Yoi/BxC59xdXYX0uVSN0fbPOk=
github.com/aws/aws-sdk-go-v2/service/route53 v1.48.8/go.mod h1:bOsuAIYHQbL+AqCldJ286MeljQL1sjUVGlpz9JMxCRM=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 h1:c5WJ3iHz7rLIgArznb3JCSQT3uUMiz9DLZhIX+1G8ok=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.14/go.mod h1:+JJQTxB6N4niArC14YNtxcQtwEqzS3o9Z32n7q33Rfs=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.16 h1:YV6xIKDJp6U7YB2bxfud9IENO1LRpGhe2Tv/OKtPrOQ=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.16/go.mod h1:DvbmMKgtpA6OihFJK13gHMZOZrCHttz8wPHGKXqU+3o=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 h1:f1L/JtUkVODD+k1+IiSJUUv8A++2qVr+Xvb3xWXETMU=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13/go.mod h1:tvqlFoja8/s0o+UruA1Nrezo/df0PzdunMDDurUfg6U=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.15 h1:kMyK3aKotq1aTBsj1eS8ERJLjqYRRRcsmP33ozlCvlk=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.15/go.mod h1:5uPZU7vSNzb8Y0dm75xTikinegPYK3uJmIHQZFq5Aqo=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.13 h1:3LXNnmtH3TURctC23hnC0p/39Q5gre3FI7BNOiDcVWc=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.13/go.mod h1:7Yn+p66q/jt38qMoVfNvjbm3D89mGBnkwDcijgtih8w=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.15 h1:ht1jVmeeo2anR7zDiYJLSnRYnO/9NILXXu42FP3rJg0=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.15/go.mod h1:xWZ5cOiFe3czngChE4LhCBqUxNwgfwndEF7XlYP/yD8=
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ=
github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k=
github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
@ -804,8 +763,6 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/bsm/redislock v0.9.4 h1:X/Wse1DPpiQgHbVYRE9zv6m070UcKoOGekgvpNhiSvw=
github.com/bsm/redislock v0.9.4/go.mod h1:Epf7AJLiSFwLCiZcfi6pWFO/8eAYrYpQXFxEDPoDeAk=
github.com/bytedance/sonic v1.12.8 h1:4xYRVRlXIgvSZ4e8iVTlMF5szgpXd4AfvuWgA8I8lgs=
github.com/bytedance/sonic v1.12.8/go.mod h1:uVvFidNmlt9+wa31S1urfwwthTWteBgG0hWuoKAXTx8=
github.com/bytedance/sonic v1.12.9 h1:Od1BvK55NnewtGaJsTDeAOSnLVO2BTSLOe0+ooKokmQ=
github.com/bytedance/sonic v1.12.9/go.mod h1:uVvFidNmlt9+wa31S1urfwwthTWteBgG0hWuoKAXTx8=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
@ -816,8 +773,6 @@ github.com/c-bata/go-prompt v0.2.6/go.mod h1:/LMAke8wD2FsNu9EXNdHxNLbd9MedkPnCdf
github.com/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA=
github.com/caarlos0/env/v11 v11.3.1/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U=
github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
github.com/casdoor/casdoor-go-sdk v1.3.0 h1:iUZKsrNUkhtAoyitFIFw3e6TchctAdoxmVgLDtNAgpc=
github.com/casdoor/casdoor-go-sdk v1.3.0/go.mod h1:cMnkCQJgMYpgAlgEx8reSt1AVaDIQLcJ1zk5pzBaz+4=
github.com/casdoor/casdoor-go-sdk v1.4.0 h1:EhnIcMeCPiDE66tedy6EISkVjndR78slnwXqTfUnyhU=
github.com/casdoor/casdoor-go-sdk v1.4.0/go.mod h1:cMnkCQJgMYpgAlgEx8reSt1AVaDIQLcJ1zk5pzBaz+4=
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
@ -827,7 +782,6 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@ -839,8 +793,6 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/civo/civogo v0.3.93 h1:wxzMamDKYu2lszObvx92tTFDpi0sCJbDO+CL3cR/P28=
github.com/civo/civogo v0.3.93/go.mod h1:7UCYX+qeeJbrG55E1huv+0ySxcHTqq/26FcHLVelQJM=
github.com/civo/civogo v0.3.94 h1:VhdqaJ2m4z8Jz8arzyzVjokRnO8JQ3lGjLKLshJ1eJI=
github.com/civo/civogo v0.3.94/go.mod h1:LaEbkszc+9nXSh4YNG0sYXFGYqdQFmXXzQg0gESs2hc=
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
@ -871,8 +823,6 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpu/goacmedns v0.1.1 h1:DM3H2NiN2oam7QljgGY5ygy4yDXhK5Z4JUnqaugs2C4=
github.com/cpu/goacmedns v0.1.1/go.mod h1:MuaouqEhPAHxsbqjgnck5zeghuwBP1dLnPoobeGqugQ=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
@ -965,8 +915,6 @@ github.com/gin-contrib/static v1.1.3 h1:WLOpkBtMDJ3gATFZgNJyVibFMio/UHonnueqJsQ0
github.com/gin-contrib/static v1.1.3/go.mod h1:zejpJ/YWp8cZj/6EpiL5f/+skv5daQTNwRx1E8Pci30=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-acme/lego/v4 v4.21.0 h1:arEW+8o5p7VI8Bk1kr/PDlgD1DrxtTH1gJ4b7mehL8o=
github.com/go-acme/lego/v4 v4.21.0/go.mod h1:HrSWzm3Ckj45Ie3i+p1zKVobbQoMOaGu9m4up0dUeDI=
github.com/go-acme/lego/v4 v4.22.2 h1:ck+HllWrV/rZGeYohsKQ5iKNnU/WAZxwOdiu6cxky+0=
github.com/go-acme/lego/v4 v4.22.2/go.mod h1:E2FndyI3Ekv0usNJt46mFb9LVpV/XBYT+4E3tz02Tzo=
github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s=
@ -1016,15 +964,11 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg=
github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8=
github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-sql-driver/mysql v1.9.0 h1:Y0zIbQXhQKmQgTp44Y1dp3wTXcn804QoTptLZT1vtvo=
github.com/go-sql-driver/mysql v1.9.0/go.mod h1:pDetrLJeA3oMujJuvXc8RJoasr589B6A9fwzD3QMrqw=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
@ -1034,12 +978,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-webauthn/webauthn v0.11.2 h1:Fgx0/wlmkClTKlnOsdOQ+K5HcHDsDcYIvtYmfhEOSUc=
github.com/go-webauthn/webauthn v0.11.2/go.mod h1:aOtudaF94pM71g3jRwTYYwQTG1KyTILTcZqN1srkmD0=
github.com/go-webauthn/webauthn v0.12.1 h1:fQNKWc+gd7i1TW8FmlB0jQTHyc2GYYlV/QdLUxo+MSA=
github.com/go-webauthn/webauthn v0.12.1/go.mod h1:Q13xKHZi459wU8IoFjm8jQ6CMRyad+kegblwMFFhQGU=
github.com/go-webauthn/x v0.1.16 h1:EaVXZntpyHviN9ykjdRBQIw9B0Ed3LO5FW7mDiMQEa8=
github.com/go-webauthn/x v0.1.16/go.mod h1:jhYjfwe/AVYaUs2mUXArj7vvZj+SpooQPyyQGNab+Us=
github.com/go-webauthn/x v0.1.18 h1:9xxiKRKCHx/1R2RF+4xb1qY5QDIO0RlTmH5L02lmRH4=
github.com/go-webauthn/x v0.1.18/go.mod h1:Q/uHdGGFrZ7psEcoEStYunurZuG3Z9UDZJetM8qDTtA=
github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
@ -1275,8 +1215,6 @@ github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKEN
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.134 h1:s0IUKcV6UCu84UZMKejNiUnJ4l2Jw9HM0IxHvdJCg9A=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.134/go.mod h1:JWz2ujO9X3oU5wb6kXp+DpR2UuDj2SldDbX8T0FSuhI=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.136 h1:T785NUg5245nWpPVHLVR8lBd+zGQYR14Vi/TCX1iu3A=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.136/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY=
github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=
@ -1595,12 +1533,8 @@ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYr
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE=
github.com/oracle/oci-go-sdk/v65 v65.83.0 h1:KFI0oyyCTPmgevHF+QlN02Zdf23Jx1p1X+4KPyH14H8=
github.com/oracle/oci-go-sdk/v65 v65.83.0/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0=
github.com/oracle/oci-go-sdk/v65 v65.83.2 h1:4DtSCVe/AaHcqb08wXgjplOM8+tc4pqNwcUYZmplbv8=
github.com/oracle/oci-go-sdk/v65 v65.83.2/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0=
github.com/ovh/go-ovh v1.6.0 h1:ixLOwxQdzYDx296sXcgS35TOPEahJkpjMGtzPadCjQI=
github.com/ovh/go-ovh v1.6.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c=
github.com/ovh/go-ovh v1.7.0 h1:V14nF7FwDjQrZt9g7jzcvAAQ3HN6DNShRFRMC3jLoPw=
github.com/ovh/go-ovh v1.7.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
@ -1714,8 +1648,6 @@ github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6g
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
github.com/sashabaranov/go-openai v1.36.1 h1:EVfRXwIlW2rUzpx6vR+aeIKCK/xylSrVYAx1TMTSX3g=
github.com/sashabaranov/go-openai v1.36.1/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
github.com/sashabaranov/go-openai v1.37.0 h1:hQQowgYm4OXJ1Z/wTrE+XZaO20BYsL0R3uRPSpfNZkY=
github.com/sashabaranov/go-openai v1.37.0/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
@ -1817,12 +1749,8 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1091 h1:RxogX8ZCPBmZ6PY7DjnWnwGRkAkYEEinT5WNNxbLVeo=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1091/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1101 h1:pz6QIjHR7TXQfEogg4pwvvTDgsB1L+RQGgnr2tBDzc4=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1101/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1091 h1:36WwNgrtoGKszQovUj3+0CjNsM1gMQ3a5lvZx2bkjng=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1091/go.mod h1:cWRGvOUnMQMky4oliMX1dXT6Z4CbsSGOIxaUcrD5Zvw=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1101 h1:9c05Ky7Ppww06YFE579TjI89pfNnC2zdJufx7SXUTi8=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1101/go.mod h1:fBdcH58lmwIwePei24b9QFdE1w8+brIX9yTrf82n7yM=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
@ -1847,8 +1775,6 @@ github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65E
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec h1:2s/ghQ8wKE+UzD/hf3P4Gd1j0JI9ncbxv+nsypPoUYI=
github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec/go.mod h1:BZr7Qs3ku1ckpqed8tCRSqTlp8NAeZfAVpfx4OzXMss=
github.com/uozi-tech/cosy v1.14.3 h1:YDleGHghw5Dtd8H7Fy0Iq0caXfxmhk7Zt6tJBONjq5Q=
github.com/uozi-tech/cosy v1.14.3/go.mod h1:DSKLtoVaGLUlJ8KiQ1vWEsnv85epRrAAMXSijuq+asM=
github.com/uozi-tech/cosy v1.14.4 h1:9X9CzxYjTg9DRQKgBjYvDNOAYYFclOXYYq518nO4vr0=
github.com/uozi-tech/cosy v1.14.4/go.mod h1:DSKLtoVaGLUlJ8KiQ1vWEsnv85epRrAAMXSijuq+asM=
github.com/uozi-tech/cosy-driver-mysql v0.2.2 h1:22S/XNIvuaKGqxQPsYPXN8TZ8hHjCQdcJKVQ83Vzxoo=
@ -1862,8 +1788,6 @@ github.com/urfave/cli/v3 v3.0.0-beta1 h1:6DTaaUarcM0wX7qj5Hcvs+5Dm3dyUTBbEwIWAjc
github.com/urfave/cli/v3 v3.0.0-beta1/go.mod h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y=
github.com/vinyldns/go-vinyldns v0.9.16 h1:GZJStDkcCk1F1AcRc64LuuMh+ENL8pHA0CVd4ulRMcQ=
github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q=
github.com/volcengine/volc-sdk-golang v1.0.194 h1:3o0INQzdtYJWvdGrtX02booCqPL5TsWSq2W1Ur7Bzlo=
github.com/volcengine/volc-sdk-golang v1.0.194/go.mod h1:u0VtPvlXWpXDTmc9IHkaW1q+5Jjwus4oAqRhNMDRInE=
github.com/volcengine/volc-sdk-golang v1.0.195 h1:hKX4pBhmKcB3652BTdcAmtgizEPBnoQUpTM+j5blMA4=
github.com/volcengine/volc-sdk-golang v1.0.195/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ=
github.com/vultr/govultr/v3 v3.14.1 h1:9BpyZgsWasuNoR39YVMcq44MSaF576Z4D+U3ro58eJQ=
@ -1879,12 +1803,8 @@ github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yandex-cloud/go-genproto v0.0.0-20250203115010-0bcba64c41f6 h1:CHYGew+KO1JaK5sx/N2ApgVCTGCKvfSl0sSPplTyCog=
github.com/yandex-cloud/go-genproto v0.0.0-20250203115010-0bcba64c41f6/go.mod h1:0LDD/IZLIUIV4iPH+YcF+jysO3jkSvADFGm4dCAuwQo=
github.com/yandex-cloud/go-genproto v0.0.0-20250217111757-ccaea642a16c h1:WTK2XiEf68Uv0rT6mjrB5hKkwZvMnWWHPF3OjK/fYL8=
github.com/yandex-cloud/go-genproto v0.0.0-20250217111757-ccaea642a16c/go.mod h1:0LDD/IZLIUIV4iPH+YcF+jysO3jkSvADFGm4dCAuwQo=
github.com/yandex-cloud/go-sdk v0.0.0-20250203123950-24786ecffd92 h1:UTcY1921ZXBABB5JpSWxccyZaCjMuSbniyifW6nmLZ4=
github.com/yandex-cloud/go-sdk v0.0.0-20250203123950-24786ecffd92/go.mod h1:MQm5WxsYpQRdUklz2C8q3uDhuIaDzzV7seLErAILLBE=
github.com/yandex-cloud/go-sdk v0.0.0-20250210144447-399a857b9c4e h1:RiNKkceZPeMWLSIl31RSgPeSmpT9K7eTXOcA9YxTBfg=
github.com/yandex-cloud/go-sdk v0.0.0-20250210144447-399a857b9c4e/go.mod h1:OCW2kKPZ900GNQ9aKDaX7/FUQmxGdm+CKeXVocbM4d0=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
@ -1911,7 +1831,6 @@ go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsX
go.etcd.io/etcd/client/v2 v2.305.7/go.mod h1:GQGT5Z3TBuAQGvgPfhR7VPySu/SudxmEkRq9BgzFU6s=
go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0=
go.etcd.io/etcd/client/v3 v3.5.9/go.mod h1:i/Eo5LrZ5IKqpbtpPDuaUnDOUv471oDg8cjQaUr2MbA=
go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0=
go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo=
go.mongodb.org/mongo-driver v1.17.2 h1:gvZyk8352qSfzyZ2UMWcpDpMSGEr1eqE4T793SqyhzM=
go.mongodb.org/mongo-driver v1.17.2/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
@ -2009,11 +1928,6 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/crypto v0.34.0 h1:+/C6tk6rf/+t5DhUketUbD1aNGqiSX3j15Z6xuIDlBA=
golang.org/x/crypto v0.34.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -2031,10 +1945,6 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c h1:KL/ZBHXgKGVmuZBZ01Lt57yE5ws8ZPSkkihmEyq7FXc=
golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3 h1:qNgPs5exUA+G0C96DrPwNrvLSj7GT/9D+3WMWUcUg34=
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4=
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
@ -2160,8 +2070,6 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -2334,7 +2242,6 @@ golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
@ -2352,9 +2259,8 @@ golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -2374,7 +2280,6 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -2461,8 +2366,6 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -2541,8 +2444,6 @@ google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60c
google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0=
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms=
google.golang.org/api v0.219.0 h1:nnKIvxKs/06jWawp2liznTBnMRQBEPpGo7I+oEypTX0=
google.golang.org/api v0.219.0/go.mod h1:K6OmjGm+NtLrIkHxv1U3a0qIf/0JOvAHd5O/6AoyKYE=
google.golang.org/api v0.221.0 h1:qzaJfLhDsbMeFee8zBRdt/Nc+xmOuafD/dbdgGfutOU=
google.golang.org/api v0.221.0/go.mod h1:7sOU2+TL4TxUTdbi0gWgAIg7tH5qBXxoyhtL+9x3biQ=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
@ -2685,16 +2586,10 @@ google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOl
google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
google.golang.org/genproto v0.0.0-20250204164813-702378808489 h1:nQcbCCOg2h2CQ0yA8SY3AHqriNKDvsetuq9mE/HFjtc=
google.golang.org/genproto v0.0.0-20250204164813-702378808489/go.mod h1:wkQ2Aj/xvshAUDtO/JHvu9y+AaN9cqs28QuSVSHtZSY=
google.golang.org/genproto v0.0.0-20250218202821-56aae31c358a h1:Xx6e5r1AOINOgm2ZuzvwDueGlOOml4PKBUry8jqyS6U=
google.golang.org/genproto v0.0.0-20250218202821-56aae31c358a/go.mod h1:Cmg1ztsSOnOsWxOiPTOUX8gegyHg5xADRncIHdtec8U=
google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 h1:fCuMM4fowGzigT89NCIsW57Pk9k2D12MMi2ODn+Nk+o=
google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489/go.mod h1:iYONQfRdizDB8JJBybql13nArx91jcUk7zCXEsOofM4=
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0=
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250204164813-702378808489 h1:5bKytslY8ViY0Cj/ewmRtrWHW64bNF03cAatUUFCdFI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250204164813-702378808489/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@ -2759,8 +2654,6 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
@ -2840,12 +2733,8 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc=
k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k=
k8s.io/api v0.32.2 h1:bZrMLEkgizC24G9eViHGOPbW+aRo9duEISRIJKfdJuw=
k8s.io/api v0.32.2/go.mod h1:hKlhk4x1sJyYnHENsrdCWw31FEmCijNGPJO5WzHiJ6Y=
k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs=
k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/apimachinery v0.32.2 h1:yoQBR9ZGkA6Rgmhbp/yuT9/g+4lxtsGYwW6dR6BDPLQ=
k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=

79
internal/stream/delete.go Normal file
View file

@ -0,0 +1,79 @@
package stream
import (
"fmt"
"net/http"
"os"
"runtime"
"github.com/0xJacky/Nginx-UI/internal/helper"
"github.com/0xJacky/Nginx-UI/internal/nginx"
"github.com/0xJacky/Nginx-UI/internal/notification"
"github.com/0xJacky/Nginx-UI/model"
"github.com/0xJacky/Nginx-UI/query"
"github.com/go-resty/resty/v2"
"github.com/uozi-tech/cosy/logger"
)
// Delete deletes a site by removing the file in sites-available
func Delete(name string) (err error) {
availablePath := nginx.GetConfPath("streams-available", name)
syncDelete(name)
s := query.Site
_, err = s.Where(s.Path.Eq(availablePath)).Unscoped().Delete(&model.Site{})
if err != nil {
return
}
enabledPath := nginx.GetConfPath("streams-enabled", name)
if !helper.FileExists(availablePath) {
return ErrStreamNotFound
}
if helper.FileExists(enabledPath) {
return ErrStreamIsEnabled
}
certModel := model.Cert{Filename: name}
_ = certModel.Remove()
err = os.Remove(availablePath)
if err != nil {
return
}
return
}
func syncDelete(name string) {
nodes := getSyncNodes(name)
for _, node := range nodes {
go func() {
defer func() {
if err := recover(); err != nil {
buf := make([]byte, 1024)
runtime.Stack(buf, false)
logger.Error(err)
}
}()
client := resty.New()
client.SetBaseURL(node.URL)
resp, err := client.R().
SetHeader("X-Node-Secret", node.Token).
Delete(fmt.Sprintf("/api/streams/%s", name))
if err != nil {
notification.Error("Delete Remote Stream Error", err.Error())
return
}
if resp.StatusCode() != http.StatusOK {
notification.Error("Delete Remote Stream Error", NewSyncResult(node.Name, name, resp).String())
return
}
notification.Success("Delete Remote Stream Success", NewSyncResult(node.Name, name, resp).String())
}()
}
}

View file

@ -0,0 +1,81 @@
package stream
import (
"fmt"
"github.com/0xJacky/Nginx-UI/internal/nginx"
"github.com/0xJacky/Nginx-UI/internal/notification"
"github.com/0xJacky/Nginx-UI/model"
"github.com/go-resty/resty/v2"
"github.com/uozi-tech/cosy/logger"
"net/http"
"os"
"runtime"
"sync"
)
// Disable disables a site by removing the symlink in sites-enabled
func Disable(name string) (err error) {
enabledConfigFilePath := nginx.GetConfPath("streams-enabled", name)
_, err = os.Stat(enabledConfigFilePath)
if err != nil {
return
}
err = os.Remove(enabledConfigFilePath)
if err != nil {
return
}
// delete auto cert record
certModel := model.Cert{Filename: name}
err = certModel.Remove()
if err != nil {
return
}
output := nginx.Reload()
if nginx.GetLogLevel(output) > nginx.Warn {
return fmt.Errorf("%s", output)
}
go syncDisable(name)
return
}
func syncDisable(name string) {
nodes := getSyncNodes(name)
wg := &sync.WaitGroup{}
wg.Add(len(nodes))
for _, node := range nodes {
go func() {
defer func() {
if err := recover(); err != nil {
buf := make([]byte, 1024)
runtime.Stack(buf, false)
logger.Error(err)
}
}()
defer wg.Done()
client := resty.New()
client.SetBaseURL(node.URL)
resp, err := client.R().
SetHeader("X-Node-Secret", node.Token).
Post(fmt.Sprintf("/api/streams/%s/disable", name))
if err != nil {
notification.Error("Disable Remote Stream Error", err.Error())
return
}
if resp.StatusCode() != http.StatusOK {
notification.Error("Disable Remote Stream Error", NewSyncResult(node.Name, name, resp).String())
return
}
notification.Success("Disable Remote Stream Success", NewSyncResult(node.Name, name, resp).String())
}()
}
wg.Wait()
}

View file

@ -0,0 +1,23 @@
package stream
import (
"github.com/0xJacky/Nginx-UI/internal/helper"
"github.com/0xJacky/Nginx-UI/internal/nginx"
)
// Duplicate duplicates a site by copying the file
func Duplicate(src, dst string) (err error) {
src = nginx.GetConfPath("streams-available", src)
dst = nginx.GetConfPath("streams-available", dst)
if helper.FileExists(dst) {
return ErrDstFileExists
}
_, err = helper.CopyFile(src, dst)
if err != nil {
return
}
return
}

87
internal/stream/enable.go Normal file
View file

@ -0,0 +1,87 @@
package stream
import (
"fmt"
"github.com/0xJacky/Nginx-UI/internal/helper"
"github.com/0xJacky/Nginx-UI/internal/nginx"
"github.com/0xJacky/Nginx-UI/internal/notification"
"github.com/go-resty/resty/v2"
"github.com/uozi-tech/cosy/logger"
"net/http"
"os"
"runtime"
"sync"
)
// Enable enables a site by creating a symlink in sites-enabled
func Enable(name string) (err error) {
configFilePath := nginx.GetConfPath("streams-available", name)
enabledConfigFilePath := nginx.GetConfPath("streams-enabled", name)
_, err = os.Stat(configFilePath)
if err != nil {
return
}
if helper.FileExists(enabledConfigFilePath) {
return
}
err = os.Symlink(configFilePath, enabledConfigFilePath)
if err != nil {
return
}
// Test nginx config, if not pass, then disable the site.
output := nginx.TestConf()
if nginx.GetLogLevel(output) > nginx.Warn {
_ = os.Remove(enabledConfigFilePath)
return fmt.Errorf("%s", output)
}
output = nginx.Reload()
if nginx.GetLogLevel(output) > nginx.Warn {
return fmt.Errorf("%s", output)
}
go syncEnable(name)
return
}
func syncEnable(name string) {
nodes := getSyncNodes(name)
wg := &sync.WaitGroup{}
wg.Add(len(nodes))
for _, node := range nodes {
go func() {
defer func() {
if err := recover(); err != nil {
buf := make([]byte, 1024)
runtime.Stack(buf, false)
logger.Error(err)
}
}()
defer wg.Done()
client := resty.New()
client.SetBaseURL(node.URL)
resp, err := client.R().
SetHeader("X-Node-Secret", node.Token).
Post(fmt.Sprintf("/api/streams/%s/enable", name))
if err != nil {
notification.Error("Enable Remote Stream Error", err.Error())
return
}
if resp.StatusCode() != http.StatusOK {
notification.Error("Enable Remote Stream Error", NewSyncResult(node.Name, name, resp).String())
return
}
notification.Success("Enable Remote Stream Success", NewSyncResult(node.Name, name, resp).String())
}()
}
wg.Wait()
}

10
internal/stream/errors.go Normal file
View file

@ -0,0 +1,10 @@
package stream
import "github.com/uozi-tech/cosy"
var (
e = cosy.NewErrorScope("stream")
ErrStreamNotFound = e.New(40401, "stream not found")
ErrDstFileExists = e.New(50001, "destination file already exists")
ErrStreamIsEnabled = e.New(50002, "stream is enabled")
)

108
internal/stream/rename.go Normal file
View file

@ -0,0 +1,108 @@
package stream
import (
"fmt"
"github.com/0xJacky/Nginx-UI/internal/helper"
"github.com/0xJacky/Nginx-UI/internal/nginx"
"github.com/0xJacky/Nginx-UI/internal/notification"
"github.com/0xJacky/Nginx-UI/query"
"github.com/go-resty/resty/v2"
"github.com/uozi-tech/cosy/logger"
"net/http"
"os"
"runtime"
"sync"
)
func Rename(oldName string, newName string) (err error) {
oldPath := nginx.GetConfPath("streams-available", oldName)
newPath := nginx.GetConfPath("streams-available", newName)
if oldPath == newPath {
return
}
// check if dst file exists, do not rename
if helper.FileExists(newPath) {
return ErrDstFileExists
}
s := query.Site
_, _ = s.Where(s.Path.Eq(oldPath)).Update(s.Path, newPath)
err = os.Rename(oldPath, newPath)
if err != nil {
return
}
// recreate a soft link
oldEnabledConfigFilePath := nginx.GetConfPath("streams-enabled", oldName)
if helper.SymbolLinkExists(oldEnabledConfigFilePath) {
_ = os.Remove(oldEnabledConfigFilePath)
newEnabledConfigFilePath := nginx.GetConfPath("streams-enabled", newName)
err = os.Symlink(newPath, newEnabledConfigFilePath)
if err != nil {
return
}
}
// test nginx configuration
output := nginx.TestConf()
if nginx.GetLogLevel(output) > nginx.Warn {
return fmt.Errorf("%s", output)
}
// reload nginx
output = nginx.Reload()
if nginx.GetLogLevel(output) > nginx.Warn {
return fmt.Errorf("%s", output)
}
go syncRename(oldName, newName)
return
}
func syncRename(oldName, newName string) {
nodes := getSyncNodes(newName)
wg := &sync.WaitGroup{}
wg.Add(len(nodes))
for _, node := range nodes {
go func() {
defer func() {
if err := recover(); err != nil {
buf := make([]byte, 1024)
runtime.Stack(buf, false)
logger.Error(err)
}
}()
defer wg.Done()
client := resty.New()
client.SetBaseURL(node.URL)
resp, err := client.R().
SetHeader("X-Node-Secret", node.Token).
SetBody(map[string]string{
"new_name": newName,
}).
Post(fmt.Sprintf("/api/streams/%s/rename", oldName))
if err != nil {
notification.Error("Rename Remote Stream Error", err.Error())
return
}
if resp.StatusCode() != http.StatusOK {
notification.Error("Rename Remote Stream Error",
NewSyncResult(node.Name, oldName, resp).
SetNewName(newName).String())
return
}
notification.Success("Rename Remote Stream Success",
NewSyncResult(node.Name, oldName, resp).
SetNewName(newName).String())
}()
}
wg.Wait()
}

107
internal/stream/save.go Normal file
View file

@ -0,0 +1,107 @@
package stream
import (
"fmt"
"net/http"
"os"
"runtime"
"sync"
"github.com/0xJacky/Nginx-UI/internal/helper"
"github.com/0xJacky/Nginx-UI/internal/nginx"
"github.com/0xJacky/Nginx-UI/internal/notification"
"github.com/0xJacky/Nginx-UI/model"
"github.com/0xJacky/Nginx-UI/query"
"github.com/go-resty/resty/v2"
"github.com/uozi-tech/cosy/logger"
)
// Save saves a site configuration file
func Save(name string, content string, overwrite bool, syncNodeIds []uint64) (err error) {
path := nginx.GetConfPath("streams-available", name)
if !overwrite && helper.FileExists(path) {
return ErrDstFileExists
}
err = os.WriteFile(path, []byte(content), 0644)
if err != nil {
return
}
enabledConfigFilePath := nginx.GetConfPath("streams-enabled", name)
if helper.FileExists(enabledConfigFilePath) {
// Test nginx configuration
output := nginx.TestConf()
if nginx.GetLogLevel(output) > nginx.Warn {
return fmt.Errorf("%s", output)
}
output = nginx.Reload()
if nginx.GetLogLevel(output) > nginx.Warn {
return fmt.Errorf("%s", output)
}
}
s := query.Stream
_, err = s.Where(s.Path.Eq(path)).
Select(s.SyncNodeIDs).
Updates(&model.Site{
SyncNodeIDs: syncNodeIds,
})
if err != nil {
return
}
go syncSave(name, content)
return
}
func syncSave(name string, content string) {
nodes := getSyncNodes(name)
wg := &sync.WaitGroup{}
wg.Add(len(nodes))
for _, node := range nodes {
go func() {
defer func() {
if err := recover(); err != nil {
buf := make([]byte, 1024)
runtime.Stack(buf, false)
logger.Error(err)
}
}()
defer wg.Done()
client := resty.New()
client.SetBaseURL(node.URL)
resp, err := client.R().
SetHeader("X-Node-Secret", node.Token).
SetBody(map[string]interface{}{
"content": content,
"overwrite": true,
}).
Post(fmt.Sprintf("/api/streams/%s", name))
if err != nil {
notification.Error("Save Remote Stream Error", err.Error())
return
}
if resp.StatusCode() != http.StatusOK {
notification.Error("Save Remote Stream Error", NewSyncResult(node.Name, name, resp).String())
return
}
notification.Success("Save Remote Stream Success", NewSyncResult(node.Name, name, resp).String())
// Check if the site is enabled, if so then enable it on the remote node
enabledConfigFilePath := nginx.GetConfPath("streams-enabled", name)
if helper.FileExists(enabledConfigFilePath) {
syncEnable(name)
}
}()
}
wg.Wait()
}

74
internal/stream/sync.go Normal file
View file

@ -0,0 +1,74 @@
package stream
import (
"encoding/json"
"github.com/0xJacky/Nginx-UI/internal/nginx"
"github.com/0xJacky/Nginx-UI/model"
"github.com/0xJacky/Nginx-UI/query"
"github.com/gin-gonic/gin"
"github.com/go-resty/resty/v2"
"github.com/samber/lo"
"github.com/uozi-tech/cosy/logger"
)
// getSyncNodes returns the nodes that need to be synchronized by site name
func getSyncNodes(name string) (nodes []*model.Environment) {
configFilePath := nginx.GetConfPath("streams-available", name)
s := query.Site
site, err := s.Where(s.Path.Eq(configFilePath)).
Preload(s.SiteCategory).First()
if err != nil {
logger.Error(err)
return
}
syncNodeIds := site.SyncNodeIDs
// inherit sync node ids from site category
if site.SiteCategory != nil {
syncNodeIds = append(syncNodeIds, site.SiteCategory.SyncNodeIds...)
}
syncNodeIds = lo.Uniq(syncNodeIds)
e := query.Environment
nodes, err = e.Where(e.ID.In(syncNodeIds...)).Find()
if err != nil {
logger.Error(err)
return
}
return
}
type SyncResult struct {
StatusCode int `json:"status_code"`
Node string `json:"node"`
Name string `json:"name"`
NewName string `json:"new_name,omitempty"`
Response gin.H `json:"response"`
Error string `json:"error"`
}
func NewSyncResult(node string, siteName string, resp *resty.Response) (s *SyncResult) {
s = &SyncResult{
StatusCode: resp.StatusCode(),
Node: node,
Name: siteName,
}
err := json.Unmarshal(resp.Body(), &s.Response)
if err != nil {
logger.Error(err)
}
return
}
func (s *SyncResult) SetNewName(name string) *SyncResult {
s.NewName = name
return s
}
func (s *SyncResult) String() string {
b, err := json.Marshal(s)
if err != nil {
logger.Error(err)
}
return string(b)
}

View file

@ -4,4 +4,5 @@ type Stream struct {
Model
Path string `json:"path"`
Advanced bool `json:"advanced"`
SyncNodeIDs []uint64 `json:"sync_node_ids" gorm:"serializer:json"`
}

View file

@ -34,6 +34,7 @@ func newStream(db *gorm.DB, opts ...gen.DOOption) stream {
_stream.DeletedAt = field.NewField(tableName, "deleted_at")
_stream.Path = field.NewString(tableName, "path")
_stream.Advanced = field.NewBool(tableName, "advanced")
_stream.SyncNodeIDs = field.NewField(tableName, "sync_node_ids")
_stream.fillFieldMap()
@ -50,6 +51,7 @@ type stream struct {
DeletedAt field.Field
Path field.String
Advanced field.Bool
SyncNodeIDs field.Field
fieldMap map[string]field.Expr
}
@ -72,6 +74,7 @@ func (s *stream) updateTableName(table string) *stream {
s.DeletedAt = field.NewField(table, "deleted_at")
s.Path = field.NewString(table, "path")
s.Advanced = field.NewBool(table, "advanced")
s.SyncNodeIDs = field.NewField(table, "sync_node_ids")
s.fillFieldMap()
@ -88,13 +91,14 @@ func (s *stream) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
}
func (s *stream) fillFieldMap() {
s.fieldMap = make(map[string]field.Expr, 6)
s.fieldMap = make(map[string]field.Expr, 7)
s.fieldMap["id"] = s.ID
s.fieldMap["created_at"] = s.CreatedAt
s.fieldMap["updated_at"] = s.UpdatedAt
s.fieldMap["deleted_at"] = s.DeletedAt
s.fieldMap["path"] = s.Path
s.fieldMap["advanced"] = s.Advanced
s.fieldMap["sync_node_ids"] = s.SyncNodeIDs
}
func (s stream) clone(db *gorm.DB) stream {