开发容器分页查询:
* 用户设置-> 开发容器列表 * GET /api/devcontainer/user?page=1&page_size=10
This commit is contained in:
@@ -46,6 +46,10 @@ CHARSET_COLLATION = utf8mb4_bin
|
||||
|
||||
[cors]
|
||||
CONTENT_SECURITY_POLICY = default-src 'self' data: 'unsafe-inline' https://mp.weixin.qq.com; img-src * data:
|
||||
|
||||
[ui.admin]
|
||||
;; Dev Container 分页参数(每页展示 DevContainer 个数),若未指定,默认值 50
|
||||
DEV_CONTAINERS_PAGING_NUM = 50
|
||||
```
|
||||
|
||||
正式部署单机版
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
)
|
||||
|
||||
// DevstarDevcontainer devContainer 与 代码仓库一对一关联表
|
||||
// DevstarDevcontainer devContainer 关联 代码仓库 和 用户
|
||||
//
|
||||
// 遵循gonic规则映射数据库表 `devstar_devcontainer`,各字段注解见 https://xorm.io/docs/chapter-02/4.columns/
|
||||
type DevstarDevcontainer struct {
|
||||
@@ -15,7 +15,8 @@ type DevstarDevcontainer struct {
|
||||
DevcontainerUsername string `xorm:"VARCHAR(32) charset=utf8mb4 collate=utf8mb4_bin NOT NULL 'devcontainer_username' comment('SSH Username')"`
|
||||
DevcontainerPassword string `xorm:"VARCHAR(32) charset=utf8mb4 collate=utf8mb4_bin NOT NULL 'devcontainer_password' comment('SSH Password')"`
|
||||
DevcontainerWorkDir string `xorm:"VARCHAR(256) charset=utf8mb4 collate=utf8mb4_bin NOT NULL 'devcontainer_work_dir' comment('SSH 工作路径,典型值 ~/${project_name},256字节以内')"`
|
||||
RepoId int64 `xorm:"BIGINT UNIQUE NOT NULL 'repo_id' comment('repository表主键')"`
|
||||
RepoId int64 `xorm:"BIGINT NOT NULL 'repo_id' comment('repository表主键')"`
|
||||
UserId int64 `xorm:"BIGINT NOT NULL 'user_id' comment('user表主键')"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package devstar_devcontainer
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
)
|
||||
|
||||
// DevstarDevcontainerUser devContainer 与 用户 多对多关联表
|
||||
//
|
||||
// 遵循gonic规则映射数据库表 `devstar_devcontainer_user`,各字段注解见 https://xorm.io/docs/chapter-02/4.columns/
|
||||
type DevstarDevcontainerUser struct {
|
||||
Id int64 `xorm:"BIGINT pk NOT NULL autoincr 'id' comment('主键')"`
|
||||
DevcontainerId int64 `xorm:"BIGINT NOT NULL 'devcontainer_id' comment('devstar_devcontainer表主键')"`
|
||||
UserId int64 `xorm:"BIGINT NOT NULL 'user_id' comment('user表主键')"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
db.RegisterModel(new(DevstarDevcontainerUser))
|
||||
}
|
||||
@@ -13,16 +13,11 @@ func InitializeDevContainerDbTables(x *xorm.Engine) error {
|
||||
|
||||
var err error
|
||||
|
||||
// 1. 初始化 devContainer 与 Repository 一对一关系表
|
||||
// 1. 初始化 devContainer 表
|
||||
if err = addDBDevStarDevContainer(x); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. 初始化 devContainer 与 user 多对多关系表
|
||||
if err = addDBDevStarDevContainerUser(x); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -39,17 +34,3 @@ func addDBDevStarDevContainer(x *xorm.Engine) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// addDBDevStarDevContainerUser 2. 初始化 devContainer 与 user 多对多关系表
|
||||
func addDBDevStarDevContainerUser(x *xorm.Engine) error {
|
||||
|
||||
err := x.Sync(new(devcontainer_models.DevstarDevcontainerUser))
|
||||
if err != nil {
|
||||
return ErrMigrateDevstarDatabase{
|
||||
Step: "create table 'devstar_devcontainer_user'",
|
||||
Message: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -60,6 +60,8 @@ var UI = struct {
|
||||
RepoPagingNum int
|
||||
NoticePagingNum int
|
||||
OrgPagingNum int
|
||||
// DevContainersPagingNum Dev Container 分页参数,每页展示 Dev Container 数量
|
||||
DevContainersPagingNum int
|
||||
} `ini:"ui.admin"`
|
||||
User struct {
|
||||
RepoPagingNum int
|
||||
@@ -118,11 +120,15 @@ var UI = struct {
|
||||
RepoPagingNum int
|
||||
NoticePagingNum int
|
||||
OrgPagingNum int
|
||||
// DevContainersPagingNum Dev Container 分页参数,每页展示 Dev Container 数量
|
||||
DevContainersPagingNum int
|
||||
}{
|
||||
UserPagingNum: 50,
|
||||
RepoPagingNum: 50,
|
||||
NoticePagingNum: 25,
|
||||
OrgPagingNum: 50,
|
||||
// DevContainersPagingNum Dev Container 分页参数,每页展示 Dev Container 数量
|
||||
DevContainersPagingNum: 50,
|
||||
},
|
||||
User: struct {
|
||||
RepoPagingNum int
|
||||
|
||||
@@ -691,6 +691,8 @@ social = Social Accounts
|
||||
applications = Applications
|
||||
orgs = Manage Organizations
|
||||
repos = Repositories
|
||||
dev_containers_list = Dev Containers
|
||||
dev_containers_none = You do not own any dev containers.
|
||||
delete = Delete Account
|
||||
twofa = Two-Factor Authentication (TOTP)
|
||||
account_link = Linked Accounts
|
||||
@@ -1010,6 +1012,7 @@ visibility.private = Private
|
||||
visibility.private_tooltip = Visible only to members of organizations you have joined
|
||||
|
||||
[repo]
|
||||
dev_container = Dev Container
|
||||
new_repo_helper = A repository contains all project files, including revision history. Already hosting one elsewhere? <a href="%s">Migrate repository.</a>
|
||||
owner = Owner
|
||||
owner_helper = Some organizations may not show up in the dropdown due to a maximum repository count limit.
|
||||
|
||||
@@ -690,6 +690,8 @@ social=社交帐号绑定
|
||||
applications=应用
|
||||
orgs=管理组织
|
||||
repos=仓库列表
|
||||
dev_containers_list = 开发容器列表
|
||||
dev_containers_none = 你没有任何开发容器。
|
||||
delete=删除帐户
|
||||
twofa=两步验证
|
||||
account_link=已绑定帐户
|
||||
@@ -1009,6 +1011,7 @@ visibility.private=私有
|
||||
visibility.private_tooltip=仅对您已加入的组织的成员可见。
|
||||
|
||||
[repo]
|
||||
dev_container = 开发容器
|
||||
new_repo_helper=代码仓库包含了所有的项目文件,包括版本历史记录。已经在其他地方托管了?<a href="%s">迁移仓库。</a>
|
||||
owner=拥有者
|
||||
owner_helper=由于最大仓库数量限制,一些组织可能不会显示在下拉列表中。
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package devcontainer
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
Result "code.gitea.io/gitea/routers/entity"
|
||||
DevcontainersVO "code.gitea.io/gitea/routers/web/devcontainer/vo"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
@@ -9,8 +11,10 @@ import (
|
||||
|
||||
// ListUserDevcontainers 枚举已登录用户所有的 devContainers,但目前测试阶段只返回 mock数据
|
||||
//
|
||||
// GET /api/devcontainer/user
|
||||
// 请求输入参数:无(已在ctx隐含)
|
||||
// GET /api/devcontainer/user
|
||||
// 请求输入参数:
|
||||
// - page: 当前第几页(默认第1页),从1开始计数
|
||||
// - pageSize: 每页记录数(默认值 setting.UI.Admin.DevContainersPagingNum)
|
||||
func ListUserDevcontainers(ctx *context.Context) {
|
||||
|
||||
// 1. 检查用户登录状态,若未登录则返回未授权错误
|
||||
@@ -20,17 +24,28 @@ func ListUserDevcontainers(ctx *context.Context) {
|
||||
}
|
||||
|
||||
// 2. 查询数据库 当前登录用户拥有写入权限的仓库
|
||||
userId := ctx.Doer.ID
|
||||
userDevcontainersItemVOList, _ := devstar_devcontainer_service.GetUserDevcontainerListByUserId(ctx, userId)
|
||||
userPage := ctx.FormInt("page")
|
||||
if userPage <= 0 {
|
||||
userPage = 1
|
||||
}
|
||||
userPageSize := ctx.FormInt("page_size")
|
||||
if userPageSize <= 0 || userPageSize > setting.UI.Admin.DevContainersPagingNum {
|
||||
userPageSize = setting.UI.Admin.DevContainersPagingNum
|
||||
}
|
||||
opts := &DevcontainersVO.SearchDevcontainerItemVoOptions{
|
||||
Actor: ctx.Doer,
|
||||
PaginationOptions: db.ListOptions{
|
||||
Page: userPage,
|
||||
PageSize: userPageSize,
|
||||
},
|
||||
}
|
||||
userDevcontainersVO, _ := devstar_devcontainer_service.GetUserDevcontainersList(ctx, opts)
|
||||
|
||||
// 3. 封装VO
|
||||
resultListUserDevcontainersVO := Result.ResultType{
|
||||
Code: Result.RespSuccess.Code,
|
||||
Msg: Result.RespSuccess.Msg,
|
||||
Data: DevcontainersVO.ListUserDevcontainersVO{
|
||||
UserID: userId,
|
||||
DevContainers: userDevcontainersItemVOList,
|
||||
},
|
||||
Data: userDevcontainersVO,
|
||||
}
|
||||
|
||||
// 4. JSON序列化,写入输出流
|
||||
|
||||
@@ -1,21 +1,38 @@
|
||||
package vo
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
)
|
||||
|
||||
// ListUserDevcontainersVO 封装用户所有 devContainer 信息列表
|
||||
type ListUserDevcontainersVO struct {
|
||||
UserID int64 `json:"userId"`
|
||||
Username string `json:"username"`
|
||||
DevContainers []DevContainerItemVO `json:"devContainers"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
PageTotalNum int `json:"pageTotalNum"`
|
||||
ItemTotalNum int64 `json:"itemTotalNum"`
|
||||
}
|
||||
|
||||
// DevContainerItemVO 描述每一项 devContainer
|
||||
type DevContainerItemVO struct {
|
||||
RepoId int64 `json:"repoId" xorm:"RepoId"`
|
||||
RepoName string `json:"repoName" xorm:"RepoName"`
|
||||
RepoDescription string `json:"repoDescription" xorm:"RepoDescription"`
|
||||
DevContainerId int64 `json:"devContainerId" xorm:"DevContainerId"`
|
||||
DevContainerName string `json:"devContainerName" xorm:"DevContainerName"`
|
||||
DevContainerHost string `json:"devContainerHost" xorm:"DevContainerHost"`
|
||||
DevContainerPort uint16 `json:"devContainerPort" xorm:"DevContainerPort"`
|
||||
DevContainerUsername string `json:"devContainerUsername" xorm:"DevContainerUsername"`
|
||||
DevContainerPassword string `json:"devContainerPassword" xorm:"DevContainerPassword"`
|
||||
DevContainerWorkDir string `json:"devContainerWorkDir" xorm:"DevContainerWorkDir"`
|
||||
DevContainerId int64 `json:"devContainerId" xorm:"devcontainer_id"`
|
||||
DevContainerName string `json:"devContainerName" xorm:"devcontainer_name"`
|
||||
DevContainerHost string `json:"devContainerHost" xorm:"devcontainer_host"`
|
||||
DevContainerPort uint16 `json:"devContainerPort" xorm:"devcontainer_port"`
|
||||
DevContainerUsername string `json:"devContainerUsername" xorm:"devcontainer_username"`
|
||||
DevContainerPassword string `json:"devContainerPassword" xorm:"devcontainer_password"`
|
||||
DevContainerWorkDir string `json:"devContainerWorkDir" xorm:"devcontainer_work_dir"`
|
||||
RepoId int64 `json:"repoId" xorm:"repo_id"`
|
||||
RepoName string `json:"repoName" xorm:"repo_name"`
|
||||
RepoDescription string `json:"repoDescription" xorm:"repo_description"`
|
||||
}
|
||||
|
||||
// SearchDevcontainerItemVoOptions 查询条件
|
||||
type SearchDevcontainerItemVoOptions struct {
|
||||
PaginationOptions db.ListOptions // XORM 分页查询条件
|
||||
Actor *user_model.User // Dev Container 所属用户
|
||||
OrderBy db.SearchOrderBy // 排序
|
||||
}
|
||||
|
||||
59
routers/web/user/setting/dev_containers_list.go
Normal file
59
routers/web/user/setting/dev_containers_list.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package setting
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
DevcontainersVO "code.gitea.io/gitea/routers/web/devcontainer/vo"
|
||||
gitea_context "code.gitea.io/gitea/services/context"
|
||||
devstar_devcontainer_service "code.gitea.io/gitea/services/devstar_devcontainer"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
tplSettingsDevContainers base.TplName = "user/settings/dev_containers_list"
|
||||
)
|
||||
|
||||
// ListDevContainers 展示用户所有开发容器
|
||||
func ListDevContainers(ctx *gitea_context.Context) {
|
||||
|
||||
// 1. 检查页面是否登录,若未登录则重定向到登录页
|
||||
ctxUser := ctx.Doer
|
||||
if ctxUser == nil {
|
||||
ctx.Redirect(setting.AppSubURL + string(setting.LandingPageLogin))
|
||||
return
|
||||
}
|
||||
|
||||
// 2. 设置前端展示元数据数据
|
||||
ctx.Data["Title"] = ctx.Tr("settings.dev_containers_list")
|
||||
ctx.Data["PageIsSettingsDevContainersList"] = true
|
||||
|
||||
// 3. 分页查询
|
||||
paginationOptions := db.ListOptions{
|
||||
PageSize: setting.UI.Admin.DevContainersPagingNum, // 参考 app.ini 配置文件 ui.admin.DEV_CONTAINERS_PAGING_NUM,默认50
|
||||
Page: ctx.FormInt("page"), // URL参数起始页码
|
||||
}
|
||||
if paginationOptions.Page <= 0 {
|
||||
paginationOptions.Page = 1
|
||||
}
|
||||
opts := &DevcontainersVO.SearchDevcontainerItemVoOptions{
|
||||
PaginationOptions: paginationOptions,
|
||||
Actor: ctxUser,
|
||||
}
|
||||
userDevcontainersListVO, err := devstar_devcontainer_service.GetUserDevcontainersList(ctx, opts)
|
||||
if err != nil {
|
||||
ctx.ServerError("ListDevContainers", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["DevContainers"] = userDevcontainersListVO.DevContainers
|
||||
ctx.Data["ContextUser"] = ctxUser
|
||||
itemTotalNum := userDevcontainersListVO.ItemTotalNum
|
||||
pageTotalNum := userDevcontainersListVO.PageTotalNum
|
||||
pageSize := userDevcontainersListVO.PageSize
|
||||
currentPage := userDevcontainersListVO.Page
|
||||
pager := gitea_context.NewPagination(int(itemTotalNum), pageSize, currentPage, pageTotalNum)
|
||||
pager.SetDefaultParams(ctx)
|
||||
ctx.Data["Page"] = pager
|
||||
ctx.HTML(http.StatusOK, tplSettingsDevContainers)
|
||||
return
|
||||
}
|
||||
@@ -651,6 +651,13 @@ func registerRoutes(m *web.Router) {
|
||||
m.Get("/repos", user_setting.Repos)
|
||||
m.Post("/repos/unadopted", user_setting.AdoptOrDeleteRepository)
|
||||
|
||||
// ***** START: /user/settings/dev-containers *****
|
||||
// 管理用户级别开发容器列表,现只做查看,如需控制/修改,需要跳转到对应仓库开发容器页面
|
||||
m.Group("/dev-containers-list", func() {
|
||||
m.Get("", user_setting.ListDevContainers)
|
||||
})
|
||||
// ***** END: /user/settings/dev-containers *****
|
||||
|
||||
m.Group("/hooks", func() {
|
||||
m.Get("", user_setting.Webhooks)
|
||||
m.Post("/delete", user_setting.DeleteWebhook)
|
||||
|
||||
@@ -3,52 +3,131 @@ package devstar_devcontainer
|
||||
import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/devstar_devcontainer"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
DevcontainersVO "code.gitea.io/gitea/routers/web/devcontainer/vo"
|
||||
"context"
|
||||
"fmt"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// GetUserDevcontainerListByUserId 根据 userId 查询名下 devContainer 列表
|
||||
func GetUserDevcontainerListByUserId(ctx context.Context, userId int64) ([]DevcontainersVO.DevContainerItemVO, error) {
|
||||
// GetUserDevcontainersList 根据 userId 查询名下 devContainer 列表
|
||||
func GetUserDevcontainersList(ctx context.Context, opts *DevcontainersVO.SearchDevcontainerItemVoOptions) (DevcontainersVO.ListUserDevcontainersVO, error) {
|
||||
|
||||
var devcontainerItemVOList []DevcontainersVO.DevContainerItemVO
|
||||
/*
|
||||
SELECT
|
||||
devstar_devcontainer.repo_id AS RepoId,
|
||||
repository.name AS RepoName,
|
||||
repository.description AS RepoDescription,
|
||||
devstar_devcontainer.id AS DevContainerId,
|
||||
devstar_devcontainer.name AS DevContainerName,
|
||||
devstar_devcontainer.devcontainer_host AS DevContainerHost,
|
||||
devstar_devcontainer.devcontainer_port AS DevContainerPort,
|
||||
devstar_devcontainer.devcontainer_username AS DevContainerUsername,
|
||||
devstar_devcontainer.devcontainer_password AS DevContainerPassword,
|
||||
devstar_devcontainer.devcontainer_work_dir AS DevContainerWorkDir
|
||||
FROM devstar_devcontainer
|
||||
LEFT JOIN repository ON devstar_devcontainer.repo_id = repository.id
|
||||
WHERE devstar_devcontainer.id IN (
|
||||
SELECT devcontainer_id
|
||||
FROM devstar_devcontainer_user
|
||||
WHERE user_id = ?
|
||||
);
|
||||
*/
|
||||
err := db.GetEngine(ctx).
|
||||
Table("devstar_devcontainer").
|
||||
Select("devstar_devcontainer.repo_id AS RepoId, repository.name AS RepoName, repository.description AS RepoDescription, devstar_devcontainer.id AS DevContainerId, devstar_devcontainer.name AS DevContainerName, devstar_devcontainer.devcontainer_host AS DevContainerHost, devstar_devcontainer.devcontainer_port AS DevContainerPort, devstar_devcontainer.devcontainer_username AS DevContainerUsername,devstar_devcontainer.devcontainer_password AS DevContainerPassword, devstar_devcontainer.devcontainer_work_dir AS DevContainerWorkDir").
|
||||
Join("LEFT", "repository", "devstar_devcontainer.repo_id = repository.id").
|
||||
Where("devstar_devcontainer.id IN (SELECT devcontainer_id FROM devstar_devcontainer_user WHERE user_id = ?)", userId).
|
||||
Find(&devcontainerItemVOList)
|
||||
|
||||
if devcontainerItemVOList == nil {
|
||||
devcontainerItemVOList = []DevcontainersVO.DevContainerItemVO{}
|
||||
// 0. 构造异常返回时的空数据
|
||||
var resultDevContainerListVO = DevcontainersVO.ListUserDevcontainersVO{
|
||||
Page: 0,
|
||||
PageSize: setting.UI.Admin.DevContainersPagingNum,
|
||||
PageTotalNum: 0,
|
||||
ItemTotalNum: 0,
|
||||
DevContainers: []DevcontainersVO.DevContainerItemVO{},
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return devcontainerItemVOList,
|
||||
// 1. 查询参数预处理
|
||||
if opts == nil || opts.Actor == nil {
|
||||
return resultDevContainerListVO, devstar_devcontainer.ErrFailedToOperateDevstarDevcontainerDB{
|
||||
Action: "construct query condition for devContainer user list",
|
||||
Message: "invalid search condition",
|
||||
}
|
||||
}
|
||||
resultDevContainerListVO.UserID = opts.Actor.ID
|
||||
resultDevContainerListVO.Username = opts.Actor.Name
|
||||
if len(opts.OrderBy) == 0 {
|
||||
opts.OrderBy = "devcontainer_id DESC"
|
||||
}
|
||||
opts.PaginationOptions.ListAll = false // 强制使用分页查询,禁止一次性列举所有 devContainers
|
||||
if opts.PaginationOptions.Page <= 0 { // 未指定页码/无效页码:查询第 1 页
|
||||
opts.PaginationOptions.Page = 1
|
||||
}
|
||||
if opts.PaginationOptions.PageSize <= 0 || opts.PaginationOptions.PageSize > setting.UI.Admin.DevContainersPagingNum {
|
||||
opts.PaginationOptions.PageSize = setting.UI.Admin.DevContainersPagingNum // /无效页面大小/超过每页最大限制:自动调整到系统最大开发容器页面大小
|
||||
}
|
||||
resultDevContainerListVO.Page = opts.PaginationOptions.Page
|
||||
resultDevContainerListVO.PageSize = opts.PaginationOptions.PageSize
|
||||
|
||||
// 2. SQL 条件构建
|
||||
sqlCondition := builder.NewCond()
|
||||
sqlCondition = sqlCondition.And(builder.Eq{"user_id": opts.Actor.ID})
|
||||
|
||||
// 3. 开启数据库事务,进行分页查询,同时封装 VO
|
||||
errDbTransaction := db.WithTx(ctx, func(ctx context.Context) error {
|
||||
sess := db.GetEngine(ctx)
|
||||
var err error
|
||||
|
||||
// 2.1 查询符合条件的记录总数
|
||||
/*
|
||||
SELECT COUNT(*)
|
||||
FROM devstar_devcontainer
|
||||
LEFT JOIN repository ON devstar_devcontainer.repo_id = repository.id
|
||||
WHERE devstar_devcontainer.user_id = #{opts.Actor.ID}
|
||||
*/
|
||||
resultDevContainerListVO.ItemTotalNum, err = sess.
|
||||
Table("devstar_devcontainer").
|
||||
Join("LEFT", "repository", "devstar_devcontainer.repo_id = repository.id").
|
||||
Where(sqlCondition).
|
||||
Count()
|
||||
if err != nil {
|
||||
return devstar_devcontainer.ErrFailedToOperateDevstarDevcontainerDB{
|
||||
Action: "count devContainer item numbers",
|
||||
Message: err.Error(),
|
||||
}
|
||||
}
|
||||
if resultDevContainerListVO.ItemTotalNum == 0 {
|
||||
return nil // 无符合记录,返回 nil 提交/结束 事务
|
||||
}
|
||||
|
||||
// 2.3 计算分页参数
|
||||
totalRecords := resultDevContainerListVO.ItemTotalNum
|
||||
pageSize := int64(resultDevContainerListVO.PageSize)
|
||||
resultDevContainerListVO.PageTotalNum += int(totalRecords/pageSize) + 1
|
||||
|
||||
// 2.3 数据库带条件分页查询
|
||||
resultDevContainerListVO.DevContainers = make([]DevcontainersVO.DevContainerItemVO, 0, opts.PaginationOptions.PageSize)
|
||||
/*
|
||||
SELECT
|
||||
devstar_devcontainer.id AS devcontainer_id,
|
||||
devstar_devcontainer.name AS devcontainer_name,
|
||||
devstar_devcontainer.devcontainer_host AS devcontainer_host,
|
||||
devstar_devcontainer.devcontainer_port AS devcontainer_port,
|
||||
devstar_devcontainer.devcontainer_username AS devcontainer_username,
|
||||
devstar_devcontainer.devcontainer_password AS devcontainer_password,
|
||||
devstar_devcontainer.devcontainer_work_dir AS devcontainer_work_dir,
|
||||
repository.id AS repo_id,
|
||||
repository.name AS repo_name,
|
||||
repository.description AS repo_description
|
||||
FROM devstar_devcontainer
|
||||
LEFT JOIN repository ON devstar_devcontainer.repo_id = repository.id
|
||||
WHERE devstar_devcontainer.user_id = #{opts.Actor.ID}
|
||||
ORDER BY #{opts.OrderBy.String()}
|
||||
LIMIT #{opts.PaginationOptions.PageSize}
|
||||
OFFSET ( (#{opts.PaginationOptions.Page} - 1) * #{opts.PaginationOptions.PageSize});
|
||||
*/
|
||||
sess = sess.
|
||||
Table("devstar_devcontainer").
|
||||
Select("devstar_devcontainer.repo_id AS repo_id, repository.name AS repo_name, repository.description AS repo_description, devstar_devcontainer.id AS devcontainer_id, devstar_devcontainer.name AS devcontainer_name, devstar_devcontainer.devcontainer_host AS devcontainer_host, devstar_devcontainer.devcontainer_port AS devcontainer_port, devstar_devcontainer.devcontainer_username AS devcontainer_username, devstar_devcontainer.devcontainer_password AS devcontainer_password, devstar_devcontainer.devcontainer_work_dir AS devcontainer_work_dir").
|
||||
Join("LEFT", "repository", "devstar_devcontainer.repo_id = repository.id").
|
||||
Where(sqlCondition).
|
||||
OrderBy(opts.OrderBy.String())
|
||||
err = db.SetSessionPagination(sess, &opts.PaginationOptions).
|
||||
Find(&resultDevContainerListVO.DevContainers)
|
||||
if err != nil {
|
||||
// 查询出错,返回错误信息,结束事务
|
||||
return devstar_devcontainer.ErrFailedToOperateDevstarDevcontainerDB{
|
||||
Action: fmt.Sprintf("list devContainer for user '%v' at page %v", opts.Actor.Name, opts.PaginationOptions.Page),
|
||||
Message: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
// 2.x 查询完成,返回 nil 自动提交事务
|
||||
return nil
|
||||
})
|
||||
|
||||
if errDbTransaction != nil {
|
||||
return resultDevContainerListVO,
|
||||
devstar_devcontainer.ErrFailedToOperateDevstarDevcontainerDB{
|
||||
Action: "query user DevContainer List",
|
||||
Message: err.Error(),
|
||||
Message: errDbTransaction.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
return devcontainerItemVOList, nil
|
||||
return resultDevContainerListVO, nil
|
||||
}
|
||||
|
||||
@@ -138,6 +138,13 @@
|
||||
</a>
|
||||
{{end}}
|
||||
|
||||
<!-- 定义tab: DevContainer List -->
|
||||
{{if .Permission.CanWrite ctx.Consts.RepoUnitTypeCode}}
|
||||
<a class="{{if .PageIsRepoDevcontainerDetails}}active {{end}}item" href="{{.RepoLink}}/dev-container">
|
||||
{{svg "octicon-container"}} {{ctx.Locale.Tr "repo.dev_container"}}
|
||||
</a>
|
||||
{{end}}
|
||||
|
||||
{{if .Permission.CanRead ctx.Consts.RepoUnitTypeIssues}}
|
||||
<a class="{{if .PageIsIssueList}}active {{end}}item" href="{{.RepoLink}}/issues">
|
||||
{{svg "octicon-issue-opened"}} {{ctx.Locale.Tr "repo.issues"}}
|
||||
@@ -234,3 +241,10 @@
|
||||
</div>
|
||||
<div class="ui tabs divider"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
console.log(`当用户点击 DevContainer 按钮,跳转到 {{.RepoLink}}/dev-container`)
|
||||
})
|
||||
</script>
|
||||
|
||||
38
templates/user/settings/dev_containers_list.tmpl
Normal file
38
templates/user/settings/dev_containers_list.tmpl
Normal file
@@ -0,0 +1,38 @@
|
||||
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings repos")}}
|
||||
<div class="user-setting-content">
|
||||
<h4 class="ui top attached header">
|
||||
{{ctx.Locale.Tr "settings.dev_containers_list"}}
|
||||
</h4>
|
||||
<div class="ui attached segment">
|
||||
{{if .DevContainers}}
|
||||
<div class="ui middle aligned divided list">
|
||||
{{range .DevContainers}}
|
||||
<div class="item">
|
||||
<div class="content flex-text-block">
|
||||
<span class="text gold icon">{{svg "octicon-container"}}</span>
|
||||
<a class="name"
|
||||
href="{{AppSubUrl}}/{{$.ContextUser.Name}}/{{.RepoName}}/dev-container"
|
||||
title="ssh://{{.DevContainerUsername}}@{{.DevContainerHost}}:{{.DevContainerPort}}">
|
||||
{{.DevContainerName}}
|
||||
</a>
|
||||
</div>
|
||||
<span>
|
||||
{{svg "octicon-repo"}} {{ctx.Locale.Tr "repo.repo_name"}}:
|
||||
<a class="name"
|
||||
href="{{AppSubUrl}}/{{$.ContextUser.Name}}/{{.RepoName}}"
|
||||
title="{{.RepoDescription}}">
|
||||
{{.RepoName}}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{template "base/paginate" .}}
|
||||
{{else}}
|
||||
<div class="item">
|
||||
{{ctx.Locale.Tr "settings.dev_containers_none"}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{template "user/settings/layout_footer" .}}
|
||||
@@ -54,5 +54,8 @@
|
||||
<a class="{{if .PageIsSettingsRepos}}active {{end}}item" href="{{AppSubUrl}}/user/settings/repos">
|
||||
{{ctx.Locale.Tr "settings.repos"}}
|
||||
</a>
|
||||
<a class="{{if .PageIsSettingsDevContainersList}}active {{end}}item" href="{{AppSubUrl}}/user/settings/dev-containers-list">
|
||||
{{ctx.Locale.Tr "settings.dev_containers_list"}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user