wip: ChatGPT assistant

This commit is contained in:
0xJacky 2023-03-20 19:48:43 +08:00
parent ba87f02a53
commit 4cd77f28eb
No known key found for this signature in database
GPG key ID: B6E4A6E4A561BAF0
27 changed files with 3197 additions and 529 deletions

View file

@ -14,3 +14,9 @@ NginxConfigDir =
[nginx_log]
AccessLogPath = /var/log/nginx/access.log
ErrorLogPath = /var/log/nginx/error.log
[openai]
Model =
BaseUrl =
Proxy =
Token =

View file

@ -16,6 +16,7 @@ declare module '@vue/runtime-core' {
ACol: typeof import('ant-design-vue/es')['Col']
ACollapse: typeof import('ant-design-vue/es')['Collapse']
ACollapsePanel: typeof import('ant-design-vue/es')['CollapsePanel']
AComment: typeof import('ant-design-vue/es')['Comment']
AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
ADivider: typeof import('ant-design-vue/es')['Divider']
ADrawer: typeof import('ant-design-vue/es')['Drawer']
@ -55,9 +56,11 @@ declare module '@vue/runtime-core' {
ATabs: typeof import('ant-design-vue/es')['Tabs']
ATag: typeof import('ant-design-vue/es')['Tag']
ATextarea: typeof import('ant-design-vue/es')['Textarea']
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
BreadcrumbBreadcrumb: typeof import('./src/components/Breadcrumb/Breadcrumb.vue')['default']
ChartAreaChart: typeof import('./src/components/Chart/AreaChart.vue')['default']
ChartRadialBarChart: typeof import('./src/components/Chart/RadialBarChart.vue')['default']
ChatGPTChatGPT: typeof import('./src/components/ChatGPT/ChatGPT.vue')['default']
CodeEditorCodeEditor: typeof import('./src/components/CodeEditor/CodeEditor.vue')['default']
FooterToolbarFooterToolBar: typeof import('./src/components/FooterToolbar/FooterToolBar.vue')['default']
LogoLogo: typeof import('./src/components/Logo/Logo.vue')['default']

View file

@ -20,6 +20,7 @@
"apexcharts": "^3.36.3",
"axios": "^1.2.2",
"dayjs": "^1.11.7",
"highlight.js": "^11.7.0",
"marked": "^4.2.5",
"nprogress": "^0.2.0",
"pinia": "^2.0.28",

View file

@ -0,0 +1,9 @@
import http from '@/lib/http'
const openai = {
store_record(data: any) {
return http.post('/chat_gpt_record', data)
}
}
export default openai

View file

@ -0,0 +1,219 @@
<script setup lang="ts">
import {computed, ref, watch} from 'vue'
import {useGettext} from 'vue3-gettext'
import {useUserStore} from '@/pinia'
import {storeToRefs} from 'pinia'
import {urlJoin} from '@/lib/helper'
import {marked} from 'marked'
import hljs from 'highlight.js'
import 'highlight.js/styles/vs2015.css'
import {SendOutlined} from '@ant-design/icons-vue'
import Template from '@/views/template/Template.vue'
import openai from '@/api/openai'
const {$gettext} = useGettext()
const props = defineProps(['content', 'path', 'history_messages'])
watch(computed(() => props.history_messages), () => {
messages.value = props.history_messages
})
const {current} = useGettext()
const messages: any = ref([])
const loading = ref(false)
const ask_buffer = ref('')
async function send() {
if (messages.value.length === 0) {
messages.value.push({
role: 'user',
content: props.content + '\n\nCurrent Language Code: ' + current
})
} else {
messages.value.push({
role: 'user',
content: ask_buffer.value
})
ask_buffer.value = ''
}
loading.value = true
const t = ref({
role: 'assistant',
content: ''
})
const user = useUserStore()
const {token} = storeToRefs(user)
console.log('fetching...')
let res = await fetch(urlJoin(window.location.pathname, '/api/chat_gpt'), {
method: 'POST',
headers: {'Accept': 'text/event-stream', Authorization: token.value},
body: JSON.stringify({messages: messages.value})
})
messages.value.push(t.value)
// read body as stream
console.log('reading...')
let reader = res.body!.getReader()
// read stream
console.log('reading stream...')
let buffer = ''
while (true) {
let {done, value} = await reader.read()
if (done) {
console.log('done')
loading.value = false
store_record()
break
}
apply(value)
}
function apply(input: any) {
const decoder = new TextDecoder('utf-8')
const raw = decoder.decode(input)
const regex = /{"content":"(.+?)"}/g
const matches = raw.match(regex)
matches?.forEach(v => {
const content = JSON.parse(v).content
for (let c of content) {
buffer += c
if (isCodeBlockComplete(buffer)) {
t.value.content = buffer
} else {
t.value.content = buffer + '\n```'
}
}
})
}
function isCodeBlockComplete(text: string) {
const codeBlockRegex = /```/g
const matches = text.match(codeBlockRegex)
if (matches) {
return matches.length % 2 === 0
} else {
return true
}
}
}
const renderer = new marked.Renderer()
renderer.code = (code, lang: string) => {
const language = hljs.getLanguage(lang) ? lang : 'nginx'
const highlightedCode = hljs.highlight(code, {language}).value
return `<pre><code class="hljs ${language}">${highlightedCode}</code></pre>`
}
marked.setOptions({
renderer: renderer,
langPrefix: 'hljs language-', // highlight.js css expects a top-level 'hljs' class.
pedantic: false,
gfm: true,
breaks: false,
sanitize: false,
smartypants: true,
xhtml: false
})
function store_record() {
openai.store_record({
file_name: props.path,
messages: messages.value
})
}
</script>
<template>
<a-card title="ChatGPT">
<div class="chatgpt-container">
<template v-if="messages?.length>0">
<a-list
class="chatgpt-log"
item-layout="horizontal"
:data-source="messages"
>
<template #renderItem="{ item }">
<a-list-item>
<a-comment :author="item.role" :avatar="item.avatar">
<template #content>
<div class="content" v-html="marked.parse(item.content)"></div>
</template>
</a-comment>
</a-list-item>
</template>
</a-list>
<div class="input-msg">
<a-textarea auto-size v-model:value="ask_buffer"/>
<div class="sned-btn">
<a-button size="small" type="text" :loading="loading" @click="send">
<send-outlined/>
</a-button>
</div>
</div>
</template>
<template v-else>
<a-button @click="send">{{ $gettext('Chat with ChatGPT') }}</a-button>
</template>
</div>
</a-card>
</template>
<style lang="less" scoped>
.chatgpt-container {
margin: 0 auto;
max-width: 800px;
.chatgpt-log {
.content {
width: 100%;
:deep(.hljs) {
border-radius: 5px;
}
}
:deep(.ant-comment-content) {
width: 100%;
}
:deep(.ant-comment) {
width: 100%;
}
:deep(.ant-comment-content-detail) {
width: 100%;
p {
margin-bottom: 10px;
}
}
:deep(.ant-list-item:first-child) {
display: none;
}
}
.input-msg {
position: relative;
.sned-btn {
position: absolute;
right: 0;
bottom: 3px;
}
}
}
</style>

View file

@ -10,6 +10,7 @@ import domain from '@/api/domain'
import ngx from '@/api/ngx'
import {message} from 'ant-design-vue'
import config from '@/api/config'
import ChatGPT from '@/components/ChatGPT/ChatGPT.vue'
const {$gettext, interpolate} = useGettext()
@ -52,6 +53,7 @@ const advance_mode = computed({
advance_mode_ref.value = v
}
})
const history_chatgpt_record = ref([])
function handle_response(r: any) {
@ -64,6 +66,7 @@ function handle_response(r: any) {
configText.value = r.config
enabled.value = r.enabled
auto_cert.value = r.auto_cert
history_chatgpt_record.value = r.chatgpt_messages
Object.assign(ngx_config, r.tokenized)
Object.assign(cert_info_map, r.cert_info)
}
@ -73,6 +76,8 @@ function init() {
domain.get(name.value).then((r: any) => {
handle_response(r)
}).catch(handle_parse_error)
} else {
history_chatgpt_record.value = []
}
}
@ -159,65 +164,71 @@ function on_change_enabled(checked: boolean) {
}
</script>
<template>
<div>
<a-card :bordered="false">
<template #title>
<span style="margin-right: 10px">{{ interpolate($gettext('Edit %{n}'), {n: name}) }}</span>
<a-tag color="blue" v-if="enabled">
{{ $gettext('Enabled') }}
</a-tag>
<a-tag color="orange" v-else>
{{ $gettext('Disabled') }}
</a-tag>
</template>
<template #extra>
<div class="mode-switch">
<div class="switch">
<a-switch size="small" :disabled="parse_error_status"
v-model:checked="advance_mode" @change="on_mode_change"/>
<a-row :gutter="16">
<a-col :xs="24" :sm="18" :md="16">
<a-card :bordered="false">
<template #title>
<span style="margin-right: 10px">{{ interpolate($gettext('Edit %{n}'), {n: name}) }}</span>
<a-tag color="blue" v-if="enabled">
{{ $gettext('Enabled') }}
</a-tag>
<a-tag color="orange" v-else>
{{ $gettext('Disabled') }}
</a-tag>
</template>
<template #extra>
<div class="mode-switch">
<div class="switch">
<a-switch size="small" :disabled="parse_error_status"
v-model:checked="advance_mode" @change="on_mode_change"/>
</div>
<template v-if="advance_mode">
<div>{{ $gettext('Advance Mode') }}</div>
</template>
<template v-else>
<div>{{ $gettext('Basic Mode') }}</div>
</template>
</div>
<template v-if="advance_mode">
<div>{{ $gettext('Advance Mode') }}</div>
</template>
<template v-else>
<div>{{ $gettext('Basic Mode') }}</div>
</template>
</div>
</template>
</template>
<transition name="slide-fade">
<div v-if="advance_mode" key="advance">
<div class="parse-error-alert-wrapper" v-if="parse_error_status">
<a-alert :message="$gettext('Nginx Configuration Parse Error')"
:description="parse_error_message"
type="error"
show-icon
<transition name="slide-fade">
<div v-if="advance_mode" key="advance">
<div class="parse-error-alert-wrapper" v-if="parse_error_status">
<a-alert :message="$gettext('Nginx Configuration Parse Error')"
:description="parse_error_message"
type="error"
show-icon
/>
</div>
<div>
<code-editor v-model:content="configText"/>
</div>
</div>
<div class="domain-edit-container" key="basic" v-else>
<a-form-item :label="$gettext('Enabled')">
<a-switch v-model:checked="enabled" @change="on_change_enabled"/>
</a-form-item>
<a-form-item :label="$gettext('Name')">
<a-input v-model:value="filename"/>
</a-form-item>
<ngx-config-editor
ref="ngx_config_editor"
:ngx_config="ngx_config"
:cert_info="cert_info_map"
v-model:auto_cert="auto_cert"
:enabled="enabled"
@callback="save()"
/>
</div>
<div>
<code-editor v-model:content="configText"/>
</div>
</div>
</transition>
</a-card>
</a-col>
<div class="domain-edit-container" key="basic" v-else>
<a-form-item :label="$gettext('Enabled')">
<a-switch v-model:checked="enabled" @change="on_change_enabled"/>
</a-form-item>
<a-form-item :label="$gettext('Name')">
<a-input v-model:value="filename"/>
</a-form-item>
<ngx-config-editor
ref="ngx_config_editor"
:ngx_config="ngx_config"
:cert_info="cert_info_map"
v-model:auto_cert="auto_cert"
:enabled="enabled"
@callback="save()"
/>
</div>
</transition>
</a-card>
<a-col class="col-right" :xs="24" :sm="6" :md="8">
<chat-g-p-t class="chatgpt" :content="configText" :path="ngx_config.file_name"
:history_messages="history_chatgpt_record"/>
</a-col>
<footer-tool-bar>
<a-space>
@ -229,7 +240,7 @@ function on_change_enabled(checked: boolean) {
</a-button>
</a-space>
</footer-tool-bar>
</div>
</a-row>
</template>
<style lang="less">
@ -237,6 +248,15 @@ function on_change_enabled(checked: boolean) {
</style>
<style lang="less" scoped>
.col-right {
position: relative;
.chatgpt {
position: sticky;
top: 78px;
}
}
.ant-card {
margin: 10px 0;
box-shadow: unset;

View file

@ -1912,6 +1912,11 @@ he@1.2.0, he@^1.2.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
highlight.js@^11.7.0:
version "11.7.0"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.7.0.tgz#3ff0165bc843f8c9bce1fd89e2fda9143d24b11e"
integrity sha512-1rRqesRFhMO/PRF+G86evnyJkCgaZFOI+Z6kdj15TA18funfoqJXvgPCLSf0SWq3SRfg1j3HlDs8o4s3EGq1oQ==
html-minifier-terser@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#bfc818934cc07918f6b3669f5774ecdfd48f32ab"

37
go.mod
View file

@ -11,7 +11,7 @@ require (
github.com/go-co-op/gocron v1.18.0
github.com/go-playground/locales v0.14.1
github.com/go-playground/universal-translator v0.18.1
github.com/go-playground/validator/v10 v10.11.2
github.com/go-playground/validator/v10 v10.12.0
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/google/uuid v1.3.0
github.com/gorilla/websocket v1.5.0
@ -19,54 +19,61 @@ require (
github.com/jpillora/overseer v1.1.6
github.com/lib/pq v1.10.7
github.com/pkg/errors v0.9.1
github.com/sashabaranov/go-openai v1.5.3
github.com/shirou/gopsutil/v3 v3.23.1
github.com/spf13/cast v1.5.0
github.com/tufanbarisyildirim/gonginx v0.0.0-20230104065106-9ae864d29eed
github.com/unknwon/com v1.0.1
golang.org/x/crypto v0.6.0
golang.org/x/crypto v0.7.0
gopkg.in/ini.v1 v1.67.0
gorm.io/driver/sqlite v1.4.4
gorm.io/gorm v1.24.5
gorm.io/gen v0.3.21
gorm.io/gorm v1.24.6
gorm.io/plugin/dbresolver v1.4.1
)
require (
github.com/StackExchange/wmi v1.2.1 // indirect
github.com/bytedance/sonic v1.8.2 // indirect
github.com/bytedance/sonic v1.8.5 // indirect
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/goccy/go-json v0.10.0 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/jpillora/s3 v1.1.4 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/leodido/go-urn v1.2.2 // indirect
github.com/lufia/plan9stats v0.0.0-20230110061619-bbe2e5e100de // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-sqlite3 v1.14.16 // indirect
github.com/miekg/dns v1.1.50 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/pelletier/go-toml/v2 v2.0.7 // indirect
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/tklauser/go-sysconf v0.3.11 // indirect
github.com/tklauser/numcpus v0.6.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.10 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/arch v0.2.0 // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/mod v0.9.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect
golang.org/x/tools v0.6.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect
golang.org/x/tools v0.7.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/fsnotify.v1 v1.4.7 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/datatypes v1.1.1 // indirect
gorm.io/driver/mysql v1.4.7 // indirect
gorm.io/hints v1.1.1 // indirect
)

100
go.sum
View file

@ -2,8 +2,8 @@ github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrU
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.8.2 h1:Eq1oE3xWIBE3tj2ZtJFK1rDAx7+uA4bRytozVhXMHKY=
github.com/bytedance/sonic v1.8.2/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/bytedance/sonic v1.8.5 h1:kjX0/vo5acEQ/sinD/18SkA/lDDUk23F0RcaHvI7omc=
github.com/bytedance/sonic v1.8.5/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
@ -44,12 +44,17 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+
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.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU=
github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s=
github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI=
github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@ -67,6 +72,14 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w=
github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
@ -87,8 +100,8 @@ github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8t
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/leodido/go-urn v1.2.2 h1:7z68G0FCGvDk646jz1AelTYNYWrTNm0bEcFAo147wt4=
github.com/leodido/go-urn v1.2.2/go.mod h1:kUaIbLZWttglzwNuG0pgsh5vuV6u2YcGBYz1hIPjtOQ=
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
@ -100,6 +113,7 @@ github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE=
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -108,8 +122,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us=
github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -119,7 +133,10 @@ github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3g
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ=
github.com/sashabaranov/go-openai v1.5.3 h1:o6n6dj0h9u+5mE1m+D8eT0zYhh7229o8ymDd2zDwAXU=
github.com/sashabaranov/go-openai v1.5.3/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
github.com/shirou/gopsutil/v3 v3.23.1 h1:a9KKO+kGLKEvcPIs4W62v0nu3sciVDOOOPUD0Hz7z/4=
github.com/shirou/gopsutil/v3 v3.23.1/go.mod h1:NN6mnm5/0k8jw4cBfCnJtr5L7ErOTg18tMNpgFkn0hA=
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
@ -141,8 +158,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
@ -153,8 +171,8 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.10 h1:eimT6Lsr+2lzmSZxPhLFoOWFmQqwk0fllJJ5hEbTXtQ=
github.com/ugorji/go/codec v1.2.10/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@ -162,25 +180,25 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.2.0 h1:W1sUEHXiJTfjaFJ5SLo0N6lZn+0eO5gWD1MFeTGqQEY=
golang.org/x/arch v0.2.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -202,30 +220,30 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
@ -237,11 +255,27 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/datatypes v1.1.1 h1:XAjO7NNfUKVUvnS3+BkqMrPXxCAcxDlpOYbjnizxNCw=
gorm.io/datatypes v1.1.1/go.mod h1:u8GEgFjJ+GpsGfgHmBUcQqHm/937t3sj/SO9dvbndTg=
gorm.io/driver/mysql v1.4.3/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c=
gorm.io/driver/mysql v1.4.7 h1:rY46lkCspzGHn7+IYsNpSfEv9tA+SU4SkkB+GFX125Y=
gorm.io/driver/mysql v1.4.7/go.mod h1:SxzItlnT1cb6e1e4ZRpgJN2VYtcqJgqnHxWr4wsP8oc=
gorm.io/driver/postgres v1.4.5 h1:mTeXTTtHAgnS9PgmhN2YeUbazYpLhUI1doLnw42XUZc=
gorm.io/driver/sqlite v1.4.2/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
gorm.io/driver/sqlite v1.4.4 h1:gIufGoR0dQzjkyqDyYSCvsYR6fba1Gw5YKDqKeChxFc=
gorm.io/driver/sqlite v1.4.4/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0=
gorm.io/gen v0.3.21 h1:t8329wT4tW1ZZWOm7vn4LV6OIrz8a5zCg+p78ezt+rA=
gorm.io/gen v0.3.21/go.mod h1:aWgvoKdG9f8Des4TegSa0N5a+gwhGsFo0JJMaLwokvk=
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
gorm.io/gorm v1.24.5 h1:g6OPREKqqlWq4kh/3MCQbZKImeB9e6Xgc4zD+JgNZGE=
gorm.io/gorm v1.24.5/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
gorm.io/gorm v1.24.3/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
gorm.io/gorm v1.24.6 h1:wy98aq9oFEetsc4CAbKD2SoBCdMzsbSIvSUUFJuHi5s=
gorm.io/gorm v1.24.6/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/hints v1.1.1 h1:NPampLxQujY+277452rt4yqtg6JmzNZ1jA2olk0eFXw=
gorm.io/hints v1.1.1/go.mod h1:zdwzfFqvBWGbpuKiAhLFOSGSpeD3/VsRgkXR9Y7Z3cs=
gorm.io/plugin/dbresolver v1.4.1 h1:Ug4LcoPhrvqq71UhxtF346f+skTYoCa/nEsdjvHwEzk=
gorm.io/plugin/dbresolver v1.4.1/go.mod h1:CTbCtMWhsjXSiJqiW2R8POvJ2cq18RVOl4WGyT5nhNc=
gotest.tools/v3 v3.2.0 h1:I0DwBVMGAx26dttAj1BtJLAkVGncrkkUXfJLC4Flt/I=
gotest.tools/v3 v3.2.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

View file

@ -7,6 +7,7 @@ import (
"github.com/0xJacky/Nginx-UI/server/model"
"github.com/0xJacky/Nginx-UI/server/pkg/cert"
"github.com/0xJacky/Nginx-UI/server/pkg/nginx"
"github.com/0xJacky/Nginx-UI/server/query"
"github.com/0xJacky/Nginx-UI/server/router"
"github.com/0xJacky/Nginx-UI/server/service"
"github.com/0xJacky/Nginx-UI/server/settings"
@ -50,7 +51,8 @@ func prog(state overseer.State) {
log.Printf("Nginx config dir path: %s", nginx.GetConfPath())
if "" != settings.ServerSettings.JwtSecret {
model.Init()
db := model.Init()
query.Init(db)
}
s := gocron.NewScheduler(time.UTC)

View file

@ -3,6 +3,7 @@ package api
import (
"github.com/0xJacky/Nginx-UI/server/pkg/config_list"
"github.com/0xJacky/Nginx-UI/server/pkg/nginx"
"github.com/0xJacky/Nginx-UI/server/query"
"github.com/gin-gonic/gin"
"log"
"net/http"
@ -84,8 +85,17 @@ func GetConfig(c *gin.Context) {
return
}
g := query.ChatGPTLog
chatgpt, err := g.Where(g.Name.Eq(path)).FirstOrCreate()
if err != nil {
ErrHandler(c, err)
return
}
c.JSON(http.StatusOK, gin.H{
"config": string(content),
"config": string(content),
"chatgpt_messages": chatgpt.Content,
})
}

View file

@ -1,440 +1,450 @@
package api
import (
"github.com/0xJacky/Nginx-UI/server/model"
"github.com/0xJacky/Nginx-UI/server/pkg/cert"
"github.com/0xJacky/Nginx-UI/server/pkg/config_list"
"github.com/0xJacky/Nginx-UI/server/pkg/helper"
"github.com/0xJacky/Nginx-UI/server/pkg/nginx"
"github.com/gin-gonic/gin"
"log"
"net/http"
"os"
"strings"
"time"
"github.com/0xJacky/Nginx-UI/server/model"
"github.com/0xJacky/Nginx-UI/server/pkg/cert"
"github.com/0xJacky/Nginx-UI/server/pkg/config_list"
"github.com/0xJacky/Nginx-UI/server/pkg/helper"
"github.com/0xJacky/Nginx-UI/server/pkg/nginx"
"github.com/0xJacky/Nginx-UI/server/query"
"github.com/gin-gonic/gin"
"log"
"net/http"
"os"
"strings"
"time"
)
func GetDomains(c *gin.Context) {
name := c.Query("name")
orderBy := c.Query("order_by")
sort := c.DefaultQuery("sort", "desc")
name := c.Query("name")
orderBy := c.Query("order_by")
sort := c.DefaultQuery("sort", "desc")
mySort := map[string]string{
"enabled": "bool",
"name": "string",
"modify": "time",
}
mySort := map[string]string{
"enabled": "bool",
"name": "string",
"modify": "time",
}
configFiles, err := os.ReadDir(nginx.GetConfPath("sites-available"))
configFiles, err := os.ReadDir(nginx.GetConfPath("sites-available"))
if err != nil {
ErrHandler(c, err)
return
}
if err != nil {
ErrHandler(c, err)
return
}
enabledConfig, err := os.ReadDir(nginx.GetConfPath("sites-enabled"))
enabledConfig, err := os.ReadDir(nginx.GetConfPath("sites-enabled"))
if err != nil {
ErrHandler(c, err)
return
}
if err != nil {
ErrHandler(c, err)
return
}
enabledConfigMap := make(map[string]bool)
for i := range enabledConfig {
enabledConfigMap[enabledConfig[i].Name()] = true
}
enabledConfigMap := make(map[string]bool)
for i := range enabledConfig {
enabledConfigMap[enabledConfig[i].Name()] = true
}
var configs []gin.H
var configs []gin.H
for i := range configFiles {
file := configFiles[i]
fileInfo, _ := file.Info()
if !file.IsDir() {
if name != "" && !strings.Contains(file.Name(), name) {
continue
}
configs = append(configs, gin.H{
"name": file.Name(),
"size": fileInfo.Size(),
"modify": fileInfo.ModTime(),
"enabled": enabledConfigMap[file.Name()],
})
}
}
for i := range configFiles {
file := configFiles[i]
fileInfo, _ := file.Info()
if !file.IsDir() {
if name != "" && !strings.Contains(file.Name(), name) {
continue
}
configs = append(configs, gin.H{
"name": file.Name(),
"size": fileInfo.Size(),
"modify": fileInfo.ModTime(),
"enabled": enabledConfigMap[file.Name()],
})
}
}
configs = config_list.Sort(orderBy, sort, mySort[orderBy], configs)
configs = config_list.Sort(orderBy, sort, mySort[orderBy], configs)
c.JSON(http.StatusOK, gin.H{
"data": configs,
})
c.JSON(http.StatusOK, gin.H{
"data": configs,
})
}
type CertificateInfo struct {
SubjectName string `json:"subject_name"`
IssuerName string `json:"issuer_name"`
NotAfter time.Time `json:"not_after"`
NotBefore time.Time `json:"not_before"`
SubjectName string `json:"subject_name"`
IssuerName string `json:"issuer_name"`
NotAfter time.Time `json:"not_after"`
NotBefore time.Time `json:"not_before"`
}
func GetDomain(c *gin.Context) {
rewriteName, ok := c.Get("rewriteConfigFileName")
rewriteName, ok := c.Get("rewriteConfigFileName")
name := c.Param("name")
name := c.Param("name")
// for modify filename
if ok {
name = rewriteName.(string)
}
// for modify filename
if ok {
name = rewriteName.(string)
}
path := nginx.GetConfPath("sites-available", name)
path := nginx.GetConfPath("sites-available", name)
enabled := true
if _, err := os.Stat(nginx.GetConfPath("sites-enabled", name)); os.IsNotExist(err) {
enabled = false
}
enabled := true
if _, err := os.Stat(nginx.GetConfPath("sites-enabled", name)); os.IsNotExist(err) {
enabled = false
}
c.Set("maybe_error", "nginx_config_syntax_error")
config, err := nginx.ParseNgxConfig(path)
c.Set("maybe_error", "nginx_config_syntax_error")
config, err := nginx.ParseNgxConfig(path)
if err != nil {
ErrHandler(c, err)
return
}
if err != nil {
ErrHandler(c, err)
return
}
c.Set("maybe_error", "")
c.Set("maybe_error", "")
certInfoMap := make(map[int]CertificateInfo)
for serverIdx, server := range config.Servers {
for _, directive := range server.Directives {
if directive.Directive == "ssl_certificate" {
certInfoMap := make(map[int]CertificateInfo)
for serverIdx, server := range config.Servers {
for _, directive := range server.Directives {
if directive.Directive == "ssl_certificate" {
pubKey, err := cert.GetCertInfo(directive.Params)
pubKey, err := cert.GetCertInfo(directive.Params)
if err != nil {
log.Println("Failed to get certificate information", err)
break
}
if err != nil {
log.Println("Failed to get certificate information", err)
break
}
certInfoMap[serverIdx] = CertificateInfo{
SubjectName: pubKey.Subject.CommonName,
IssuerName: pubKey.Issuer.CommonName,
NotAfter: pubKey.NotAfter,
NotBefore: pubKey.NotBefore,
}
certInfoMap[serverIdx] = CertificateInfo{
SubjectName: pubKey.Subject.CommonName,
IssuerName: pubKey.Issuer.CommonName,
NotAfter: pubKey.NotAfter,
NotBefore: pubKey.NotBefore,
}
break
}
}
}
break
}
}
}
certModel, _ := model.FirstCert(name)
certModel, _ := model.FirstCert(name)
c.Set("maybe_error", "nginx_config_syntax_error")
g := query.ChatGPTLog
chatgpt, err := g.Where(g.Name.Eq(path)).FirstOrCreate()
c.JSON(http.StatusOK, gin.H{
"enabled": enabled,
"name": name,
"config": config.FmtCode(),
"tokenized": config,
"auto_cert": certModel.AutoCert == model.AutoCertEnabled,
"cert_info": certInfoMap,
})
if err != nil {
ErrHandler(c, err)
return
}
c.Set("maybe_error", "nginx_config_syntax_error")
c.JSON(http.StatusOK, gin.H{
"enabled": enabled,
"name": name,
"config": config.FmtCode(),
"tokenized": config,
"auto_cert": certModel.AutoCert == model.AutoCertEnabled,
"cert_info": certInfoMap,
"chatgpt_messages": chatgpt.Content,
})
}
func SaveDomain(c *gin.Context) {
name := c.Param("name")
name := c.Param("name")
if name == "" {
c.JSON(http.StatusNotAcceptable, gin.H{
"message": "param name is empty",
})
return
}
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"`
Overwrite bool `json:"overwrite"`
}
var json struct {
Name string `json:"name" binding:"required"`
Content string `json:"content" binding:"required"`
Overwrite bool `json:"overwrite"`
}
if !BindAndValid(c, &json) {
return
}
if !BindAndValid(c, &json) {
return
}
path := nginx.GetConfPath("sites-available", name)
path := nginx.GetConfPath("sites-available", name)
if !json.Overwrite && helper.FileExists(path) {
c.JSON(http.StatusNotAcceptable, gin.H{
"message": "File exists",
})
return
}
if !json.Overwrite && helper.FileExists(path) {
c.JSON(http.StatusNotAcceptable, gin.H{
"message": "File exists",
})
return
}
err := os.WriteFile(path, []byte(json.Content), 0644)
if err != nil {
ErrHandler(c, err)
return
}
enabledConfigFilePath := nginx.GetConfPath("sites-enabled", name)
// rename the config file if needed
if name != json.Name {
newPath := nginx.GetConfPath("sites-available", json.Name)
// check if dst file exists, do not rename
if helper.FileExists(newPath) {
c.JSON(http.StatusNotAcceptable, gin.H{
"message": "File exists",
})
return
}
// recreate soft link
if helper.FileExists(enabledConfigFilePath) {
_ = os.Remove(enabledConfigFilePath)
enabledConfigFilePath = nginx.GetConfPath("sites-enabled", json.Name)
err = os.Symlink(newPath, enabledConfigFilePath)
err := os.WriteFile(path, []byte(json.Content), 0644)
if err != nil {
ErrHandler(c, err)
return
}
enabledConfigFilePath := nginx.GetConfPath("sites-enabled", name)
// rename the config file if needed
if name != json.Name {
newPath := nginx.GetConfPath("sites-available", json.Name)
// check if dst file exists, do not rename
if helper.FileExists(newPath) {
c.JSON(http.StatusNotAcceptable, gin.H{
"message": "File exists",
})
return
}
// recreate soft link
if helper.FileExists(enabledConfigFilePath) {
_ = os.Remove(enabledConfigFilePath)
enabledConfigFilePath = nginx.GetConfPath("sites-enabled", json.Name)
err = os.Symlink(newPath, enabledConfigFilePath)
if err != nil {
ErrHandler(c, err)
return
}
}
if err != nil {
ErrHandler(c, err)
return
}
}
err = os.Rename(path, newPath)
if err != nil {
ErrHandler(c, err)
return
}
err = os.Rename(path, newPath)
if err != nil {
ErrHandler(c, err)
return
}
name = json.Name
c.Set("rewriteConfigFileName", name)
}
name = json.Name
c.Set("rewriteConfigFileName", name)
}
enabledConfigFilePath = nginx.GetConfPath("sites-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,
"error": "nginx_config_syntax_error",
})
return
}
enabledConfigFilePath = nginx.GetConfPath("sites-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,
"error": "nginx_config_syntax_error",
})
return
}
output = nginx.Reload()
output = nginx.Reload()
if nginx.GetLogLevel(output) >= nginx.Warn {
c.JSON(http.StatusInternalServerError, gin.H{
"message": output,
})
return
}
}
if nginx.GetLogLevel(output) >= nginx.Warn {
c.JSON(http.StatusInternalServerError, gin.H{
"message": output,
})
return
}
}
GetDomain(c)
GetDomain(c)
}
func EnableDomain(c *gin.Context) {
configFilePath := nginx.GetConfPath("sites-available", c.Param("name"))
enabledConfigFilePath := nginx.GetConfPath("sites-enabled", c.Param("name"))
configFilePath := nginx.GetConfPath("sites-available", c.Param("name"))
enabledConfigFilePath := nginx.GetConfPath("sites-enabled", c.Param("name"))
_, err := os.Stat(configFilePath)
_, err := os.Stat(configFilePath)
if err != nil {
ErrHandler(c, err)
return
}
if err != nil {
ErrHandler(c, err)
return
}
if _, err = os.Stat(enabledConfigFilePath); os.IsNotExist(err) {
err = os.Symlink(configFilePath, enabledConfigFilePath)
if _, err = os.Stat(enabledConfigFilePath); os.IsNotExist(err) {
err = os.Symlink(configFilePath, enabledConfigFilePath)
if err != nil {
ErrHandler(c, err)
return
}
}
if err != nil {
ErrHandler(c, err)
return
}
}
// Test nginx config, if not pass then disable the site.
output := nginx.TestConf()
// Test nginx config, if not pass then disable the site.
output := nginx.TestConf()
if nginx.GetLogLevel(output) >= nginx.Warn {
_ = os.Remove(enabledConfigFilePath)
c.JSON(http.StatusInternalServerError, gin.H{
"message": output,
})
return
}
if nginx.GetLogLevel(output) >= nginx.Warn {
_ = os.Remove(enabledConfigFilePath)
c.JSON(http.StatusInternalServerError, gin.H{
"message": output,
})
return
}
output = nginx.Reload()
output = nginx.Reload()
if nginx.GetLogLevel(output) >= nginx.Warn {
c.JSON(http.StatusInternalServerError, gin.H{
"message": output,
})
return
}
if nginx.GetLogLevel(output) >= nginx.Warn {
c.JSON(http.StatusInternalServerError, gin.H{
"message": output,
})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "ok",
})
c.JSON(http.StatusOK, gin.H{
"message": "ok",
})
}
func DisableDomain(c *gin.Context) {
enabledConfigFilePath := nginx.GetConfPath("sites-enabled", c.Param("name"))
enabledConfigFilePath := nginx.GetConfPath("sites-enabled", c.Param("name"))
_, err := os.Stat(enabledConfigFilePath)
_, err := os.Stat(enabledConfigFilePath)
if err != nil {
ErrHandler(c, err)
return
}
if err != nil {
ErrHandler(c, err)
return
}
err = os.Remove(enabledConfigFilePath)
err = os.Remove(enabledConfigFilePath)
if err != nil {
ErrHandler(c, err)
return
}
if err != nil {
ErrHandler(c, err)
return
}
// delete auto cert record
certModel := model.Cert{Filename: c.Param("name")}
err = certModel.Remove()
if err != nil {
ErrHandler(c, err)
return
}
// delete auto cert record
certModel := model.Cert{Filename: c.Param("name")}
err = certModel.Remove()
if err != nil {
ErrHandler(c, err)
return
}
output := nginx.Reload()
output := nginx.Reload()
if nginx.GetLogLevel(output) >= nginx.Warn {
c.JSON(http.StatusInternalServerError, gin.H{
"message": output,
})
return
}
if nginx.GetLogLevel(output) >= nginx.Warn {
c.JSON(http.StatusInternalServerError, gin.H{
"message": output,
})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "ok",
})
c.JSON(http.StatusOK, gin.H{
"message": "ok",
})
}
func DeleteDomain(c *gin.Context) {
var err error
name := c.Param("name")
availablePath := nginx.GetConfPath("sites-available", name)
enabledPath := nginx.GetConfPath("sites-enabled", name)
var err error
name := c.Param("name")
availablePath := nginx.GetConfPath("sites-available", name)
enabledPath := nginx.GetConfPath("sites-enabled", name)
if _, err = os.Stat(availablePath); os.IsNotExist(err) {
c.JSON(http.StatusNotFound, gin.H{
"message": "site not found",
})
return
}
if _, err = os.Stat(availablePath); os.IsNotExist(err) {
c.JSON(http.StatusNotFound, gin.H{
"message": "site not found",
})
return
}
if _, err = os.Stat(enabledPath); err == nil {
c.JSON(http.StatusNotAcceptable, gin.H{
"message": "site is enabled",
})
return
}
if _, err = os.Stat(enabledPath); err == nil {
c.JSON(http.StatusNotAcceptable, gin.H{
"message": "site is enabled",
})
return
}
certModel := model.Cert{Filename: name}
_ = certModel.Remove()
certModel := model.Cert{Filename: name}
_ = certModel.Remove()
err = os.Remove(availablePath)
err = os.Remove(availablePath)
if err != nil {
ErrHandler(c, err)
return
}
if err != nil {
ErrHandler(c, err)
return
}
c.JSON(http.StatusOK, gin.H{
"message": "ok",
})
c.JSON(http.StatusOK, gin.H{
"message": "ok",
})
}
func AddDomainToAutoCert(c *gin.Context) {
name := c.Param("name")
name := c.Param("name")
var json struct {
Domains []string `json:"domains"`
}
var json struct {
Domains []string `json:"domains"`
}
if !BindAndValid(c, &json) {
return
}
if !BindAndValid(c, &json) {
return
}
certModel, err := model.FirstOrCreateCert(name)
certModel, err := model.FirstOrCreateCert(name)
if err != nil {
ErrHandler(c, err)
return
}
if err != nil {
ErrHandler(c, err)
return
}
err = certModel.Updates(&model.Cert{
Name: name,
Domains: json.Domains,
AutoCert: model.AutoCertEnabled,
})
err = certModel.Updates(&model.Cert{
Name: name,
Domains: json.Domains,
AutoCert: model.AutoCertEnabled,
})
if err != nil {
ErrHandler(c, err)
return
}
if err != nil {
ErrHandler(c, err)
return
}
c.JSON(http.StatusOK, certModel)
c.JSON(http.StatusOK, certModel)
}
func RemoveDomainFromAutoCert(c *gin.Context) {
name := c.Param("name")
certModel, err := model.FirstCert(name)
name := c.Param("name")
certModel, err := model.FirstCert(name)
if err != nil {
ErrHandler(c, err)
return
}
if err != nil {
ErrHandler(c, err)
return
}
err = certModel.Updates(&model.Cert{
AutoCert: model.AutoCertDisabled,
})
err = certModel.Updates(&model.Cert{
AutoCert: model.AutoCertDisabled,
})
if err != nil {
ErrHandler(c, err)
return
}
c.JSON(http.StatusOK, nil)
if err != nil {
ErrHandler(c, err)
return
}
c.JSON(http.StatusOK, nil)
}
func DuplicateSite(c *gin.Context) {
name := c.Param("name")
name := c.Param("name")
var json struct {
Name string `json:"name" binding:"required"`
}
var json struct {
Name string `json:"name" binding:"required"`
}
if !BindAndValid(c, &json) {
return
}
if !BindAndValid(c, &json) {
return
}
src := nginx.GetConfPath("sites-available", name)
dst := nginx.GetConfPath("sites-available", json.Name)
src := nginx.GetConfPath("sites-available", name)
dst := nginx.GetConfPath("sites-available", json.Name)
if helper.FileExists(dst) {
c.JSON(http.StatusNotAcceptable, gin.H{
"message": "File exists",
})
return
}
if helper.FileExists(dst) {
c.JSON(http.StatusNotAcceptable, gin.H{
"message": "File exists",
})
return
}
_, err := helper.CopyFile(src, dst)
_, err := helper.CopyFile(src, dst)
if err != nil {
ErrHandler(c, err)
return
}
if err != nil {
ErrHandler(c, err)
return
}
c.JSON(http.StatusOK, gin.H{
"dst": dst,
})
c.JSON(http.StatusOK, gin.H{
"dst": dst,
})
}

