feat(china): set icp number and public security number #780

This commit is contained in:
Jacky 2024-12-15 14:56:53 +08:00
parent 2950be6e82
commit e326f5e930
No known key found for this signature in database
GPG key ID: 215C21B10DF38B4D
13 changed files with 150 additions and 28 deletions

14
api/public/layout.go Normal file
View file

@ -0,0 +1,14 @@
package public
import (
"github.com/0xJacky/Nginx-UI/settings"
"github.com/gin-gonic/gin"
"net/http"
)
func GetICPSettings(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"icp_number": settings.NodeSettings.ICPNumber,
"public_security_number": settings.NodeSettings.PublicSecurityNumber,
})
}

7
api/public/router.go Normal file
View file

@ -0,0 +1,7 @@
package public
import "github.com/gin-gonic/gin"
func InitRouter(r *gin.RouterGroup) {
r.GET("/icp_settings", GetICPSettings)
}

1
app/components.d.ts vendored
View file

@ -76,6 +76,7 @@ declare module 'vue' {
CodeEditorCodeEditor: typeof import('./src/components/CodeEditor/CodeEditor.vue')['default'] CodeEditorCodeEditor: typeof import('./src/components/CodeEditor/CodeEditor.vue')['default']
EnvIndicatorEnvIndicator: typeof import('./src/components/EnvIndicator/EnvIndicator.vue')['default'] EnvIndicatorEnvIndicator: typeof import('./src/components/EnvIndicator/EnvIndicator.vue')['default']
FooterToolbarFooterToolBar: typeof import('./src/components/FooterToolbar/FooterToolBar.vue')['default'] FooterToolbarFooterToolBar: typeof import('./src/components/FooterToolbar/FooterToolBar.vue')['default']
ICPICP: typeof import('./src/components/ICP/ICP.vue')['default']
LogoLogo: typeof import('./src/components/Logo/Logo.vue')['default'] LogoLogo: typeof import('./src/components/Logo/Logo.vue')['default']
NginxControlNginxControl: typeof import('./src/components/NginxControl/NginxControl.vue')['default'] NginxControlNginxControl: typeof import('./src/components/NginxControl/NginxControl.vue')['default']
NodeSelectorNodeSelector: typeof import('./src/components/NodeSelector/NodeSelector.vue')['default'] NodeSelectorNodeSelector: typeof import('./src/components/NodeSelector/NodeSelector.vue')['default']

14
app/src/api/public.ts Normal file
View file

@ -0,0 +1,14 @@
import http from '@/lib/http'
export interface ICP {
icp_number: string
public_security_number: string
}
const publicApi = {
getICP<ICP>() {
return http.get('/icp_settings')
},
}
export default publicApi

View file

@ -63,6 +63,8 @@ export interface NginxSettings {
export interface NodeSettings { export interface NodeSettings {
name: string name: string
secret: string secret: string
icp_number: string
public_security_number: number
} }
export interface OpenaiSettings { export interface OpenaiSettings {

View file

@ -0,0 +1,50 @@
<script setup lang="ts">
import type { ICP } from '@/api/public'
import publicApi from '@/api/public'
const icp = ref<ICP>({
icp_number: '',
public_security_number: '',
})
publicApi.getICP().then(r => {
icp.value = r
})
const enabled = computed(() => {
return icp.value.icp_number || icp.value.public_security_number
})
const showDot = computed(() => icp.value.icp_number && icp.value.public_security_number)
const publicSecurityNumberLink = computed(() =>
`https://www.beian.gov.cn/portal/registerSystemInfo?recordcode=${icp.value.public_security_number}`)
</script>
<template>
<div v-if="enabled">
<a href="https://beian.miit.gov.cn/" target="_blank">{{ icp.icp_number }}</a>
<span v-if="showDot"> · </span>
<a v-if="icp.public_security_number" class="public_security_number" :href="publicSecurityNumberLink">
<img src="//www.beian.gov.cn/img/new/gongan.png" alt="公安备案">
<span class="ml-5">{{ icp.public_security_number }}</span></a>
</div>
</template>
<style scoped lang="less">
a {
font-size: 14px;
}
.public_security_number {
position: relative;
img {
width: 16px;
height: 16px;
position: absolute;
top: 50%;
transform: translateY(-50%);
}
}
</style>

View file

@ -2,6 +2,7 @@
import auth from '@/api/auth' import auth from '@/api/auth'
import install from '@/api/install' import install from '@/api/install'
import passkey from '@/api/passkey' import passkey from '@/api/passkey'
import ICP from '@/components/ICP/ICP.vue'
import SetLanguage from '@/components/SetLanguage/SetLanguage.vue' import SetLanguage from '@/components/SetLanguage/SetLanguage.vue'
import SwitchAppearance from '@/components/SwitchAppearance/SwitchAppearance.vue' import SwitchAppearance from '@/components/SwitchAppearance/SwitchAppearance.vue'
import Authorization from '@/components/TwoFA/Authorization.vue' import Authorization from '@/components/TwoFA/Authorization.vue'
@ -23,7 +24,7 @@ install.get_lock().then(async (r: { lock: boolean }) => {
const loading = ref(false) const loading = ref(false)
const enabled2FA = ref(false) const enabled2FA = ref(false)
const refOTP = ref() const refOTP = useTemplateRef('refOTP')
const passcode = ref('') const passcode = ref('')
const recoveryCode = ref('') const recoveryCode = ref('')
const passkeyConfigStatus = ref(false) const passkeyConfigStatus = ref(false)
@ -266,7 +267,10 @@ async function handlePasskeyLogin() {
</AFormItem> </AFormItem>
</AForm> </AForm>
<div class="footer"> <div class="footer">
<p>Copyright © 2021 - {{ thisYear }} Nginx UI</p> <p class="mb-4">
Copyright © 2021 - {{ thisYear }} Nginx UI
</p>
<ICP class="mb-4" />
Language Language
<SetLanguage class="inline" /> <SetLanguage class="inline" />
<div class="flex justify-center mt-4"> <div class="flex justify-center mt-4">
@ -295,7 +299,7 @@ async function handlePasskeyLogin() {
height: 100vh; height: 100vh;
.login-form { .login-form {
max-width: 400px; max-width: 420px;
width: 80%; width: 80%;
.project-title { .project-title {
@ -313,7 +317,7 @@ async function handlePasskeyLogin() {
} }
.footer { .footer {
padding: 30px; padding: 30px 20px;
text-align: center; text-align: center;
font-size: 14px; font-size: 14px;
} }

View file

@ -26,6 +26,15 @@ const errors: Record<string, Record<string, string>> = inject('errors') as Recor
<AFormItem :label="$gettext('Terminal Start Command')"> <AFormItem :label="$gettext('Terminal Start Command')">
<p>{{ data.terminal.start_cmd }}</p> <p>{{ data.terminal.start_cmd }}</p>
</AFormItem> </AFormItem>
<AFormItem
:label="$gettext('Node name')"
:validate-status="errors?.node?.name ? 'error' : ''"
:help="errors?.node?.name.includes('safety_text')
? $gettext('The node name should only contain letters, unicode, numbers, hyphens, dashes, and dots.')
: $gettext('Customize the name of local node to be displayed in the environment indicator.')"
>
<AInput v-model:value="data.node.name" />
</AFormItem>
<AFormItem <AFormItem
:label="$gettext('Github Proxy')" :label="$gettext('Github Proxy')"
:validate-status="errors?.http?.github_proxy ? 'error' : ''" :validate-status="errors?.http?.github_proxy ? 'error' : ''"
@ -39,13 +48,28 @@ const errors: Record<string, Record<string, string>> = inject('errors') as Recor
/> />
</AFormItem> </AFormItem>
<AFormItem <AFormItem
:label="$gettext('Node name')" :label="$gettext('ICP Number')"
:validate-status="errors?.node?.name ? 'error' : ''" :validate-status="errors?.node?.icp_number ? 'error' : ''"
:help="errors?.node?.name.includes('safety_text') :help="errors?.node?.icp_number.includes('safety_text')
? $gettext('The node name should only contain letters, unicode, numbers, hyphens, dashes, and dots.') ? $gettext('The ICP Number should only contain letters, unicode, numbers, hyphens, dashes, and dots.')
: $gettext('Customize the name of local node to be displayed in the environment indicator.')" : ''"
> >
<AInput v-model:value="data.node.name" /> <AInput
v-model:value="data.node.icp_number"
:placeholder="$gettext('For Chinese user')"
/>
</AFormItem>
<AFormItem
:label="$gettext('Public Security Number')"
:validate-status="errors?.node?.public_security_number ? 'error' : ''"
:help="errors?.node?.public_security_number.includes('safety_text')
? $gettext('The Public Security Number should only contain letters, unicode, numbers, hyphens, dashes, and dots.')
: ''"
>
<AInput
v-model:value="data.node.public_security_number"
:placeholder="$gettext('For Chinese user')"
/>
</AFormItem> </AFormItem>
</AForm> </AForm>
</template> </template>

View file

@ -23,3 +23,16 @@
默认情况下,如果您启用了跳过安装模式,而没有在服务器部分设置 `App.JwtSecret``Node.Secret` 选项, 默认情况下,如果您启用了跳过安装模式,而没有在服务器部分设置 `App.JwtSecret``Node.Secret` 选项,
Nginx UI 将为这两个选项生成一个随机的 UUID 值。 Nginx UI 将为这两个选项生成一个随机的 UUID 值。
## ICPNumber
- 类型: `string`
- 版本: `>= v2.0.0-beta.42`
此选项用于设置 ICP 备案号。
## PublicSecurityNumber
- 类型: `string`
- 版本: `>= v2.0.0-beta.42`
此选项用于设置 ICP 备案号。

View file

@ -8,6 +8,7 @@ import (
"github.com/0xJacky/Nginx-UI/api/nginx" "github.com/0xJacky/Nginx-UI/api/nginx"
"github.com/0xJacky/Nginx-UI/api/notification" "github.com/0xJacky/Nginx-UI/api/notification"
"github.com/0xJacky/Nginx-UI/api/openai" "github.com/0xJacky/Nginx-UI/api/openai"
"github.com/0xJacky/Nginx-UI/api/public"
"github.com/0xJacky/Nginx-UI/api/settings" "github.com/0xJacky/Nginx-UI/api/settings"
"github.com/0xJacky/Nginx-UI/api/sites" "github.com/0xJacky/Nginx-UI/api/sites"
"github.com/0xJacky/Nginx-UI/api/streams" "github.com/0xJacky/Nginx-UI/api/streams"
@ -39,6 +40,7 @@ func InitRouter() {
root := r.Group("/api") root := r.Group("/api")
{ {
public.InitRouter(root)
system.InitPublicRouter(root) system.InitPublicRouter(root)
user.InitAuthRouter(root) user.InitAuthRouter(root)

View file

@ -5,7 +5,4 @@ type HTTP struct {
InsecureSkipVerify bool `json:"insecure_skip_verify" protected:"true"` InsecureSkipVerify bool `json:"insecure_skip_verify" protected:"true"`
} }
var HTTPSettings = &HTTP{ var HTTPSettings = &HTTP{}
GithubProxy: "",
InsecureSkipVerify: false,
}

View file

@ -11,7 +11,4 @@ type Nginx struct {
RestartCmd string `json:"restart_cmd" protected:"true"` RestartCmd string `json:"restart_cmd" protected:"true"`
} }
var NginxSettings = &Nginx{ var NginxSettings = &Nginx{}
AccessLogPath: "",
ErrorLogPath: "",
}

View file

@ -1,15 +1,12 @@
package settings package settings
type Node struct { type Node struct {
Name string `json:"name" binding:"omitempty,safety_text"` Name string `json:"name" binding:"omitempty,safety_text"`
Secret string `json:"secret" protected:"true"` Secret string `json:"secret" protected:"true"`
SkipInstallation bool `json:"skip_installation" protected:"true"` SkipInstallation bool `json:"skip_installation" protected:"true"`
Demo bool `json:"demo" protected:"true"` Demo bool `json:"demo" protected:"true"`
ICPNumber string `json:"icp_number" binding:"omitempty,safety_text"`
PublicSecurityNumber string `json:"public_security_number" binding:"omitempty,safety_text"`
} }
var NodeSettings = &Node{ var NodeSettings = &Node{}
Name: "",
Secret: "",
SkipInstallation: false,
Demo: false,
}