feat: Script within the container support the selection of users (#9170)

Refs #8766
This commit is contained in:
ssongliu 2025-06-18 16:46:58 +08:00 committed by GitHub
parent 692c77eb1a
commit 9f0338382f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 80 additions and 11 deletions

View file

@ -35,6 +35,29 @@ func (b *BaseApi) SearchContainer(c *gin.Context) {
})
}
// @Tags Container
// @Summary Load container users
// @Accept json
// @Param request body dto.OperationWithName true "request"
// @Produce json
// @Success 200 {array} string
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /containers/users [post]
func (b *BaseApi) LoadContainerUsers(c *gin.Context) {
var req dto.OperationWithName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
list, err := containerService.LoadUsers(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags Container
// @Summary List containers
// @Accept json

View file

@ -83,6 +83,8 @@ type IContainerService interface {
ComposeUpdate(req dto.ComposeUpdate) error
Prune(req dto.ContainerPrune) (dto.ContainerPruneReport, error)
LoadUsers(req dto.OperationWithName) ([]string, error)
StreamLogs(ctx *gin.Context, params dto.StreamLog)
}
@ -1083,6 +1085,21 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error
return &data, nil
}
func (u *ContainerService) LoadUsers(req dto.OperationWithName) ([]string, error) {
std, err := cmd.RunDefaultWithStdoutBashCf("docker exec %s cat /etc/passwd", req.Name)
if err != nil {
return nil, err
}
var users []string
lines := strings.Split(string(std), "\n")
for _, line := range lines {
if strings.Contains(line, ":") {
users = append(users, strings.Split(line, ":")[0])
}
}
return users, nil
}
func stringsToMap(list []string) map[string]string {
var labelMap = make(map[string]string)
for _, label := range list {

View file

@ -118,11 +118,21 @@ func (u *CronjobService) handleShell(cronjob model.Cronjob, taskItem *task.Task)
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem))
taskItem.AddSubTaskWithOps(i18n.GetWithName("HandleShell", cronjob.Name), func(t *task.Task) error {
if len(cronjob.ContainerName) != 0 {
scriptItem := cronjob.Script
if cronjob.ScriptMode == "select" {
scriptItem = path.Join("/tmp", path.Base(cronjob.Script))
if err := cmdMgr.Run("docker", "cp", cronjob.Script, cronjob.ContainerName+":"+scriptItem); err != nil {
return err
}
}
command := "sh"
if len(cronjob.Command) != 0 {
command = cronjob.Command
}
return cmdMgr.Run("docker", "exec", cronjob.ContainerName, command, "-c", strings.ReplaceAll(cronjob.Script, "\"", "\\\""))
if len(cronjob.User) != 0 {
return cmdMgr.Run("docker", "exec", "-u", cronjob.User, cronjob.ContainerName, command, "-c", strings.ReplaceAll(scriptItem, "\"", "\\\""))
}
return cmdMgr.Run("docker", "exec", cronjob.ContainerName, command, "-c", strings.ReplaceAll(scriptItem, "\"", "\\\""))
}
if len(cronjob.Executor) == 0 {
cronjob.Executor = "bash"

View file

@ -33,6 +33,8 @@ func (s *ContainerRouter) InitRouter(Router *gin.RouterGroup) {
baRouter.POST("/operate", baseApi.ContainerOperation)
baRouter.POST("/prune", baseApi.ContainerPrune)
baRouter.POST("/users", baseApi.LoadContainerUsers)
baRouter.GET("/repo", baseApi.ListRepo)
baRouter.POST("/repo/status", baseApi.CheckRepoStatus)
baRouter.POST("/repo/search", baseApi.SearchRepo)

View file

@ -9,6 +9,9 @@ export const searchContainer = (params: Container.ContainerSearch) => {
export const listContainer = () => {
return http.post<Array<string>>(`/containers/list`, {});
};
export const loadContainerUsers = (name: string) => {
return http.post<Array<string>>(`/containers/users`, { name: name });
};
export const loadContainerStatus = () => {
return http.get<Container.ContainerStatus>(`/containers/status`);
};

View file

@ -343,7 +343,7 @@
<el-row :gutter="20">
<el-col :span="24" v-if="hasScript()">
<el-form-item>
<el-checkbox v-model="form.inContainer">
<el-checkbox @change="loadUserOptions(false)" v-model="form.inContainer">
{{ $t('cronjob.containerCheckBox') }}
</el-checkbox>
</el-form-item>
@ -351,7 +351,10 @@
<el-row :gutter="20" v-if="form.inContainer">
<LayoutCol>
<el-form-item :label="$t('cronjob.containerName')" prop="containerName">
<el-select v-model="form.containerName">
<el-select
@change="loadUserOptions(false)"
v-model="form.containerName"
>
<el-option
v-for="item in containerOptions"
:key="item"
@ -363,7 +366,7 @@
</LayoutCol>
<LayoutCol>
<el-form-item
:label="$t('container.command') + 123"
:label="$t('container.command')"
prop="command"
:rules="Rules.requiredInput"
>
@ -390,7 +393,7 @@
</el-form-item>
</LayoutCol>
</el-row>
<el-row :gutter="20" v-if="!form.inContainer">
<el-row :gutter="20">
<LayoutCol>
<el-form-item :label="$t('commons.table.user')" prop="user">
<el-select filterable v-model="form.user">
@ -400,7 +403,7 @@
</el-select>
</el-form-item>
</LayoutCol>
<LayoutCol>
<LayoutCol v-if="!form.inContainer">
<el-form-item :label="$t('cronjob.executor')" prop="executor">
<el-checkbox border v-model="form.isCustom">
{{ $t('container.custom') }}
@ -693,6 +696,7 @@ import {
weekOptions,
} from '../helper';
import { loadUsers } from '@/api/modules/toolbox';
import { loadContainerUsers } from '@/api/modules/container';
import { storeToRefs } from 'pinia';
import { GlobalStore } from '@/store';
import LicenseImport from '@/components/license-import/index.vue';
@ -784,13 +788,13 @@ const search = async () => {
form.scriptMode = res.data.scriptMode;
form.containerName = res.data.containerName;
form.user = res.data.user;
if (form.containerName.length !== 0) {
form.inContainer = true;
form.command = res.data.command || 'sh';
form.isCustom = form.command !== 'sh' && form.command !== 'bash' && form.command !== 'ash';
} else {
form.executor = res.data.executor || 'bash';
form.user = res.data.user;
form.isCustom =
form.executor !== 'sh' &&
form.executor !== 'bash' &&
@ -846,7 +850,7 @@ const search = async () => {
}
loadBackups();
loadAppInstalls();
loadShellUsers();
loadUserOptions(true);
loadWebsites();
loadContainers();
loadScripts();
@ -1205,9 +1209,19 @@ const changeAccount = async () => {
}
};
const loadShellUsers = async () => {
const res = await loadUsers();
userOptions.value = res.data || [];
const loadUserOptions = async (isInit: boolean) => {
if (!form.inContainer) {
const res = await loadUsers();
userOptions.value = res.data || [];
} else {
if (!isInit) {
form.user = '';
}
if (form.containerName) {
const res = await loadContainerUsers(form.containerName);
userOptions.value = res.data || [];
}
}
};
const loadAppInstalls = async () => {