View file

@ -2,6 +2,7 @@ package api
import (
"github.com/0xJacky/Nginx-UI/server/model"
"github.com/0xJacky/Nginx-UI/server/query"
"github.com/0xJacky/Nginx-UI/server/settings"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
@ -54,7 +55,8 @@ func InstallNginxUI(c *gin.Context) {
}
// Init model
model.Init()
db := model.Init()
query.Init(db)
curd := model.NewCurd(&model.Auth{})
pwd, _ := bcrypt.GenerateFromPassword([]byte(json.Password), bcrypt.DefaultCost)
@ -70,5 +72,4 @@ func InstallNginxUI(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "ok",
})
}

160
server/api/openai.go Normal file
View file

@ -0,0 +1,160 @@
package api
import (
"context"
"fmt"
"github.com/0xJacky/Nginx-UI/server/model"
"github.com/0xJacky/Nginx-UI/server/query"
"github.com/0xJacky/Nginx-UI/server/settings"
"github.com/gin-gonic/gin"
"github.com/pkg/errors"
"github.com/sashabaranov/go-openai"
"io"
"log"
"net/http"
"net/url"
"os"
)
const ChatGPTInitPrompt = "You are a assistant who can help users write and optimise the configurations of Nginx, the first user message contains the content of the configuration file which is currently opened by the user and the current language code(CLC). You suppose to use the language corresponding to the CLC to give the first reply. Later the language environment depends on the user message. The first reply should involve the key information of the file and ask user what can you help them."
func MakeChatCompletionRequest(c *gin.Context) {
var json struct {
Messages []openai.ChatCompletionMessage `json:"messages"`
}
if !BindAndValid(c, &json) {
return
}
messages := []openai.ChatCompletionMessage{
{
Role: openai.ChatMessageRoleSystem,
Content: ChatGPTInitPrompt,
},
}
messages = append(messages, json.Messages...)
// sse server
c.Writer.Header().Set("Content-Type", "text/event-stream")
c.Writer.Header().Set("Cache-Control", "no-cache")
c.Writer.Header().Set("Connection", "keep-alive")
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
log.Println(settings.OpenAISettings.Token)
config := openai.DefaultConfig(settings.OpenAISettings.Token)
if settings.OpenAISettings.Proxy != "" {
proxyUrl, err := url.Parse(settings.OpenAISettings.Proxy)
if err != nil {
c.Stream(func(w io.Writer) bool {
c.SSEvent("message", gin.H{
"type": "error",
"content": err.Error(),
})
return false
})
return
}
transport := &http.Transport{
Proxy: http.ProxyURL(proxyUrl),
}
config.HTTPClient = &http.Client{
Transport: transport,
}
}
if settings.OpenAISettings.BaseUrl != "" {
config.BaseURL = settings.OpenAISettings.BaseUrl
}
openaiClient := openai.NewClientWithConfig(config)
ctx := context.Background()
req := openai.ChatCompletionRequest{
Model: openai.GPT3Dot5Turbo0301,
Messages: messages,
Stream: true,
}
stream, err := openaiClient.CreateChatCompletionStream(ctx, req)
if err != nil {
fmt.Printf("CompletionStream error: %v\n", err)
c.Stream(func(w io.Writer) bool {
c.SSEvent("message", gin.H{
"type": "error",
"content": err.Error(),
})
return false
})
return
}
defer stream.Close()
msgChan := make(chan string)
go func() {
for {
response, err := stream.Recv()
if errors.Is(err, io.EOF) {
close(msgChan)
fmt.Println()
return
}
if err != nil {
fmt.Printf("Stream error: %v\n", err)
close(msgChan)
return
}
// Send SSE to client
message := fmt.Sprintf("%s", response.Choices[0].Delta.Content)
fmt.Printf("%s", response.Choices[0].Delta.Content)
_ = os.Stdout.Sync()
msgChan <- message
}
}()
c.Stream(func(w io.Writer) bool {
if m, ok := <-msgChan; ok {
c.SSEvent("message", gin.H{
"type": "message",
"content": m,
})
return true
}
return false
})
}
func StoreChatGPTRecord(c *gin.Context) {
var json struct {
FileName string `json:"file_name"`
Messages []openai.ChatCompletionMessage `json:"messages"`
}
if !BindAndValid(c, &json) {
return
}
name := json.FileName
g := query.ChatGPTLog
_, err := g.Where(g.Name.Eq(name)).FirstOrCreate()
if err != nil {
ErrHandler(c, err)
return
}
_, err = g.Where(g.Name.Eq(name)).Updates(&model.ChatGPTLog{
Name: name,
Content: json.Messages,
})
if err != nil {
ErrHandler(c, err)
return
}
c.JSON(http.StatusOK, gin.H{
"message": "ok",
})
}

