Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ui/sidebar): show env variables in sidebar settings ui (#946) #956

Merged
merged 2 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/swagger/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -4970,9 +4970,16 @@ const docTemplate = `{
"handler.ResponseSettingGet": {
"type": "object",
"required": [
"envs",
"yaml"
],
"properties": {
"envs": {
"type": "array",
"items": {
"type": "string"
}
},
"yaml": {
"type": "string"
}
Expand Down
7 changes: 7 additions & 0 deletions docs/swagger/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -4963,9 +4963,16 @@
"handler.ResponseSettingGet": {
"type": "object",
"required": [
"envs",
"yaml"
],
"properties": {
"envs": {
"type": "array",
"items": {
"type": "string"
}
},
"yaml": {
"type": "string"
}
Expand Down
5 changes: 5 additions & 0 deletions docs/swagger/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1012,9 +1012,14 @@ definitions:
type: object
handler.ResponseSettingGet:
properties:
envs:
items:
type: string
type: array
yaml:
type: string
required:
- envs
- yaml
type: object
handler.ResponseSettingTemplate:
Expand Down
11 changes: 10 additions & 1 deletion server/handler/setting_get.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ package handler

import (
"os"
"strings"

"github.com/ArtalkJS/Artalk/internal/core"
"github.com/ArtalkJS/Artalk/internal/i18n"
"github.com/ArtalkJS/Artalk/server/common"
"github.com/gofiber/fiber/v2"
"github.com/samber/lo"
)

type ResponseSettingGet struct {
Yaml string `json:"yaml"`
Yaml string `json:"yaml"`
Envs []string `json:"envs"`
}

// @Id GetSettings
Expand All @@ -30,8 +33,14 @@ func SettingGet(app *core.App, router fiber.Router) {
return common.RespError(c, 500, i18n.T("Config file read failed"))
}

// get all environment variables which start with ATK_
envs := lo.Filter(os.Environ(), func(v string, _ int) bool {
return strings.HasPrefix(v, "ATK_")
})

return common.RespData(c, ResponseSettingGet{
Yaml: string(dat),
Envs: envs,
})
}))
}
20 changes: 16 additions & 4 deletions ui/artalk-sidebar/src/components/PreferenceArr.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,22 @@ const props = defineProps<{
}>()

const customValue = ref<string[]>([])
const disabled = ref(false)

onMounted(() => {
sync()
})

function sync() {
const value = settings.get().getCustom(props.node.path)
customValue.value = value && typeof value.toJSON === 'function' ? value.toJSON() : []
disabled.value = !!settings.get().getEnvByPath(props.node.path)
if (typeof value === 'object' && 'toJSON' in value && typeof value.toJSON === 'function') {
customValue.value = value.toJSON()
} else if (typeof value === 'string') {
customValue.value = value.split(' ')
} else {
customValue.value = []
}
}

function save() {
Expand Down Expand Up @@ -43,21 +51,25 @@ function add() {
<input
type="text"
:value="String(item)"
:disabled="disabled"
@change="onChange(index, ($event.target as any).value)"
/>
<button class="act-btn" @click="remove(index)">-</button>
<button v-if="!disabled" class="act-btn" @click="remove(index)">-</button>
</div>
<div class="act-grp">
<div v-if="!disabled" class="act-grp">
<button class="act-btn" @click="add()">+</button>
</div>
</div>
</template>

<style scoped lang="scss">
.arr-grp {
width: 100%;
}

.arr-item {
position: relative;
margin-bottom: 20px;
margin-left: 10px;
padding-right: 50px;

.act-btn {
Expand Down
34 changes: 31 additions & 3 deletions ui/artalk-sidebar/src/components/PreferenceItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ const props = defineProps<{
}>()

const value = ref('')
const disabled = ref(false)

const { t } = useI18n()

onBeforeMount(() => {
// initial value
value.value = settings.get().getCustom(props.node.path)
disabled.value = !!settings.get().getEnvByPath(props.node.path)
})

function onChange() {
Expand All @@ -27,14 +31,18 @@ function onChange() {
</div>

<div class="value">
<div v-if="disabled" class="disable-note">
{{ t('envVarControlHint', { key: 'ATK_' + props.node.path.toUpperCase() }) }}
</div>

<!-- Array -->
<template v-if="node.type === 'array'">
<PreferenceArr :node="node" />
</template>

<!-- 候选框 -->
<template v-else-if="node.selector">
<select v-model="value" @change="onChange">
<select v-model="value" :disabled="disabled" @change="onChange">
<option v-for="(item, i) in node.selector" :key="i" :value="item">
{{ item }}
</option>
Expand All @@ -43,12 +51,12 @@ function onChange() {

<!-- 开关 -->
<template v-else-if="node.type === 'boolean'">
<input v-model="value" type="checkbox" @change="onChange" />
<input v-model="value" type="checkbox" :disabled="disabled" @change="onChange" />
</template>

<!-- 文本框 -->
<template v-else>
<input v-model="value" type="text" @change="onChange" />
<input v-model="value" type="text" :disabled="disabled" @change="onChange" />
</template>
</div>
</div>
Expand Down Expand Up @@ -78,12 +86,32 @@ function onChange() {
}

& > .value {
position: relative;
flex: 1;
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
min-height: 35px;

&:hover {
.disable-note {
opacity: 1;
}
}

.disable-note {
z-index: 999;
padding: 3px 8px;
background: rgba(105, 113, 130, 0.9);
color: #fff;
position: absolute;
top: -30px;
font-size: 13px;
left: 0;
opacity: 0;
transition: opacity 0.2s;
}
}
}
</style>
3 changes: 3 additions & 0 deletions ui/artalk-sidebar/src/i18n/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const en = {
commentAllowAll: 'Anyone Comment',
commentOnlyAdmin: 'Admin Comment Only',
config: 'Config',
envVarControlHint: 'Referenced by the environment variable {key}',
userAdminHint: 'Admin user',
userInConfHint: 'This user is defined in config file',
edit: 'Edit',
Expand Down Expand Up @@ -118,6 +119,7 @@ const zhCN: typeof en = {
commentAllowAll: '所有人可评',
commentOnlyAdmin: '仅管理员可评',
config: '配置文件',
envVarControlHint: '由环境变量 {key} 控制',
userAdminHint: '该用户具有管理员权限',
userInConfHint: '该用户存在于配置文件中',
edit: '编辑',
Expand Down Expand Up @@ -201,6 +203,7 @@ const zhTW: typeof en = {
commentAllowAll: '允許任何人評論',
commentOnlyAdmin: '僅允許管理員評論',
config: '配置文件',
envVarControlHint: '由環境變數 {key} 參照',
userAdminHint: '該用戶具有管理員權限',
userInConfHint: '該用戶存在於配置文件中',
edit: '編輯',
Expand Down
29 changes: 29 additions & 0 deletions ui/artalk-sidebar/src/lib/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export class Settings {
private tree: OptionNode
private flatten: { [path: string]: OptionNode }
private customs = shallowRef<YAML.Document.Parsed<YAML.ParsedNode>>()
private envs = shallowRef<{ [key: string]: string }>()

constructor(yamlObj: YAML.Document.Parsed) {
this.tree = getTree(yamlObj)
Expand All @@ -27,7 +28,35 @@ export class Settings {
this.customs.value = YAML.parseDocument(yamlStr)
}

setEnvs(envs: string[]) {
const envsObj: { [key: string]: string } = {}
envs.forEach((env) => {
const [key, value] = env.split('=')
envsObj[key] = value
})
this.envs.value = envsObj
}

getEnv(key: string) {
return this.envs.value?.[key] || null
}

getEnvByPath(path: string) {
// replace `.` to `_` and uppercase
// replace `ATK_TRUSTED_DOMAINS_0` to `ATK_TRUSTED_DOMAINS`
// replace `ATK_ADMIN_USERS_0_NAME` to `ATK_ADMIN_USERS`
return this.getEnv(
'ATK_' +
path
.replace(/\./g, '_')
.toUpperCase()
.replace(/(_\d+?_\w+|_\d+)$/, ''),
)
}

getCustom(path: string) {
const env = this.getEnvByPath(path)
if (env) return env
return this.customs.value?.getIn(path.split('.')) as any
}

Expand Down
1 change: 1 addition & 0 deletions ui/artalk-sidebar/src/pages/settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ onMounted(() => {
tree.value = settings.init(yamlObj).getTree()
// console.log(tree.value)
settings.get().setCustoms(custom.data.yaml)
settings.get().setEnvs(custom.data.envs)
})
})

Expand Down
7 changes: 4 additions & 3 deletions ui/artalk/src/api/v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,7 @@ export interface HandlerResponsePageUpdate {
}

export interface HandlerResponseSettingGet {
envs: string[]
yaml: string
}

Expand Down Expand Up @@ -697,8 +698,8 @@ export class HttpClient<SecurityDataType = unknown> {
property instanceof Blob
? property
: typeof property === 'object' && property !== null
? JSON.stringify(property)
: `${property}`,
? JSON.stringify(property)
: `${property}`,
)
return formData
}, new FormData()),
Expand Down Expand Up @@ -774,7 +775,7 @@ export class HttpClient<SecurityDataType = unknown> {
body: typeof body === 'undefined' || body === null ? null : payloadFormatter(body),
},
).then(async (response) => {
const r = response as HttpResponse<T, E>
const r = response.clone() as HttpResponse<T, E>
r.data = null as unknown as T
r.error = null as unknown as E

Expand Down
Loading