View file

@ -0,0 +1,67 @@
package main
import (
"flag"
"fmt"
"github.com/0xJacky/Nginx-UI/server/model"
"github.com/0xJacky/Nginx-UI/server/settings"
"gorm.io/driver/sqlite"
"gorm.io/gen"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
"path"
)
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",
Mode: gen.WithoutContext | gen.WithDefaultQuery,
//if you want the nullable field generation property to be pointer type, set FieldNullable true
FieldNullable: true,
//if you want to assign field which has default value in `Create` API, set FieldCoverable true, reference: https://gorm.io/docs/create.html#Default-Values
FieldCoverable: true,
// if you want to generate field with unsigned integer type, set FieldSignable true
/* FieldSignable: true,*/
//if you want to generate index tags from database, set FieldWithIndexTag true
/* FieldWithIndexTag: true,*/
//if you want to generate type tags from database, set FieldWithTypeTag true
/* FieldWithTypeTag: true,*/
//if you need unit tests for query code, set WithUnitTest true
/* WithUnitTest: true, */
})
// reuse the database connection in Project or create a connection here
// if you want to use GenerateModel/GenerateModelAs, UseDB is necessary or it will panic
var confPath string
flag.StringVar(&confPath, "config", "app.ini", "Specify the configuration file")
flag.Parse()
settings.Init(confPath)
dbPath := path.Join(path.Dir(settings.ConfPath), fmt.Sprintf("%s.db", settings.ServerSettings.Database))
var err error
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
PrepareStmt: true,
DisableForeignKeyConstraintWhenMigrating: true,
})
if err != nil {
log.Fatalln(err)
}
g.UseDB(db)
// apply basic crud api on structs or table models which is specified by table name with function
// GenerateModel/GenerateModelAs. And generator will generate table models' code when calling Excute.
g.ApplyBasic(model.GenerateAllModel()...)
// apply diy interfaces on structs or table models
g.ApplyInterface(func(method model.Method) {}, model.GenerateAllModel()...)
// execute the action of code generation
g.Execute()
}

View file

@ -0,0 +1,34 @@
package model
import (
"database/sql/driver"
"encoding/json"
"fmt"
"github.com/pkg/errors"
"github.com/sashabaranov/go-openai"
)
type JSON []openai.ChatCompletionMessage
// Scan scan value into Jsonb, implements sql.Scanner interface
func (j *JSON) Scan(value interface{}) error {
bytes, ok := value.([]byte)
if !ok {
return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", value))
}
var result []openai.ChatCompletionMessage
err := json.Unmarshal(bytes, &result)
*j = result
return err
}
// Value return json value, implement driver.Valuer interface
func (j *JSON) Value() (driver.Value, error) {
return json.Marshal(*j)
}
type ChatGPTLog struct {
Name string `json:"name"`
Content JSON `json:"content" gorm:"serializer:json"`
}

View file

@ -6,6 +6,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/spf13/cast"
"gorm.io/driver/sqlite"
"gorm.io/gen"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
@ -22,28 +23,37 @@ type Model struct {
DeletedAt *time.Time `gorm:"index" json:"deleted_at"`
}
func Init() {
func GenerateAllModel() []any {
return []any{
ConfigBackup{},
Auth{},
AuthToken{},
Cert{},
ChatGPTLog{},
}
}
func Init() *gorm.DB {
dbPath := path.Join(path.Dir(settings.ConfPath), fmt.Sprintf("%s.db", settings.ServerSettings.Database))
var err error
db, err = gorm.Open(sqlite.Open(dbPath), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
PrepareStmt: true,
Logger: logger.Default.LogMode(logger.Info),
PrepareStmt: true,
DisableForeignKeyConstraintWhenMigrating: true,
})
if err != nil {
log.Println(err)
}
// Migrate the schema
AutoMigrate(&ConfigBackup{})
AutoMigrate(&Auth{})
AutoMigrate(&AuthToken{})
AutoMigrate(&Cert{})
}
func AutoMigrate(model interface{}) {
err := db.AutoMigrate(model)
// Migrate the schema
err = db.AutoMigrate(GenerateAllModel()...)
if err != nil {
log.Fatal(err)
}
return db
}
func orderAndPaginate(c *gin.Context) func(db *gorm.DB) *gorm.DB {
@ -114,3 +124,10 @@ func GetListWithPagination(models interface{},
return
}
type Method interface {
// FirstByID Where("id=@id")
FirstByID(id int) (*gen.T, error)
// DeleteByID update @@table set deleted_at=NOW() where id=@id
DeleteByID(id int) error
}

View file

@ -0,0 +1,354 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package query
import (
"context"
"strings"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
"github.com/0xJacky/Nginx-UI/server/model"
)
func newAuthToken(db *gorm.DB, opts ...gen.DOOption) authToken {
_authToken := authToken{}
_authToken.authTokenDo.UseDB(db, opts...)
_authToken.authTokenDo.UseModel(&model.AuthToken{})
tableName := _authToken.authTokenDo.TableName()
_authToken.ALL = field.NewAsterisk(tableName)
_authToken.Token = field.NewString(tableName, "token")
_authToken.fillFieldMap()
return _authToken
}
type authToken struct {
authTokenDo
ALL field.Asterisk
Token field.String
fieldMap map[string]field.Expr
}
func (a authToken) Table(newTableName string) *authToken {
a.authTokenDo.UseTable(newTableName)
return a.updateTableName(newTableName)
}
func (a authToken) As(alias string) *authToken {
a.authTokenDo.DO = *(a.authTokenDo.As(alias).(*gen.DO))
return a.updateTableName(alias)
}
func (a *authToken) updateTableName(table string) *authToken {
a.ALL = field.NewAsterisk(table)
a.Token = field.NewString(table, "token")
a.fillFieldMap()
return a
}
func (a *authToken) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := a.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (a *authToken) fillFieldMap() {
a.fieldMap = make(map[string]field.Expr, 1)
a.fieldMap["token"] = a.Token
}
func (a authToken) clone(db *gorm.DB) authToken {
a.authTokenDo.ReplaceConnPool(db.Statement.ConnPool)
return a
}
func (a authToken) replaceDB(db *gorm.DB) authToken {
a.authTokenDo.ReplaceDB(db)
return a
}
type authTokenDo struct{ gen.DO }
// FirstByID Where("id=@id")
func (a authTokenDo) FirstByID(id int) (result *model.AuthToken, err error) {
var params []interface{}
var generateSQL strings.Builder
params = append(params, id)
generateSQL.WriteString("id=? ")
var executeSQL *gorm.DB
executeSQL = a.UnderlyingDB().Where(generateSQL.String(), params...).Take(&result) // ignore_security_alert
err = executeSQL.Error
return
}
// DeleteByID update @@table set deleted_at=NOW() where id=@id
func (a authTokenDo) DeleteByID(id int) (err error) {
var params []interface{}
var generateSQL strings.Builder
params = append(params, id)
generateSQL.WriteString("update auth_tokens set deleted_at=NOW() where id=? ")
var executeSQL *gorm.DB
executeSQL = a.UnderlyingDB().Exec(generateSQL.String(), params...) // ignore_security_alert
err = executeSQL.Error
return
}
func (a authTokenDo) Debug() *authTokenDo {
return a.withDO(a.DO.Debug())
}
func (a authTokenDo) WithContext(ctx context.Context) *authTokenDo {
return a.withDO(a.DO.WithContext(ctx))
}
func (a authTokenDo) ReadDB() *authTokenDo {
return a.Clauses(dbresolver.Read)
}
func (a authTokenDo) WriteDB() *authTokenDo {
return a.Clauses(dbresolver.Write)
}
func (a authTokenDo) Session(config *gorm.Session) *authTokenDo {
return a.withDO(a.DO.Session(config))
}
func (a authTokenDo) Clauses(conds ...clause.Expression) *authTokenDo {
return a.withDO(a.DO.Clauses(conds...))
}
func (a authTokenDo) Returning(value interface{}, columns ...string) *authTokenDo {
return a.withDO(a.DO.Returning(value, columns...))
}
func (a authTokenDo) Not(conds ...gen.Condition) *authTokenDo {
return a.withDO(a.DO.Not(conds...))
}
func (a authTokenDo) Or(conds ...gen.Condition) *authTokenDo {
return a.withDO(a.DO.Or(conds...))
}
func (a authTokenDo) Select(conds ...field.Expr) *authTokenDo {
return a.withDO(a.DO.Select(conds...))
}
func (a authTokenDo) Where(conds ...gen.Condition) *authTokenDo {
return a.withDO(a.DO.Where(conds...))
}
func (a authTokenDo) Exists(subquery interface{ UnderlyingDB() *gorm.DB }) *authTokenDo {
return a.Where(field.CompareSubQuery(field.ExistsOp, nil, subquery.UnderlyingDB()))
}
func (a authTokenDo) Order(conds ...field.Expr) *authTokenDo {
return a.withDO(a.DO.Order(conds...))
}
func (a authTokenDo) Distinct(cols ...field.Expr) *authTokenDo {
return a.withDO(a.DO.Distinct(cols...))
}
func (a authTokenDo) Omit(cols ...field.Expr) *authTokenDo {
return a.withDO(a.DO.Omit(cols...))
}
func (a authTokenDo) Join(table schema.Tabler, on ...field.Expr) *authTokenDo {
return a.withDO(a.DO.Join(table, on...))
}
func (a authTokenDo) LeftJoin(table schema.Tabler, on ...field.Expr) *authTokenDo {
return a.withDO(a.DO.LeftJoin(table, on...))
}
func (a authTokenDo) RightJoin(table schema.Tabler, on ...field.Expr) *authTokenDo {
return a.withDO(a.DO.RightJoin(table, on...))
}
func (a authTokenDo) Group(cols ...field.Expr) *authTokenDo {
return a.withDO(a.DO.Group(cols...))
}
func (a authTokenDo) Having(conds ...gen.Condition) *authTokenDo {
return a.withDO(a.DO.Having(conds...))
}
func (a authTokenDo) Limit(limit int) *authTokenDo {
return a.withDO(a.DO.Limit(limit))
}
func (a authTokenDo) Offset(offset int) *authTokenDo {
return a.withDO(a.DO.Offset(offset))
}
func (a authTokenDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *authTokenDo {
return a.withDO(a.DO.Scopes(funcs...))
}
func (a authTokenDo) Unscoped() *authTokenDo {
return a.withDO(a.DO.Unscoped())
}
func (a authTokenDo) Create(values ...*model.AuthToken) error {
if len(values) == 0 {
return nil
}
return a.DO.Create(values)
}
func (a authTokenDo) CreateInBatches(values []*model.AuthToken, batchSize int) error {
return a.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (a authTokenDo) Save(values ...*model.AuthToken) error {
if len(values) == 0 {
return nil
}
return a.DO.Save(values)
}
func (a authTokenDo) First() (*model.AuthToken, error) {
if result, err := a.DO.First(); err != nil {
return nil, err
} else {
return result.(*model.AuthToken), nil
}
}
func (a authTokenDo) Take() (*model.AuthToken, error) {
if result, err := a.DO.Take(); err != nil {
return nil, err
} else {
return result.(*model.AuthToken), nil
}
}
func (a authTokenDo) Last() (*model.AuthToken, error) {
if result, err := a.DO.Last(); err != nil {
return nil, err
} else {
return result.(*model.AuthToken), nil
}
}
func (a authTokenDo) Find() ([]*model.AuthToken, error) {
result, err := a.DO.Find()
return result.([]*model.AuthToken), err
}
func (a authTokenDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.AuthToken, err error) {
buf := make([]*model.AuthToken, 0, batchSize)
err = a.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (a authTokenDo) FindInBatches(result *[]*model.AuthToken, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return a.DO.FindInBatches(result, batchSize, fc)
}
func (a authTokenDo) Attrs(attrs ...field.AssignExpr) *authTokenDo {
return a.withDO(a.DO.Attrs(attrs...))
}
func (a authTokenDo) Assign(attrs ...field.AssignExpr) *authTokenDo {
return a.withDO(a.DO.Assign(attrs...))
}
func (a authTokenDo) Joins(fields ...field.RelationField) *authTokenDo {
for _, _f := range fields {
a = *a.withDO(a.DO.Joins(_f))
}
return &a
}
func (a authTokenDo) Preload(fields ...field.RelationField) *authTokenDo {
for _, _f := range fields {
a = *a.withDO(a.DO.Preload(_f))
}
return &a
}
func (a authTokenDo) FirstOrInit() (*model.AuthToken, error) {
if result, err := a.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*model.AuthToken), nil
}
}
func (a authTokenDo) FirstOrCreate() (*model.AuthToken, error) {
if result, err := a.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*model.AuthToken), nil
}
}
func (a authTokenDo) FindByPage(offset int, limit int) (result []*model.AuthToken, count int64, err error) {
result, err = a.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = a.Offset(-1).Limit(-1).Count()
return
}
func (a authTokenDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = a.Count()
if err != nil {
return
}
err = a.Offset(offset).Limit(limit).Scan(result)
return
}
func (a authTokenDo) Scan(result interface{}) (err error) {
return a.DO.Scan(result)
}
func (a authTokenDo) Delete(models ...*model.AuthToken) (result gen.ResultInfo, err error) {
return a.DO.Delete(models)
}
func (a *authTokenDo) withDO(do gen.Dao) *authTokenDo {
a.DO = *do.(*gen.DO)
return a
}

374
server/query/auths.gen.go Normal file
View file

@ -0,0 +1,374 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package query
import (
"context"
"strings"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
"github.com/0xJacky/Nginx-UI/server/model"
)
func newAuth(db *gorm.DB, opts ...gen.DOOption) auth {
_auth := auth{}
_auth.authDo.UseDB(db, opts...)
_auth.authDo.UseModel(&model.Auth{})
tableName := _auth.authDo.TableName()
_auth.ALL = field.NewAsterisk(tableName)
_auth.ID = field.NewUint(tableName, "id")
_auth.CreatedAt = field.NewTime(tableName, "created_at")
_auth.UpdatedAt = field.NewTime(tableName, "updated_at")
_auth.DeletedAt = field.NewTime(tableName, "deleted_at")
_auth.Name = field.NewString(tableName, "name")
_auth.Password = field.NewString(tableName, "password")
_auth.fillFieldMap()
return _auth
}
type auth struct {
authDo
ALL field.Asterisk
ID field.Uint
CreatedAt field.Time
UpdatedAt field.Time
DeletedAt field.Time
Name field.String
Password field.String
fieldMap map[string]field.Expr
}
func (a auth) Table(newTableName string) *auth {
a.authDo.UseTable(newTableName)
return a.updateTableName(newTableName)
}
func (a auth) As(alias string) *auth {
a.authDo.DO = *(a.authDo.As(alias).(*gen.DO))
return a.updateTableName(alias)
}
func (a *auth) updateTableName(table string) *auth {
a.ALL = field.NewAsterisk(table)
a.ID = field.NewUint(table, "id")
a.CreatedAt = field.NewTime(table, "created_at")
a.UpdatedAt = field.NewTime(table, "updated_at")
a.DeletedAt = field.NewTime(table, "deleted_at")
a.Name = field.NewString(table, "name")
a.Password = field.NewString(table, "password")
a.fillFieldMap()
return a
}
func (a *auth) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := a.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (a *auth) fillFieldMap() {
a.fieldMap = make(map[string]field.Expr, 6)
a.fieldMap["id"] = a.ID
a.fieldMap["created_at"] = a.CreatedAt
a.fieldMap["updated_at"] = a.UpdatedAt
a.fieldMap["deleted_at"] = a.DeletedAt
a.fieldMap["name"] = a.Name
a.fieldMap["password"] = a.Password
}
func (a auth) clone(db *gorm.DB) auth {
a.authDo.ReplaceConnPool(db.Statement.ConnPool)
return a
}
func (a auth) replaceDB(db *gorm.DB) auth {
a.authDo.ReplaceDB(db)
return a
}
type authDo struct{ gen.DO }
// FirstByID Where("id=@id")
func (a authDo) FirstByID(id int) (result *model.Auth, err error) {
var params []interface{}
var generateSQL strings.Builder
params = append(params, id)
generateSQL.WriteString("id=? ")
var executeSQL *gorm.DB
executeSQL = a.UnderlyingDB().Where(generateSQL.String(), params...).Take(&result) // ignore_security_alert
err = executeSQL.Error
return
}
// DeleteByID update @@table set deleted_at=NOW() where id=@id
func (a authDo) DeleteByID(id int) (err error) {
var params []interface{}
var generateSQL strings.Builder
params = append(params, id)
generateSQL.WriteString("update auths set deleted_at=NOW() where id=? ")
var executeSQL *gorm.DB
executeSQL = a.UnderlyingDB().Exec(generateSQL.String(), params...) // ignore_security_alert
err = executeSQL.Error
return
}
func (a authDo) Debug() *authDo {
return a.withDO(a.DO.Debug())
}
func (a authDo) WithContext(ctx context.Context) *authDo {
return a.withDO(a.DO.WithContext(ctx))
}
func (a authDo) ReadDB() *authDo {
return a.Clauses(dbresolver.Read)
}
func (a authDo) WriteDB() *authDo {
return a.Clauses(dbresolver.Write)
}
func (a authDo) Session(config *gorm.Session) *authDo {
return a.withDO(a.DO.Session(config))
}
func (a authDo) Clauses(conds ...clause.Expression) *authDo {
return a.withDO(a.DO.Clauses(conds...))
}
func (a authDo) Returning(value interface{}, columns ...string) *authDo {
return a.withDO(a.DO.Returning(value, columns...))
}
func (a authDo) Not(conds ...gen.Condition) *authDo {
return a.withDO(a.DO.Not(conds...))
}
func (a authDo) Or(conds ...gen.Condition) *authDo {
return a.withDO(a.DO.Or(conds...))
}
func (a authDo) Select(conds ...field.Expr) *authDo {
return a.withDO(a.DO.Select(conds...))
}
func (a authDo) Where(conds ...gen.Condition) *authDo {
return a.withDO(a.DO.Where(conds...))
}
func (a authDo) Exists(subquery interface{ UnderlyingDB() *gorm.DB }) *authDo {
return a.Where(field.CompareSubQuery(field.ExistsOp, nil, subquery.UnderlyingDB()))
}
func (a authDo) Order(conds ...field.Expr) *authDo {
return a.withDO(a.DO.Order(conds...))
}
func (a authDo) Distinct(cols ...field.Expr) *authDo {
return a.withDO(a.DO.Distinct(cols...))
}
func (a authDo) Omit(cols ...field.Expr) *authDo {
return a.withDO(a.DO.Omit(cols...))
}
func (a authDo) Join(table schema.Tabler, on ...field.Expr) *authDo {
return a.withDO(a.DO.Join(table, on...))
}
func (a authDo) LeftJoin(table schema.Tabler, on ...field.Expr) *authDo {
return a.withDO(a.DO.LeftJoin(table, on...))
}
func (a authDo) RightJoin(table schema.Tabler, on ...field.Expr) *authDo {
return a.withDO(a.DO.RightJoin(table, on...))
}
func (a authDo) Group(cols ...field.Expr) *authDo {
return a.withDO(a.DO.Group(cols...))
}
func (a authDo) Having(conds ...gen.Condition) *authDo {
return a.withDO(a.DO.Having(conds...))
}
func (a authDo) Limit(limit int) *authDo {
return a.withDO(a.DO.Limit(limit))
}
func (a authDo) Offset(offset int) *authDo {
return a.withDO(a.DO.Offset(offset))
}
func (a authDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *authDo {
return a.withDO(a.DO.Scopes(funcs...))
}
func (a authDo) Unscoped() *authDo {
return a.withDO(a.DO.Unscoped())
}
func (a authDo) Create(values ...*model.Auth) error {
if len(values) == 0 {
return nil
}
return a.DO.Create(values)
}
func (a authDo) CreateInBatches(values []*model.Auth, batchSize int) error {
return a.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (a authDo) Save(values ...*model.Auth) error {
if len(values) == 0 {
return nil
}
return a.DO.Save(values)
}
func (a authDo) First() (*model.Auth, error) {
if result, err := a.DO.First(); err != nil {
return nil, err
} else {
return result.(*model.Auth), nil
}
}
func (a authDo) Take() (*model.Auth, error) {
if result, err := a.DO.Take(); err != nil {
return nil, err
} else {
return result.(*model.Auth), nil
}
}
func (a authDo) Last() (*model.Auth, error) {
if result, err := a.DO.Last(); err != nil {
return nil, err
} else {
return result.(*model.Auth), nil
}
}
func (a authDo) Find() ([]*model.Auth, error) {
result, err := a.DO.Find()
return result.([]*model.Auth), err
}
func (a authDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Auth, err error) {
buf := make([]*model.Auth, 0, batchSize)
err = a.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (a authDo) FindInBatches(result *[]*model.Auth, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return a.DO.FindInBatches(result, batchSize, fc)
}
func (a authDo) Attrs(attrs ...field.AssignExpr) *authDo {
return a.withDO(a.DO.Attrs(attrs...))
}
func (a authDo) Assign(attrs ...field.AssignExpr) *authDo {
return a.withDO(a.DO.Assign(attrs...))
}
func (a authDo) Joins(fields ...field.RelationField) *authDo {
for _, _f := range fields {
a = *a.withDO(a.DO.Joins(_f))
}
return &a
}
func (a authDo) Preload(fields ...field.RelationField) *authDo {
for _, _f := range fields {
a = *a.withDO(a.DO.Preload(_f))
}
return &a
}
func (a authDo) FirstOrInit() (*model.Auth, error) {
if result, err := a.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*model.Auth), nil
}
}
func (a authDo) FirstOrCreate() (*model.Auth, error) {
if result, err := a.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*model.Auth), nil
}
}
func (a authDo) FindByPage(offset int, limit int) (result []*model.Auth, count int64, err error) {
result, err = a.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = a.Offset(-1).Limit(-1).Count()
return
}
func (a authDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = a.Count()
if err != nil {
return
}
err = a.Offset(offset).Limit(limit).Scan(result)
return
}
func (a authDo) Scan(result interface{}) (err error) {
return a.DO.Scan(result)
}
func (a authDo) Delete(models ...*model.Auth) (result gen.ResultInfo, err error) {
return a.DO.Delete(models)
}
func (a *authDo) withDO(do gen.Dao) *authDo {
a.DO = *do.(*gen.DO)
return a
}

394
server/query/certs.gen.go Normal file
View file

@ -0,0 +1,394 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package query
import (
"context"
"strings"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
"github.com/0xJacky/Nginx-UI/server/model"
)
func newCert(db *gorm.DB, opts ...gen.DOOption) cert {
_cert := cert{}
_cert.certDo.UseDB(db, opts...)
_cert.certDo.UseModel(&model.Cert{})
tableName := _cert.certDo.TableName()
_cert.ALL = field.NewAsterisk(tableName)
_cert.ID = field.NewUint(tableName, "id")
_cert.CreatedAt = field.NewTime(tableName, "created_at")
_cert.UpdatedAt = field.NewTime(tableName, "updated_at")
_cert.DeletedAt = field.NewTime(tableName, "deleted_at")
_cert.Name = field.NewString(tableName, "name")
_cert.Domains = field.NewField(tableName, "domains")
_cert.Filename = field.NewString(tableName, "filename")
_cert.SSLCertificatePath = field.NewString(tableName, "ssl_certificate_path")
_cert.SSLCertificateKeyPath = field.NewString(tableName, "ssl_certificate_key_path")
_cert.AutoCert = field.NewInt(tableName, "auto_cert")
_cert.Log = field.NewString(tableName, "log")
_cert.fillFieldMap()
return _cert
}
type cert struct {
certDo
ALL field.Asterisk
ID field.Uint
CreatedAt field.Time
UpdatedAt field.Time
DeletedAt field.Time
Name field.String
Domains field.Field
Filename field.String
SSLCertificatePath field.String
SSLCertificateKeyPath field.String
AutoCert field.Int
Log field.String
fieldMap map[string]field.Expr
}
func (c cert) Table(newTableName string) *cert {
c.certDo.UseTable(newTableName)
return c.updateTableName(newTableName)
}
func (c cert) As(alias string) *cert {
c.certDo.DO = *(c.certDo.As(alias).(*gen.DO))
return c.updateTableName(alias)
}
func (c *cert) updateTableName(table string) *cert {
c.ALL = field.NewAsterisk(table)
c.ID = field.NewUint(table, "id")
c.CreatedAt = field.NewTime(table, "created_at")
c.UpdatedAt = field.NewTime(table, "updated_at")
c.DeletedAt = field.NewTime(table, "deleted_at")
c.Name = field.NewString(table, "name")
c.Domains = field.NewField(table, "domains")
c.Filename = field.NewString(table, "filename")
c.SSLCertificatePath = field.NewString(table, "ssl_certificate_path")
c.SSLCertificateKeyPath = field.NewString(table, "ssl_certificate_key_path")
c.AutoCert = field.NewInt(table, "auto_cert")
c.Log = field.NewString(table, "log")
c.fillFieldMap()
return c
}
func (c *cert) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := c.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (c *cert) fillFieldMap() {
c.fieldMap = make(map[string]field.Expr, 11)
c.fieldMap["id"] = c.ID
c.fieldMap["created_at"] = c.CreatedAt
c.fieldMap["updated_at"] = c.UpdatedAt
c.fieldMap["deleted_at"] = c.DeletedAt
c.fieldMap["name"] = c.Name
c.fieldMap["domains"] = c.Domains
c.fieldMap["filename"] = c.Filename
c.fieldMap["ssl_certificate_path"] = c.SSLCertificatePath
c.fieldMap["ssl_certificate_key_path"] = c.SSLCertificateKeyPath
c.fieldMap["auto_cert"] = c.AutoCert
c.fieldMap["log"] = c.Log
}
func (c cert) clone(db *gorm.DB) cert {
c.certDo.ReplaceConnPool(db.Statement.ConnPool)
return c
}
func (c cert) replaceDB(db *gorm.DB) cert {
c.certDo.ReplaceDB(db)
return c
}
type certDo struct{ gen.DO }
// FirstByID Where("id=@id")
func (c certDo) FirstByID(id int) (result *model.Cert, err error) {
var params []interface{}
var generateSQL strings.Builder
params = append(params, id)
generateSQL.WriteString("id=? ")
var executeSQL *gorm.DB
executeSQL = c.UnderlyingDB().Where(generateSQL.String(), params...).Take(&result) // ignore_security_alert
err = executeSQL.Error
return
}
// DeleteByID update @@table set deleted_at=NOW() where id=@id
func (c certDo) DeleteByID(id int) (err error) {
var params []interface{}
var generateSQL strings.Builder
params = append(params, id)
generateSQL.WriteString("update certs set deleted_at=NOW() where id=? ")
var executeSQL *gorm.DB
executeSQL = c.UnderlyingDB().Exec(generateSQL.String(), params...) // ignore_security_alert
err = executeSQL.Error
return
}
func (c certDo) Debug() *certDo {
return c.withDO(c.DO.Debug())
}
func (c certDo) WithContext(ctx context.Context) *certDo {
return c.withDO(c.DO.WithContext(ctx))
}
func (c certDo) ReadDB() *certDo {
return c.Clauses(dbresolver.Read)
}
func (c certDo) WriteDB() *certDo {
return c.Clauses(dbresolver.Write)
}
func (c certDo) Session(config *gorm.Session) *certDo {
return c.withDO(c.DO.Session(config))
}
func (c certDo) Clauses(conds ...clause.Expression) *certDo {
return c.withDO(c.DO.Clauses(conds...))
}
func (c certDo) Returning(value interface{}, columns ...string) *certDo {
return c.withDO(c.DO.Returning(value, columns...))
}
func (c certDo) Not(conds ...gen.Condition) *certDo {
return c.withDO(c.DO.Not(conds...))
}
func (c certDo) Or(conds ...gen.Condition) *certDo {
return c.withDO(c.DO.Or(conds...))
}
func (c certDo) Select(conds ...field.Expr) *certDo {
return c.withDO(c.DO.Select(conds...))
}
func (c certDo) Where(conds ...gen.Condition) *certDo {
return c.withDO(c.DO.Where(conds...))
}
func (c certDo) Exists(subquery interface{ UnderlyingDB() *gorm.DB }) *certDo {
return c.Where(field.CompareSubQuery(field.ExistsOp, nil, subquery.UnderlyingDB()))
}
func (c certDo) Order(conds ...field.Expr) *certDo {
return c.withDO(c.DO.Order(conds...))
}
func (c certDo) Distinct(cols ...field.Expr) *certDo {
return c.withDO(c.DO.Distinct(cols...))
}
func (c certDo) Omit(cols ...field.Expr) *certDo {
return c.withDO(c.DO.Omit(cols...))
}
func (c certDo) Join(table schema.Tabler, on ...field.Expr) *certDo {
return c.withDO(c.DO.Join(table, on...))
}
func (c certDo) LeftJoin(table schema.Tabler, on ...field.Expr) *certDo {
return c.withDO(c.DO.LeftJoin(table, on...))
}
func (c certDo) RightJoin(table schema.Tabler, on ...field.Expr) *certDo {
return c.withDO(c.DO.RightJoin(table, on...))
}
func (c certDo) Group(cols ...field.Expr) *certDo {
return c.withDO(c.DO.Group(cols...))
}
func (c certDo) Having(conds ...gen.Condition) *certDo {
return c.withDO(c.DO.Having(conds...))
}
func (c certDo) Limit(limit int) *certDo {
return c.withDO(c.DO.Limit(limit))
}
func (c certDo) Offset(offset int) *certDo {
return c.withDO(c.DO.Offset(offset))
}
func (c certDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *certDo {
return c.withDO(c.DO.Scopes(funcs...))
}
func (c certDo) Unscoped() *certDo {
return c.withDO(c.DO.Unscoped())
}
func (c certDo) Create(values ...*model.Cert) error {
if len(values) == 0 {
return nil
}
return c.DO.Create(values)
}
func (c certDo) CreateInBatches(values []*model.Cert, batchSize int) error {
return c.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (c certDo) Save(values ...*model.Cert) error {
if len(values) == 0 {
return nil
}
return c.DO.Save(values)
}
func (c certDo) First() (*model.Cert, error) {
if result, err := c.DO.First(); err != nil {
return nil, err
} else {
return result.(*model.Cert), nil
}
}
func (c certDo) Take() (*model.Cert, error) {
if result, err := c.DO.Take(); err != nil {
return nil, err
} else {
return result.(*model.Cert), nil
}
}
func (c certDo) Last() (*model.Cert, error) {
if result, err := c.DO.Last(); err != nil {
return nil, err
} else {
return result.(*model.Cert), nil
}
}
func (c certDo) Find() ([]*model.Cert, error) {
result, err := c.DO.Find()
return result.([]*model.Cert), err
}
func (c certDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Cert, err error) {
buf := make([]*model.Cert, 0, batchSize)
err = c.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (c certDo) FindInBatches(result *[]*model.Cert, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return c.DO.FindInBatches(result, batchSize, fc)
}
func (c certDo) Attrs(attrs ...field.AssignExpr) *certDo {
return c.withDO(c.DO.Attrs(attrs...))
}
func (c certDo) Assign(attrs ...field.AssignExpr) *certDo {
return c.withDO(c.DO.Assign(attrs...))
}
func (c certDo) Joins(fields ...field.RelationField) *certDo {
for _, _f := range fields {
c = *c.withDO(c.DO.Joins(_f))
}
return &c
}
func (c certDo) Preload(fields ...field.RelationField) *certDo {
for _, _f := range fields {
c = *c.withDO(c.DO.Preload(_f))
}
return &c
}
func (c certDo) FirstOrInit() (*model.Cert, error) {
if result, err := c.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*model.Cert), nil
}
}
func (c certDo) FirstOrCreate() (*model.Cert, error) {
if result, err := c.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*model.Cert), nil
}
}
func (c certDo) FindByPage(offset int, limit int) (result []*model.Cert, count int64, err error) {
result, err = c.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = c.Offset(-1).Limit(-1).Count()
return
}
func (c certDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = c.Count()
if err != nil {
return
}
err = c.Offset(offset).Limit(limit).Scan(result)
return
}
func (c certDo) Scan(result interface{}) (err error) {
return c.DO.Scan(result)
}
func (c certDo) Delete(models ...*model.Cert) (result gen.ResultInfo, err error) {
return c.DO.Delete(models)
}
func (c *certDo) withDO(do gen.Dao) *certDo {
c.DO = *do.(*gen.DO)
return c
}

View file

@ -0,0 +1,358 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package query
import (
"context"
"strings"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
"github.com/0xJacky/Nginx-UI/server/model"
)
func newChatGPTLog(db *gorm.DB, opts ...gen.DOOption) chatGPTLog {
_chatGPTLog := chatGPTLog{}
_chatGPTLog.chatGPTLogDo.UseDB(db, opts...)
_chatGPTLog.chatGPTLogDo.UseModel(&model.ChatGPTLog{})
tableName := _chatGPTLog.chatGPTLogDo.TableName()
_chatGPTLog.ALL = field.NewAsterisk(tableName)
_chatGPTLog.Name = field.NewString(tableName, "name")
_chatGPTLog.Content = field.NewField(tableName, "content")
_chatGPTLog.fillFieldMap()
return _chatGPTLog
}
type chatGPTLog struct {
chatGPTLogDo
ALL field.Asterisk
Name field.String
Content field.Field
fieldMap map[string]field.Expr
}
func (c chatGPTLog) Table(newTableName string) *chatGPTLog {
c.chatGPTLogDo.UseTable(newTableName)
return c.updateTableName(newTableName)
}
func (c chatGPTLog) As(alias string) *chatGPTLog {
c.chatGPTLogDo.DO = *(c.chatGPTLogDo.As(alias).(*gen.DO))
return c.updateTableName(alias)
}
func (c *chatGPTLog) updateTableName(table string) *chatGPTLog {
c.ALL = field.NewAsterisk(table)
c.Name = field.NewString(table, "name")
c.Content = field.NewField(table, "content")
c.fillFieldMap()
return c
}
func (c *chatGPTLog) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := c.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (c *chatGPTLog) fillFieldMap() {
c.fieldMap = make(map[string]field.Expr, 2)
c.fieldMap["name"] = c.Name
c.fieldMap["content"] = c.Content
}
func (c chatGPTLog) clone(db *gorm.DB) chatGPTLog {
c.chatGPTLogDo.ReplaceConnPool(db.Statement.ConnPool)
return c
}
func (c chatGPTLog) replaceDB(db *gorm.DB) chatGPTLog {
c.chatGPTLogDo.ReplaceDB(db)
return c
}
type chatGPTLogDo struct{ gen.DO }
// FirstByID Where("id=@id")
func (c chatGPTLogDo) FirstByID(id int) (result *model.ChatGPTLog, err error) {
var params []interface{}
var generateSQL strings.Builder
params = append(params, id)
generateSQL.WriteString("id=? ")
var executeSQL *gorm.DB
executeSQL = c.UnderlyingDB().Where(generateSQL.String(), params...).Take(&result) // ignore_security_alert
err = executeSQL.Error
return
}
// DeleteByID update @@table set deleted_at=NOW() where id=@id
func (c chatGPTLogDo) DeleteByID(id int) (err error) {
var params []interface{}
var generateSQL strings.Builder
params = append(params, id)
generateSQL.WriteString("update chat_gpt_logs set deleted_at=NOW() where id=? ")
var executeSQL *gorm.DB
executeSQL = c.UnderlyingDB().Exec(generateSQL.String(), params...) // ignore_security_alert
err = executeSQL.Error
return
}
func (c chatGPTLogDo) Debug() *chatGPTLogDo {
return c.withDO(c.DO.Debug())
}
func (c chatGPTLogDo) WithContext(ctx context.Context) *chatGPTLogDo {
return c.withDO(c.DO.WithContext(ctx))
}
func (c chatGPTLogDo) ReadDB() *chatGPTLogDo {
return c.Clauses(dbresolver.Read)
}
func (c chatGPTLogDo) WriteDB() *chatGPTLogDo {
return c.Clauses(dbresolver.Write)
}
func (c chatGPTLogDo) Session(config *gorm.Session) *chatGPTLogDo {
return c.withDO(c.DO.Session(config))
}
func (c chatGPTLogDo) Clauses(conds ...clause.Expression) *chatGPTLogDo {
return c.withDO(c.DO.Clauses(conds...))
}
func (c chatGPTLogDo) Returning(value interface{}, columns ...string) *chatGPTLogDo {
return c.withDO(c.DO.Returning(value, columns...))
}
func (c chatGPTLogDo) Not(conds ...gen.Condition) *chatGPTLogDo {
return c.withDO(c.DO.Not(conds...))
}
func (c chatGPTLogDo) Or(conds ...gen.Condition) *chatGPTLogDo {
return c.withDO(c.DO.Or(conds...))
}
func (c chatGPTLogDo) Select(conds ...field.Expr) *chatGPTLogDo {
return c.withDO(c.DO.Select(conds...))
}
func (c chatGPTLogDo) Where(conds ...gen.Condition) *chatGPTLogDo {
return c.withDO(c.DO.Where(conds...))
}
func (c chatGPTLogDo) Exists(subquery interface{ UnderlyingDB() *gorm.DB }) *chatGPTLogDo {
return c.Where(field.CompareSubQuery(field.ExistsOp, nil, subquery.UnderlyingDB()))
}
func (c chatGPTLogDo) Order(conds ...field.Expr) *chatGPTLogDo {
return c.withDO(c.DO.Order(conds...))
}
func (c chatGPTLogDo) Distinct(cols ...field.Expr) *chatGPTLogDo {
return c.withDO(c.DO.Distinct(cols...))
}
func (c chatGPTLogDo) Omit(cols ...field.Expr) *chatGPTLogDo {
return c.withDO(c.DO.Omit(cols...))
}
func (c chatGPTLogDo) Join(table schema.Tabler, on ...field.Expr) *chatGPTLogDo {
return c.withDO(c.DO.Join(table, on...))
}
func (c chatGPTLogDo) LeftJoin(table schema.Tabler, on ...field.Expr) *chatGPTLogDo {
return c.withDO(c.DO.LeftJoin(table, on...))
}
func (c chatGPTLogDo) RightJoin(table schema.Tabler, on ...field.Expr) *chatGPTLogDo {
return c.withDO(c.DO.RightJoin(table, on...))
}
func (c chatGPTLogDo) Group(cols ...field.Expr) *chatGPTLogDo {
return c.withDO(c.DO.Group(cols...))
}
func (c chatGPTLogDo) Having(conds ...gen.Condition) *chatGPTLogDo {
return c.withDO(c.DO.Having(conds...))
}
func (c chatGPTLogDo) Limit(limit int) *chatGPTLogDo {
return c.withDO(c.DO.Limit(limit))
}
func (c chatGPTLogDo) Offset(offset int) *chatGPTLogDo {
return c.withDO(c.DO.Offset(offset))
}
func (c chatGPTLogDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *chatGPTLogDo {
return c.withDO(c.DO.Scopes(funcs...))
}
func (c chatGPTLogDo) Unscoped() *chatGPTLogDo {
return c.withDO(c.DO.Unscoped())
}
func (c chatGPTLogDo) Create(values ...*model.ChatGPTLog) error {
if len(values) == 0 {
return nil
}
return c.DO.Create(values)
}
func (c chatGPTLogDo) CreateInBatches(values []*model.ChatGPTLog, batchSize int) error {
return c.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (c chatGPTLogDo) Save(values ...*model.ChatGPTLog) error {
if len(values) == 0 {
return nil
}
return c.DO.Save(values)
}
func (c chatGPTLogDo) First() (*model.ChatGPTLog, error) {
if result, err := c.DO.First(); err != nil {
return nil, err
} else {
return result.(*model.ChatGPTLog), nil
}
}
func (c chatGPTLogDo) Take() (*model.ChatGPTLog, error) {
if result, err := c.DO.Take(); err != nil {
return nil, err
} else {
return result.(*model.ChatGPTLog), nil
}
}
func (c chatGPTLogDo) Last() (*model.ChatGPTLog, error) {
if result, err := c.DO.Last(); err != nil {
return nil, err
} else {
return result.(*model.ChatGPTLog), nil
}
}
func (c chatGPTLogDo) Find() ([]*model.ChatGPTLog, error) {
result, err := c.DO.Find()
return result.([]*model.ChatGPTLog), err
}
func (c chatGPTLogDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.ChatGPTLog, err error) {
buf := make([]*model.ChatGPTLog, 0, batchSize)
err = c.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (c chatGPTLogDo) FindInBatches(result *[]*model.ChatGPTLog, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return c.DO.FindInBatches(result, batchSize, fc)
}
func (c chatGPTLogDo) Attrs(attrs ...field.AssignExpr) *chatGPTLogDo {
return c.withDO(c.DO.Attrs(attrs...))
}
func (c chatGPTLogDo) Assign(attrs ...field.AssignExpr) *chatGPTLogDo {
return c.withDO(c.DO.Assign(attrs...))
}
func (c chatGPTLogDo) Joins(fields ...field.RelationField) *chatGPTLogDo {
for _, _f := range fields {
c = *c.withDO(c.DO.Joins(_f))
}
return &c
}
func (c chatGPTLogDo) Preload(fields ...field.RelationField) *chatGPTLogDo {
for _, _f := range fields {
c = *c.withDO(c.DO.Preload(_f))
}
return &c
}
func (c chatGPTLogDo) FirstOrInit() (*model.ChatGPTLog, error) {
if result, err := c.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*model.ChatGPTLog), nil
}
}
func (c chatGPTLogDo) FirstOrCreate() (*model.ChatGPTLog, error) {
if result, err := c.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*model.ChatGPTLog), nil
}
}
func (c chatGPTLogDo) FindByPage(offset int, limit int) (result []*model.ChatGPTLog, count int64, err error) {
result, err = c.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = c.Offset(-1).Limit(-1).Count()
return
}
func (c chatGPTLogDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = c.Count()
if err != nil {
return
}
err = c.Offset(offset).Limit(limit).Scan(result)
return
}
func (c chatGPTLogDo) Scan(result interface{}) (err error) {
return c.DO.Scan(result)
}
func (c chatGPTLogDo) Delete(models ...*model.ChatGPTLog) (result gen.ResultInfo, err error) {
return c.DO.Delete(models)
}
func (c *chatGPTLogDo) withDO(do gen.Dao) *chatGPTLogDo {
c.DO = *do.(*gen.DO)
return c
}

View file

@ -0,0 +1,378 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package query
import (
"context"
"strings"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
"github.com/0xJacky/Nginx-UI/server/model"
)
func newConfigBackup(db *gorm.DB, opts ...gen.DOOption) configBackup {
_configBackup := configBackup{}
_configBackup.configBackupDo.UseDB(db, opts...)
_configBackup.configBackupDo.UseModel(&model.ConfigBackup{})
tableName := _configBackup.configBackupDo.TableName()
_configBackup.ALL = field.NewAsterisk(tableName)
_configBackup.ID = field.NewUint(tableName, "id")
_configBackup.CreatedAt = field.NewTime(tableName, "created_at")
_configBackup.UpdatedAt = field.NewTime(tableName, "updated_at")
_configBackup.DeletedAt = field.NewTime(tableName, "deleted_at")
_configBackup.Name = field.NewString(tableName, "name")
_configBackup.FilePath = field.NewString(tableName, "file_path")
_configBackup.Content = field.NewString(tableName, "content")
_configBackup.fillFieldMap()
return _configBackup
}
type configBackup struct {
configBackupDo
ALL field.Asterisk
ID field.Uint
CreatedAt field.Time
UpdatedAt field.Time
DeletedAt field.Time
Name field.String
FilePath field.String
Content field.String
fieldMap map[string]field.Expr
}
func (c configBackup) Table(newTableName string) *configBackup {
c.configBackupDo.UseTable(newTableName)
return c.updateTableName(newTableName)
}
func (c configBackup) As(alias string) *configBackup {
c.configBackupDo.DO = *(c.configBackupDo.As(alias).(*gen.DO))
return c.updateTableName(alias)
}
func (c *configBackup) updateTableName(table string) *configBackup {
c.ALL = field.NewAsterisk(table)
c.ID = field.NewUint(table, "id")
c.CreatedAt = field.NewTime(table, "created_at")
c.UpdatedAt = field.NewTime(table, "updated_at")
c.DeletedAt = field.NewTime(table, "deleted_at")
c.Name = field.NewString(table, "name")
c.FilePath = field.NewString(table, "file_path")
c.Content = field.NewString(table, "content")
c.fillFieldMap()
return c
}
func (c *configBackup) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := c.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (c *configBackup) fillFieldMap() {
c.fieldMap = make(map[string]field.Expr, 7)
c.fieldMap["id"] = c.ID
c.fieldMap["created_at"] = c.CreatedAt
c.fieldMap["updated_at"] = c.UpdatedAt
c.fieldMap["deleted_at"] = c.DeletedAt
c.fieldMap["name"] = c.Name
c.fieldMap["file_path"] = c.FilePath
c.fieldMap["content"] = c.Content
}
func (c configBackup) clone(db *gorm.DB) configBackup {
c.configBackupDo.ReplaceConnPool(db.Statement.ConnPool)
return c
}
func (c configBackup) replaceDB(db *gorm.DB) configBackup {
c.configBackupDo.ReplaceDB(db)
return c
}
type configBackupDo struct{ gen.DO }
// FirstByID Where("id=@id")
func (c configBackupDo) FirstByID(id int) (result *model.ConfigBackup, err error) {
var params []interface{}
var generateSQL strings.Builder
params = append(params, id)
generateSQL.WriteString("id=? ")
var executeSQL *gorm.DB
executeSQL = c.UnderlyingDB().Where(generateSQL.String(), params...).Take(&result) // ignore_security_alert
err = executeSQL.Error
return
}
// DeleteByID update @@table set deleted_at=NOW() where id=@id
func (c configBackupDo) DeleteByID(id int) (err error) {
var params []interface{}
var generateSQL strings.Builder
params = append(params, id)
generateSQL.WriteString("update config_backups set deleted_at=NOW() where id=? ")
var executeSQL *gorm.DB
executeSQL = c.UnderlyingDB().Exec(generateSQL.String(), params...) // ignore_security_alert
err = executeSQL.Error
return
}
func (c configBackupDo) Debug() *configBackupDo {
return c.withDO(c.DO.Debug())
}
func (c configBackupDo) WithContext(ctx context.Context) *configBackupDo {
return c.withDO(c.DO.WithContext(ctx))
}
func (c configBackupDo) ReadDB() *configBackupDo {
return c.Clauses(dbresolver.Read)
}
func (c configBackupDo) WriteDB() *configBackupDo {
return c.Clauses(dbresolver.Write)
}
func (c configBackupDo) Session(config *gorm.Session) *configBackupDo {
return c.withDO(c.DO.Session(config))
}
func (c configBackupDo) Clauses(conds ...clause.Expression) *configBackupDo {
return c.withDO(c.DO.Clauses(conds...))
}
func (c configBackupDo) Returning(value interface{}, columns ...string) *configBackupDo {
return c.withDO(c.DO.Returning(value, columns...))
}
func (c configBackupDo) Not(conds ...gen.Condition) *configBackupDo {
return c.withDO(c.DO.Not(conds...))
}
func (c configBackupDo) Or(conds ...gen.Condition) *configBackupDo {
return c.withDO(c.DO.Or(conds...))
}
func (c configBackupDo) Select(conds ...field.Expr) *configBackupDo {
return c.withDO(c.DO.Select(conds...))
}
func (c configBackupDo) Where(conds ...gen.Condition) *configBackupDo {
return c.withDO(c.DO.Where(conds...))
}
func (c configBackupDo) Exists(subquery interface{ UnderlyingDB() *gorm.DB }) *configBackupDo {
return c.Where(field.CompareSubQuery(field.ExistsOp, nil, subquery.UnderlyingDB()))
}
func (c configBackupDo) Order(conds ...field.Expr) *configBackupDo {
return c.withDO(c.DO.Order(conds...))
}
func (c configBackupDo) Distinct(cols ...field.Expr) *configBackupDo {
return c.withDO(c.DO.Distinct(cols...))
}
func (c configBackupDo) Omit(cols ...field.Expr) *configBackupDo {
return c.withDO(c.DO.Omit(cols...))
}
func (c configBackupDo) Join(table schema.Tabler, on ...field.Expr) *configBackupDo {
return c.withDO(c.DO.Join(table, on...))
}
func (c configBackupDo) LeftJoin(table schema.Tabler, on ...field.Expr) *configBackupDo {
return c.withDO(c.DO.LeftJoin(table, on...))
}
func (c configBackupDo) RightJoin(table schema.Tabler, on ...field.Expr) *configBackupDo {
return c.withDO(c.DO.RightJoin(table, on...))
}
func (c configBackupDo) Group(cols ...field.Expr) *configBackupDo {
return c.withDO(c.DO.Group(cols...))
}
func (c configBackupDo) Having(conds ...gen.Condition) *configBackupDo {
return c.withDO(c.DO.Having(conds...))
}
func (c configBackupDo) Limit(limit int) *configBackupDo {
return c.withDO(c.DO.Limit(limit))
}
func (c configBackupDo) Offset(offset int) *configBackupDo {
return c.withDO(c.DO.Offset(offset))
}
func (c configBackupDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *configBackupDo {
return c.withDO(c.DO.Scopes(funcs...))
}
func (c configBackupDo) Unscoped() *configBackupDo {
return c.withDO(c.DO.Unscoped())
}
func (c configBackupDo) Create(values ...*model.ConfigBackup) error {
if len(values) == 0 {
return nil
}
return c.DO.Create(values)
}
func (c configBackupDo) CreateInBatches(values []*model.ConfigBackup, batchSize int) error {
return c.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (c configBackupDo) Save(values ...*model.ConfigBackup) error {
if len(values) == 0 {
return nil
}
return c.DO.Save(values)
}
func (c configBackupDo) First() (*model.ConfigBackup, error) {
if result, err := c.DO.First(); err != nil {
return nil, err
} else {
return result.(*model.ConfigBackup), nil
}
}
func (c configBackupDo) Take() (*model.ConfigBackup, error) {
if result, err := c.DO.Take(); err != nil {
return nil, err
} else {
return result.(*model.ConfigBackup), nil
}
}
func (c configBackupDo) Last() (*model.ConfigBackup, error) {
if result, err := c.DO.Last(); err != nil {
return nil, err
} else {
return result.(*model.ConfigBackup), nil
}
}
func (c configBackupDo) Find() ([]*model.ConfigBackup, error) {
result, err := c.DO.Find()
return result.([]*model.ConfigBackup), err
}
func (c configBackupDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.ConfigBackup, err error) {
buf := make([]*model.ConfigBackup, 0, batchSize)
err = c.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (c configBackupDo) FindInBatches(result *[]*model.ConfigBackup, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return c.DO.FindInBatches(result, batchSize, fc)
}
func (c configBackupDo) Attrs(attrs ...field.AssignExpr) *configBackupDo {
return c.withDO(c.DO.Attrs(attrs...))
}
func (c configBackupDo) Assign(attrs ...field.AssignExpr) *configBackupDo {
return c.withDO(c.DO.Assign(attrs...))
}
func (c configBackupDo) Joins(fields ...field.RelationField) *configBackupDo {
for _, _f := range fields {
c = *c.withDO(c.DO.Joins(_f))
}
return &c
}
func (c configBackupDo) Preload(fields ...field.RelationField) *configBackupDo {
for _, _f := range fields {
c = *c.withDO(c.DO.Preload(_f))
}
return &c
}
func (c configBackupDo) FirstOrInit() (*model.ConfigBackup, error) {
if result, err := c.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*model.ConfigBackup), nil
}
}
func (c configBackupDo) FirstOrCreate() (*model.ConfigBackup, error) {
if result, err := c.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*model.ConfigBackup), nil
}
}
func (c configBackupDo) FindByPage(offset int, limit int) (result []*model.ConfigBackup, count int64, err error) {
result, err = c.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = c.Offset(-1).Limit(-1).Count()
return
}
func (c configBackupDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = c.Count()
if err != nil {
return
}
err = c.Offset(offset).Limit(limit).Scan(result)
return
}
func (c configBackupDo) Scan(result interface{}) (err error) {
return c.DO.Scan(result)
}
func (c configBackupDo) Delete(models ...*model.ConfigBackup) (result gen.ResultInfo, err error) {
return c.DO.Delete(models)
}
func (c *configBackupDo) withDO(do gen.Dao) *configBackupDo {
c.DO = *do.(*gen.DO)
return c
}

131
server/query/gen.go Normal file
View file

@ -0,0 +1,131 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package query
import (
"context"
"database/sql"
"gorm.io/gorm"
"gorm.io/gen"
"gorm.io/plugin/dbresolver"
)
var (
Q = new(Query)
Auth *auth
AuthToken *authToken
Cert *cert
ChatGPTLog *chatGPTLog
ConfigBackup *configBackup
)
func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
*Q = *Use(db, opts...)
Auth = &Q.Auth
AuthToken = &Q.AuthToken
Cert = &Q.Cert
ChatGPTLog = &Q.ChatGPTLog
ConfigBackup = &Q.ConfigBackup
}
func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
return &Query{
db: db,
Auth: newAuth(db, opts...),
AuthToken: newAuthToken(db, opts...),
Cert: newCert(db, opts...),
ChatGPTLog: newChatGPTLog(db, opts...),
ConfigBackup: newConfigBackup(db, opts...),
}
}
type Query struct {
db *gorm.DB
Auth auth
AuthToken authToken
Cert cert
ChatGPTLog chatGPTLog
ConfigBackup configBackup
}
func (q *Query) Available() bool { return q.db != nil }
func (q *Query) clone(db *gorm.DB) *Query {
return &Query{
db: db,
Auth: q.Auth.clone(db),
AuthToken: q.AuthToken.clone(db),
Cert: q.Cert.clone(db),
ChatGPTLog: q.ChatGPTLog.clone(db),
ConfigBackup: q.ConfigBackup.clone(db),
}
}
func (q *Query) ReadDB() *Query {
return q.ReplaceDB(q.db.Clauses(dbresolver.Read))
}
func (q *Query) WriteDB() *Query {
return q.ReplaceDB(q.db.Clauses(dbresolver.Write))
}
func (q *Query) ReplaceDB(db *gorm.DB) *Query {
return &Query{
db: db,
Auth: q.Auth.replaceDB(db),
AuthToken: q.AuthToken.replaceDB(db),
Cert: q.Cert.replaceDB(db),
ChatGPTLog: q.ChatGPTLog.replaceDB(db),
ConfigBackup: q.ConfigBackup.replaceDB(db),
}
}
type queryCtx struct {
Auth *authDo
AuthToken *authTokenDo
Cert *certDo
ChatGPTLog *chatGPTLogDo
ConfigBackup *configBackupDo
}
func (q *Query) WithContext(ctx context.Context) *queryCtx {
return &queryCtx{
Auth: q.Auth.WithContext(ctx),
AuthToken: q.AuthToken.WithContext(ctx),
Cert: q.Cert.WithContext(ctx),
ChatGPTLog: q.ChatGPTLog.WithContext(ctx),
ConfigBackup: q.ConfigBackup.WithContext(ctx),
}
}
func (q *Query) Transaction(fc func(tx *Query) error, opts ...*sql.TxOptions) error {
return q.db.Transaction(func(tx *gorm.DB) error { return fc(q.clone(tx)) }, opts...)
}
func (q *Query) Begin(opts ...*sql.TxOptions) *QueryTx {
return &QueryTx{q.clone(q.db.Begin(opts...))}
}
type QueryTx struct{ *Query }
func (q *QueryTx) Commit() error {
return q.db.Commit().Error
}
func (q *QueryTx) Rollback() error {
return q.db.Rollback().Error
}
func (q *QueryTx) SavePoint(name string) error {
return q.db.SavePoint(name).Error
}
func (q *QueryTx) RollbackTo(name string) error {
return q.db.RollbackTo(name).Error
}

9
server/query/query.go Normal file
View file

@ -0,0 +1,9 @@
package query
import (
"gorm.io/gorm"
)
func Init(db *gorm.DB) {
SetDefault(db)
}

View file

@ -1,113 +1,117 @@
package router
import (
"github.com/0xJacky/Nginx-UI/server/api"
"github.com/gin-contrib/static"
"github.com/gin-gonic/gin"
"net/http"
"github.com/0xJacky/Nginx-UI/server/api"
"github.com/gin-contrib/static"
"github.com/gin-gonic/gin"
"net/http"
)
func InitRouter() *gin.Engine {
r := gin.New()
r.Use(gin.Logger())
r := gin.New()
r.Use(gin.Logger())
r.Use(recovery())
r.Use(recovery())
r.Use(cacheJs())
r.Use(cacheJs())
r.Use(static.Serve("/", mustFS("")))
r.Use(static.Serve("/", mustFS("")))
r.NoRoute(func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "/")
})
r.NoRoute(func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "/")
})
root := r.Group("/api")
{
root.GET("install", api.InstallLockCheck)
root.POST("install", api.InstallNginxUI)
root := r.Group("/api")
{
root.GET("install", api.InstallLockCheck)
root.POST("install", api.InstallNginxUI)
root.POST("/login", api.Login)
root.DELETE("/logout", api.Logout)
root.POST("/login", api.Login)
root.DELETE("/logout", api.Logout)
g := root.Group("/", authRequired())
{
g.GET("analytic", api.Analytic)
g.GET("analytic/init", api.GetAnalyticInit)
g := root.Group("/", authRequired())
{
g.GET("analytic", api.Analytic)
g.GET("analytic/init", api.GetAnalyticInit)
g.GET("users", api.GetUsers)
g.GET("user/:id", api.GetUser)
g.POST("user", api.AddUser)
g.POST("user/:id", api.EditUser)
g.DELETE("user/:id", api.DeleteUser)
g.GET("users", api.GetUsers)
g.GET("user/:id", api.GetUser)
g.POST("user", api.AddUser)
g.POST("user/:id", api.EditUser)
g.DELETE("user/:id", api.DeleteUser)
g.GET("domains", api.GetDomains)
g.GET("domain/:name", api.GetDomain)
g.GET("domains", api.GetDomains)
g.GET("domain/:name", api.GetDomain)
// Modify site configuration directly
g.POST("domain/:name", api.SaveDomain)
// Modify site configuration directly
g.POST("domain/:name", api.SaveDomain)
// Transform NgxConf to nginx configuration
g.POST("ngx/build_config", api.BuildNginxConfig)
// Tokenized nginx configuration to NgxConf
g.POST("ngx/tokenize_config", api.TokenizeNginxConfig)
// Format nginx configuration code
g.POST("ngx/format_code", api.FormatNginxConfig)
// nginx reload
g.POST("nginx/reload", api.ReloadNginx)
// nginx restart
g.POST("nginx/restart", api.RestartNginx)
// nginx test
g.POST("nginx/test", api.TestNginx)
// nginx status
g.GET("nginx/status", api.NginxStatus)
// Transform NgxConf to nginx configuration
g.POST("ngx/build_config", api.BuildNginxConfig)
// Tokenized nginx configuration to NgxConf
g.POST("ngx/tokenize_config", api.TokenizeNginxConfig)
// Format nginx configuration code
g.POST("ngx/format_code", api.FormatNginxConfig)
// nginx reload
g.POST("nginx/reload", api.ReloadNginx)
// nginx restart
g.POST("nginx/restart", api.RestartNginx)
// nginx test
g.POST("nginx/test", api.TestNginx)
// nginx status
g.GET("nginx/status", api.NginxStatus)
g.POST("domain/:name/enable", api.EnableDomain)
g.POST("domain/:name/disable", api.DisableDomain)
g.DELETE("domain/:name", api.DeleteDomain)
// duplicate site
g.POST("domain/:name/duplicate", api.DuplicateSite)
g.GET("domain/:name/cert", api.IssueCert)
g.POST("domain/:name/enable", api.EnableDomain)
g.POST("domain/:name/disable", api.DisableDomain)
g.DELETE("domain/:name", api.DeleteDomain)
// duplicate site
g.POST("domain/:name/duplicate", api.DuplicateSite)
g.GET("domain/:name/cert", api.IssueCert)
g.GET("configs", api.GetConfigs)
g.GET("config/*name", api.GetConfig)
g.POST("config", api.AddConfig)
g.POST("config/*name", api.EditConfig)
g.GET("configs", api.GetConfigs)
g.GET("config/*name", api.GetConfig)
g.POST("config", api.AddConfig)
g.POST("config/*name", api.EditConfig)
//g.GET("backups", api.GetFileBackupList)
//g.GET("backup/:id", api.GetFileBackup)
//g.GET("backups", api.GetFileBackupList)
//g.GET("backup/:id", api.GetFileBackup)
g.GET("template", api.GetTemplate)
g.GET("template/configs", api.GetTemplateConfList)
g.GET("template/blocks", api.GetTemplateBlockList)
g.GET("template/block/:name", api.GetTemplateBlock)
g.GET("template", api.GetTemplate)
g.GET("template/configs", api.GetTemplateConfList)
g.GET("template/blocks", api.GetTemplateBlockList)
g.GET("template/block/:name", api.GetTemplateBlock)
g.GET("certs", api.GetCertList)
g.GET("cert/:id", api.GetCert)
g.POST("cert", api.AddCert)
g.POST("cert/:id", api.ModifyCert)
g.DELETE("cert/:id", api.RemoveCert)
// Add domain to auto-renew cert list
g.POST("auto_cert/:name", api.AddDomainToAutoCert)
// Delete domain from auto-renew cert list
g.DELETE("auto_cert/:name", api.RemoveDomainFromAutoCert)
g.GET("certs", api.GetCertList)
g.GET("cert/:id", api.GetCert)
g.POST("cert", api.AddCert)
g.POST("cert/:id", api.ModifyCert)
g.DELETE("cert/:id", api.RemoveCert)
// Add domain to auto-renew cert list
g.POST("auto_cert/:name", api.AddDomainToAutoCert)
// Delete domain from auto-renew cert list
g.DELETE("auto_cert/:name", api.RemoveDomainFromAutoCert)
// pty
g.GET("pty", api.Pty)
// pty
g.GET("pty", api.Pty)
// Nginx log
g.GET("nginx_log", api.NginxLog)
g.POST("nginx_log", api.GetNginxLogPage)
// Nginx log
g.GET("nginx_log", api.NginxLog)
g.POST("nginx_log", api.GetNginxLogPage)
// Settings
g.GET("settings", api.GetSettings)
g.POST("settings", api.SaveSettings)
// Settings
g.GET("settings", api.GetSettings)
g.POST("settings", api.SaveSettings)
// Upgrade
g.GET("upgrade/release", api.GetRelease)
g.GET("upgrade/current", api.GetCurrentVersion)
g.GET("upgrade/perform", api.PerformCoreUpgrade)
}
}
// Upgrade
g.GET("upgrade/release", api.GetRelease)
g.GET("upgrade/current", api.GetCurrentVersion)
g.GET("upgrade/perform", api.PerformCoreUpgrade)
return r
// ChatGPT
g.POST("/chat_gpt", api.MakeChatCompletionRequest)
g.POST("/chat_gpt_record", api.StoreChatGPTRecord)
}
}
return r
}

View file

@ -36,6 +36,13 @@ type NginxLog struct {
ErrorLogPath string `json:"error_log_path"`
}
type OpenAI struct {
BaseUrl string `json:"base_url"`
Token string `json:"token"`
Proxy string `json:"proxy"`
Model string `json:"model"`
}
var ServerSettings = &Server{
HttpPort: "9000",
RunMode: "debug",
@ -53,11 +60,14 @@ var NginxLogSettings = &NginxLog{
ErrorLogPath: "",
}
var OpenAISettings = &OpenAI{}
var ConfPath string
var sections = map[string]interface{}{
"server": ServerSettings,
"nginx_log": NginxLogSettings,
"openai": OpenAISettings,
}
func init() {

View file

@ -0,0 +1,51 @@
package test
import (
"context"
"fmt"
"github.com/0xJacky/Nginx-UI/server/settings"
"github.com/pkg/errors"
"github.com/sashabaranov/go-openai"
"io"
"os"
"testing"
)
func TestChatGPT(t *testing.T) {
settings.Init("../../app.ini")
c := openai.NewClient(settings.OpenAISettings.Token)
ctx := context.Background()
req := openai.ChatCompletionRequest{
Model: openai.GPT3Dot5Turbo0301,
Messages: []openai.ChatCompletionMessage{
{
Role: openai.ChatMessageRoleUser,
Content: "帮我写一个 nginx 配置文件的示例",
},
},
Stream: true,
}
stream, err := c.CreateChatCompletionStream(ctx, req)
if err != nil {
fmt.Printf("CompletionStream error: %v\n", err)
return
}
defer stream.Close()
for {
response, err := stream.Recv()
if errors.Is(err, io.EOF) {
return
}
if err != nil {
fmt.Printf("Stream error: %v\n", err)
return
}
fmt.Printf("%v", response.Choices[0].Delta.Content)
_ = os.Stdout.Sync()
}
}