diff --git a/models/cloudbrain.go b/models/cloudbrain.go index f9c0fb4e0..7a2966f74 100755 --- a/models/cloudbrain.go +++ b/models/cloudbrain.go @@ -72,11 +72,11 @@ type CloudBrainLoginResult struct { type TaskRole struct { Name string `json:"name"` - TaskNumber int8 `json:"taskNumber"` - MinSucceededTaskCount int8 `json:"minSucceededTaskCount"` - MinFailedTaskCount int8 `json:"minFailedTaskCount"` - CPUNumber int8 `json:"cpuNumber"` - GPUNumber int8 `json:"gpuNumber"` + TaskNumber int `json:"taskNumber"` + MinSucceededTaskCount int `json:"minSucceededTaskCount"` + MinFailedTaskCount int `json:"minFailedTaskCount"` + CPUNumber int `json:"cpuNumber"` + GPUNumber int `json:"gpuNumber"` MemoryMB int `json:"memoryMB"` ShmMB int `json:"shmMB"` Command string `json:"command"` @@ -286,6 +286,18 @@ type GpuInfo struct { Queue string `json:"queue"` } +type ResourceSpecs struct { + ResourceSpec []*ResourceSpec `json:"resorce_specs"` +} + +type ResourceSpec struct { + Id int `json:"id"` + CpuNum int `json:"cpu"` + GpuNum int `json:"gpu"` + MemMiB int `json:"memMiB"` + ShareMemMiB int `json:"shareMemMiB"` +} + type CommitImageParams struct { Ip string `json:"ip"` TaskContainerId string `json:"taskContainerId"` diff --git a/models/user.go b/models/user.go index 3dcd2eee9..42e688358 100755 --- a/models/user.go +++ b/models/user.go @@ -976,12 +976,12 @@ func CreateUser(u *User) (err error) { result, err := blockchain.CreateBlockchainAccount() if err != nil { log.Error("createBlockchainAccount failed:", err.Error()) - return err + //return err + } else { + u.PublicKey = result.Payload["publickey"].(string) + u.PrivateKey = result.Payload["privatekey"].(string) } - u.PublicKey = result.Payload["publickey"].(string) - u.PrivateKey = result.Payload["privatekey"].(string) - sess := x.NewSession() defer sess.Close() if err = sess.Begin(); err != nil { diff --git a/models/user_mail.go b/models/user_mail.go old mode 100644 new mode 100755 index af9602e71..7244ec378 --- a/models/user_mail.go +++ b/models/user_mail.go @@ -80,6 +80,18 @@ func GetEmailAddressByID(uid, id int64) (*EmailAddress, error) { return email, nil } +// GetEmailAddressByIDAndEmail gets a user's email address by ID and email +func GetEmailAddressByIDAndEmail(uid int64, emailAddr string) (*EmailAddress, error) { + // User ID is required for security reasons + email := &EmailAddress{UID: uid, Email: emailAddr} + if has, err := x.Get(email); err != nil { + return nil, err + } else if !has { + return nil, nil + } + return email, nil +} + func isEmailActive(e Engine, email string, userID, emailID int64) (bool, error) { if len(email) == 0 { return true, nil diff --git a/modules/auth/cloudbrain.go b/modules/auth/cloudbrain.go index 8325dc063..d598b495c 100755 --- a/modules/auth/cloudbrain.go +++ b/modules/auth/cloudbrain.go @@ -13,6 +13,7 @@ type CreateCloudBrainForm struct { JobType string `form:"job_type" binding:"Required"` BenchmarkCategory string `form:"get_benchmark_category"` GpuType string `form:"gpu_type"` + ResourceSpecId int `form:"resource_spec_id" binding:"Required"` } type CommitImageCloudBrainForm struct { diff --git a/modules/cloudbrain/cloudbrain.go b/modules/cloudbrain/cloudbrain.go index 9fda84ee8..0de1db9a6 100755 --- a/modules/cloudbrain/cloudbrain.go +++ b/modules/cloudbrain/cloudbrain.go @@ -23,12 +23,29 @@ const ( Success = "S000" ) -func GenerateTask(ctx *context.Context, jobName, image, command, uuid, codePath, modelPath, benchmarkPath, snn4imagenetPath, jobType, gpuQueue string) error { +var ( + ResourceSpecs *models.ResourceSpecs +) + +func GenerateTask(ctx *context.Context, jobName, image, command, uuid, codePath, modelPath, benchmarkPath, snn4imagenetPath, jobType, gpuQueue string, resourceSpecId int) error { dataActualPath := setting.Attachment.Minio.RealPath + setting.Attachment.Minio.Bucket + "/" + setting.Attachment.Minio.BasePath + models.AttachmentRelativePath(uuid) + uuid + + var resourceSpec *models.ResourceSpec + for _, spec := range ResourceSpecs.ResourceSpec { + if resourceSpecId == spec.Id { + resourceSpec = spec + } + } + + if resourceSpec == nil { + log.Error("no such resourceSpecId(%d)", resourceSpecId, ctx.Data["MsgID"]) + return errors.New("no such resourceSpec") + } + jobResult, err := CreateJob(jobName, models.CreateJobParams{ JobName: jobName, RetryCount: 1, @@ -40,10 +57,10 @@ func GenerateTask(ctx *context.Context, jobName, image, command, uuid, codePath, TaskNumber: 1, MinSucceededTaskCount: 1, MinFailedTaskCount: 1, - CPUNumber: 2, - GPUNumber: 1, - MemoryMB: 16384, - ShmMB: 8192, + CPUNumber: resourceSpec.CpuNum, + GPUNumber: resourceSpec.GpuNum, + MemoryMB: resourceSpec.MemMiB, + ShmMB: resourceSpec.ShareMemMiB, Command: command, NeedIBDevice: false, IsMainRole: false, diff --git a/modules/convert/convert.go b/modules/convert/convert.go old mode 100644 new mode 100755 index ec18b1305..fa2e8f2e7 --- a/modules/convert/convert.go +++ b/modules/convert/convert.go @@ -335,6 +335,7 @@ func ToUser(user *models.User, signed, authed bool) *api.User { AvatarURL: user.AvatarLink(), FullName: markup.Sanitize(user.FullName), Created: user.CreatedUnix.AsTime(), + IsActive: user.IsActive, } // hide primary email if API caller is anonymous or user keep email private if signed && (!user.KeepEmailPrivate || authed) { diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 7785028f8..986a18313 100755 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -439,6 +439,7 @@ var ( JobType string GpuTypes string DebugServerHost string + ResourceSpecs string //benchmark config IsBenchmarkEnabled bool @@ -1147,7 +1148,8 @@ func NewContext() { JobPath = sec.Key("JOB_PATH").MustString("/datasets/minio/data/opendata/jobs/") DebugServerHost = sec.Key("DEBUG_SERVER_HOST").MustString("http://192.168.202.73") JobType = sec.Key("GPU_TYPE_DEFAULT").MustString("openidebug") - GpuTypes = sec.Key("GPU_TYPES").MustString("openidebug,openidgx") + GpuTypes = sec.Key("GPU_TYPES").MustString("") + ResourceSpecs = sec.Key("RESOURCE_SPECS").MustString("") sec = Cfg.Section("benchmark") IsBenchmarkEnabled = sec.Key("ENABLED").MustBool(false) diff --git a/modules/structs/user.go b/modules/structs/user.go old mode 100644 new mode 100755 index bf52cc9ed..d6e7298cc --- a/modules/structs/user.go +++ b/modules/structs/user.go @@ -26,6 +26,8 @@ type User struct { Language string `json:"language"` // Is the user an administrator IsAdmin bool `json:"is_admin"` + // Is the user active + IsActive bool `json:"is_active"` // swagger:strfmt date-time LastLogin time.Time `json:"last_login,omitempty"` // swagger:strfmt date-time diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index e809a33e8..b81dc9694 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -90,7 +90,7 @@ loading = Loading… error404_index = Request forbidden by administrative rules error500_index = Internal Server Error error404 = The page you are trying to reach either does not exist or you are not authorized to view it. - +error500= Sorry, the site has encountered some problems, we are trying to fix the page, please try again later. [error] occurred = An error has occurred report_message = An error has occurred diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 9747bc7dc..5ac54f6c8 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -90,6 +90,7 @@ loading=正在加载... error404_index = 您的访问受限! error500_index = 抱歉!您指定的网页无法访问。 error404=您正尝试访问的页面 不存在您尚未被授权 查看该页面。 +error500=抱歉,站点遇到一些问题,我们正尝试修复网页,请您稍后再试。 [error] occurred=发生错误 diff --git a/package-lock.json b/package-lock.json index 2c3c983e6..b0b0acad0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1841,6 +1841,14 @@ "async-done": "^1.2.2" } }, + "async-validator": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-1.8.5.tgz", + "integrity": "sha512-tXBM+1m056MAX0E8TL2iCjg8WvSyXu0Zc8LNtYqrVeyoL3+esHRZ4SieE9fKQyyU09uONjnMEjrNBMqT0mbvmA==", + "requires": { + "babel-runtime": "6.x" + } + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1934,6 +1942,11 @@ } } }, + "babel-helper-vue-jsx-merge-props": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz", + "integrity": "sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg==" + }, "babel-loader": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz", @@ -3882,6 +3895,26 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.437.tgz", "integrity": "sha512-PBQn2q68ErqMyBUABh9Gh8R6DunGky8aB5y3N5lPM7OVpldwyUbAK5AX9WcwE/5F6ceqvQ+iQLYkJYRysAs6Bg==" }, + "element-ui": { + "version": "2.15.5", + "resolved": "https://registry.npmjs.org/element-ui/-/element-ui-2.15.5.tgz", + "integrity": "sha512-B/YCdz2aRY2WnFXzbTRTHPKZHBD/2KV6u88EBnkaARC/Lyxnap+7vpvrcW5UNTyVwjItS5Fj1eQyRy6236lbXg==", + "requires": { + "async-validator": "~1.8.1", + "babel-helper-vue-jsx-merge-props": "^2.0.0", + "deepmerge": "^1.2.0", + "normalize-wheel": "^1.0.1", + "resize-observer-polyfill": "^1.5.0", + "throttle-debounce": "^1.0.1" + }, + "dependencies": { + "deepmerge": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz", + "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==" + } + } + }, "elliptic": { "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", @@ -9186,6 +9219,11 @@ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==" }, + "normalize-wheel": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/normalize-wheel/-/normalize-wheel-1.0.1.tgz", + "integrity": "sha1-rsiGr/2wRQcNhWRH32Ls+GFG7EU=" + }, "now-and-later": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", @@ -11687,6 +11725,11 @@ "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.0.0.tgz", "integrity": "sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA==" }, + "resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, "resolve": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", @@ -13526,6 +13569,11 @@ "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-2.6.0.tgz", "integrity": "sha512-49WtAWS+tcsy93dRt6P0P3AMD2m5PvXRhuEA0kaXos5ZLlujtYmpmFsB+QvWUSxE1ZsstmYXfQ7L40+EcQgpAQ==" }, + "throttle-debounce": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-1.1.0.tgz", + "integrity": "sha512-XH8UiPCQcWNuk2LYePibW/4qL97+ZQ1AN3FNXwZRBNPPowo/NRU5fAlDCSNBJIYCKbioZfuYtMhG4quqoJhVzg==" + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", diff --git a/package.json b/package.json index 4afc213e7..bda9843e8 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "cssnano": "4.1.10", "domino": "2.1.5", "dropzone": "5.7.2", + "element-ui": "2.15.5", "esdk-obs-browserjs": "3.20.7", "esdk-obs-nodejs": "3.20.11", "fast-glob": "3.2.2", diff --git a/routers/repo/attachment.go b/routers/repo/attachment.go index 830f193ee..fea7a3384 100755 --- a/routers/repo/attachment.go +++ b/routers/repo/attachment.go @@ -151,18 +151,20 @@ func DownloadUserIsOrgOrCollaboration(ctx *context.Context, attach *models.Attac log.Info("query repo error.") } else { repo.GetOwner() - if repo.Owner.IsOrganization() { - //log.Info("ower is org.") - if repo.Owner.IsUserPartOfOrg(ctx.User.ID) { - log.Info("org user may visit the attach.") + if ctx.User != nil { + + if repo.Owner.IsOrganization() { + if repo.Owner.IsUserPartOfOrg(ctx.User.ID) { + log.Info("org user may visit the attach.") + return true + } + } + isCollaborator, _ := repo.IsCollaborator(ctx.User.ID) + if isCollaborator { + log.Info("Collaborator user may visit the attach.") return true } } - isCollaborator, _ := repo.IsCollaborator(ctx.User.ID) - if isCollaborator { - log.Info("Collaborator user may visit the attach.") - return true - } } } return false @@ -192,19 +194,29 @@ func GetAttachment(ctx *context.Context) { ctx.ServerError("LinkedRepository", err) return } + dataSet, err := attach.LinkedDataSet() + if err != nil { + ctx.ServerError("LinkedDataSet", err) + return + } + + if repository == nil && dataSet != nil { + repository, _ = models.GetRepositoryByID(dataSet.RepoID) + unitType = models.UnitTypeDatasets + } if repository == nil { //If not linked //if !(ctx.IsSigned && attach.UploaderID == ctx.User.ID) && attach.IsPrivate { //We block if not the uploader - if !(ctx.IsSigned && attach.UploaderID == ctx.User.ID) && !DownloadUserIsOrgOrCollaboration(ctx, attach) { //We block if not the uploader + //log.Info("ctx.IsSigned =" + fmt.Sprintf("%v", ctx.IsSigned)) + if !(ctx.IsSigned && attach.UploaderID == ctx.User.ID) && attach.IsPrivate && !DownloadUserIsOrgOrCollaboration(ctx, attach) { //We block if not the uploader ctx.Error(http.StatusNotFound) return } } else { //If we have the repository we check access - - perm, err := models.GetUserRepoPermission(repository, ctx.User) - if err != nil { - ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err.Error()) + perm, errPermission := models.GetUserRepoPermission(repository, ctx.User) + if errPermission != nil { + ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", errPermission.Error()) return } if !perm.CanRead(unitType) { @@ -213,12 +225,6 @@ func GetAttachment(ctx *context.Context) { } } - dataSet, err := attach.LinkedDataSet() - if err != nil { - ctx.ServerError("LinkedDataSet", err) - return - } - if dataSet != nil { isPermit, err := models.GetUserDataSetPermission(dataSet, ctx.User) if err != nil { diff --git a/routers/repo/cloudbrain.go b/routers/repo/cloudbrain.go index 1088f47d3..f6f796e14 100755 --- a/routers/repo/cloudbrain.go +++ b/routers/repo/cloudbrain.go @@ -148,6 +148,11 @@ func CloudBrainNew(ctx *context.Context) { json.Unmarshal([]byte(setting.GpuTypes), &gpuInfos) } ctx.Data["gpu_types"] = gpuInfos.GpuInfo + + if cloudbrain.ResourceSpecs == nil { + json.Unmarshal([]byte(setting.ResourceSpecs), &cloudbrain.ResourceSpecs) + } + ctx.Data["resource_specs"] = cloudbrain.ResourceSpecs.ResourceSpec ctx.Data["snn4imagenet_path"] = cloudbrain.Snn4imagenetMountPath ctx.Data["is_snn4imagenet_enabled"] = setting.IsSnn4imagenetEnabled ctx.HTML(200, tplCloudBrainNew) @@ -162,6 +167,7 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { jobType := form.JobType gpuQueue := setting.JobType codePath := setting.JobPath + jobName + cloudbrain.CodeMountPath + resourceSpecId := form.ResourceSpecId if jobType != string(models.JobTypeBenchmark) && jobType != string(models.JobTypeDebug) && jobType != string(models.JobTypeSnn4imagenet) { log.Error("jobtype error:", jobType, ctx.Data["msgID"]) @@ -208,7 +214,7 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { downloadRateCode(repo, jobName, setting.Snn4imagenetCode, snn4imagenetPath, "", "") } - err = cloudbrain.GenerateTask(ctx, jobName, image, command, uuid, codePath, modelPath, benchmarkPath, snn4imagenetPath, jobType, gpuQueue) + err = cloudbrain.GenerateTask(ctx, jobName, image, command, uuid, codePath, modelPath, benchmarkPath, snn4imagenetPath, jobType, gpuQueue, resourceSpecId) if err != nil { ctx.RenderWithErr(err.Error(), tplCloudBrainNew, &form) return diff --git a/routers/repo/dataset.go b/routers/repo/dataset.go index bdadd2066..b2da4b8d8 100755 --- a/routers/repo/dataset.go +++ b/routers/repo/dataset.go @@ -32,13 +32,13 @@ func newFilterPrivateAttachments(ctx *context.Context, list []*models.Attachment repo.GetOwner() } permission := false - if repo.Owner.IsOrganization() { + if repo.Owner.IsOrganization() && ctx.User != nil { if repo.Owner.IsUserPartOfOrg(ctx.User.ID) { log.Info("user is member of org.") permission = true } } - if !permission { + if !permission && ctx.User != nil { isCollaborator, _ := repo.IsCollaborator(ctx.User.ID) if isCollaborator { log.Info("Collaborator user may visit the attach.") diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 100ff009f..b3565878e 100755 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -1129,7 +1129,7 @@ func RegisterRoutes(m *macaron.Macaron) { //secure api, m.Group("/secure", func() { - m.Post("/user", binding.Bind(structs.CreateUserOption{}), secure.CreateUser) + m.Post("/user", binding.BindIgnErr(structs.CreateUserOption{}), secure.CreateUser) }, reqBasicAuth) m.Group("/api/internal", func() { diff --git a/routers/secure/user.go b/routers/secure/user.go index 1e88a7381..8567dc9e6 100755 --- a/routers/secure/user.go +++ b/routers/secure/user.go @@ -7,6 +7,7 @@ package secure import ( "net/http" + "strings" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" @@ -75,11 +76,19 @@ func CreateUser(ctx *context.Context, form api.CreateUserOption) { u.MustChangePassword = *form.MustChangePassword } + if strings.Contains(form.Email, " ") { + log.Error("CreateUser failed: email(%s) contains blank space", form.Email, ctx.Data["MsgID"]) + ctx.JSON(http.StatusBadRequest, map[string]string{ + "error_msg": "Email contains blank space", + }) + return + } + parseLoginSource(ctx, u, form.SourceID, form.LoginName) if ctx.Written() { return } - if !password.IsComplexEnough(form.Password) { + if !password.IsComplexEnough(form.Password) || len(form.Password) < setting.MinPasswordLength { log.Error("CreateUser failed: PasswordComplexity", ctx.Data["MsgID"]) ctx.JSON(http.StatusBadRequest, map[string]string{ "error_msg": "PasswordComplexity", @@ -119,6 +128,14 @@ func CreateUser(ctx *context.Context, form api.CreateUserOption) { return } + // Send confirmation email + if setting.Service.RegisterEmailConfirm{ + mailer.SendActivateAccountMail(ctx.Locale, u) + if err := ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil { + log.Error("Set cache(MailResendLimit) fail: %v", err) + } + } + log.Trace("Account created (%s): %s", ctx.User.Name, u.Name, ctx.Data["MsgID"]) // Send email notification. diff --git a/routers/user/auth.go b/routers/user/auth.go index dd66fcb8b..13e338565 100755 --- a/routers/user/auth.go +++ b/routers/user/auth.go @@ -1266,6 +1266,15 @@ func Activate(ctx *context.Context) { log.Error("Error storing session: %v", err) } + email, err := models.GetEmailAddressByIDAndEmail(user.ID, user.Email) + if err != nil || email == nil{ + log.Error("GetEmailAddressByIDAndEmail failed", ctx.Data["MsgID"]) + } else { + if err := email.Activate(); err != nil { + log.Error("Activate failed: %v", err, ctx.Data["MsgID"]) + } + } + ctx.Flash.Success(ctx.Tr("auth.account_activated")) ctx.Redirect(setting.AppSubURL + "/") return diff --git a/routers/user/setting/profile.go b/routers/user/setting/profile.go old mode 100644 new mode 100755 index d6f25f913..a385f2cac --- a/routers/user/setting/profile.go +++ b/routers/user/setting/profile.go @@ -96,6 +96,18 @@ func ProfilePost(ctx *context.Context, form auth.UpdateProfileForm) { ctx.User.Location = form.Location ctx.User.Language = form.Language ctx.User.Description = form.Description + isUsed, err := models.IsEmailUsed(form.Email) + if err != nil { + ctx.ServerError("IsEmailUsed", err) + return + } + + if isUsed { + ctx.Flash.Error(ctx.Tr("form.email_been_used")) + ctx.Redirect(setting.AppSubURL + "/user/settings") + return + } + if err := models.UpdateUserSetting(ctx.User); err != nil { if _, ok := err.(models.ErrEmailAlreadyUsed); ok { ctx.Flash.Error(ctx.Tr("form.email_been_used")) diff --git a/templates/home.tmpl b/templates/home.tmpl index 35790cbba..97a2a7fc9 100755 --- a/templates/home.tmpl +++ b/templates/home.tmpl @@ -127,7 +127,7 @@
- 开发要素统一管理 +

开发要素统一管理

平台提供了AI开发四大要素:模型代码、数据集、模型和执行环境的统一管理
@@ -138,7 +138,7 @@
- 数据协同与共享 +

数据协同与共享

通过在项目中上传数据集,项目成员多人协作完成数据预处理;也可以通过将数据设置为公有数据集,与社区开发者共同建立更好的模型
@@ -149,7 +149,7 @@
- 模型管理与共享 +

模型管理与共享

将模型与代码版本建立关联,可以基于代码历史版本,使用不同的方式调整模型,并将结果保存下来;训练好的模型可以开放共享,让更多人的使用模型测试并提出反馈
@@ -160,7 +160,7 @@
- 一次配置,多次使用 +

一次配置,多次使用

提供执行环境共享,一次配置,多次使用,降低模型开发门槛,避免花费重复的时间配置复杂的环境
diff --git a/templates/repo/cloudbrain/new.tmpl b/templates/repo/cloudbrain/new.tmpl index c1afb9389..4e533091e 100755 --- a/templates/repo/cloudbrain/new.tmpl +++ b/templates/repo/cloudbrain/new.tmpl @@ -161,14 +161,15 @@
- + {{range .images}} {{end}} {{range .public_images}} {{end}} - +
@@ -180,6 +181,15 @@
+
+ + +
+
@@ -242,9 +252,9 @@ .dropdown(); - $('#cloudbrain_image').select2({ - placeholder: "选择镜像" - }); + // $('#cloudbrain_image').select2({ + // placeholder: "选择镜像" + // }); $(".ui.button.reset").click(function(e){ diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index 75cee742e..b48efd498 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -12,6 +12,15 @@ float: left; margin: .25em; } +.edit-link{ + vertical-align: top; + display: inline-block; + overflow: hidden; + word-break: keep-all; + white-space: nowrap; + text-overflow: ellipsis; + width: 16.5em; +} #contributorInfo > a.circular{ height: 2.0em; padding: 0; @@ -197,37 +206,55 @@
-

简介

+

简介 + +

- {{if .Repository.DescriptionHTML}} - {{.Repository.DescriptionHTML}} - {{else}} - {{.i18n.Tr "repo.no_desc"}} - {{end}} - {{.Repository.Website}} + {{if .Repository.DescriptionHTML}} + {{.Repository.DescriptionHTML}} + {{else}} + {{.i18n.Tr "repo.no_desc"}} + {{end}} +

+
+ + + {{if .Repository.Website}} +

+ + {{.Repository.Website}} +

+ + {{end}} +

{{range .Topics}}{{.Name}}{{end}} {{if and .Permission.IsAdmin (not .Repository.IsArchived)}}{{.i18n.Tr "repo.topic.manage_topics"}}{{end}}

- +

{{range .LanguageStats}} {{.Language}} {{end}}

+ + + {{if .LICENSE}}

- {{if .LICENSE}} - {{.LICENSE}} - {{end}} + {{.LICENSE}}

+ + {{end}}
diff --git a/templates/status/500.tmpl b/templates/status/500.tmpl index e4ea06d3d..ca7336f82 100644 --- a/templates/status/500.tmpl +++ b/templates/status/500.tmpl @@ -6,7 +6,7 @@

{{.i18n.Tr "error500_index"}}

-

{{.i18n.Tr "error404" | Safe}}

+

{{.i18n.Tr "error500" | Safe}}

diff --git a/templates/user/settings/profile.tmpl b/templates/user/settings/profile.tmpl index 995bdfd63..f6fb7a7ae 100644 --- a/templates/user/settings/profile.tmpl +++ b/templates/user/settings/profile.tmpl @@ -23,7 +23,7 @@
- +
diff --git a/web_src/js/components/EditAboutInfo.vue b/web_src/js/components/EditAboutInfo.vue new file mode 100644 index 000000000..6249a5b9b --- /dev/null +++ b/web_src/js/components/EditAboutInfo.vue @@ -0,0 +1,144 @@ + + + + + diff --git a/web_src/js/components/MinioUploader.vue b/web_src/js/components/MinioUploader.vue index 1dc92e4b3..243c627c5 100755 --- a/web_src/js/components/MinioUploader.vue +++ b/web_src/js/components/MinioUploader.vue @@ -335,8 +335,19 @@ export default { async function uploadMinio(url, e) { const res = await axios.put(url, e.target.result); - delete e.target.result + delete e.target.result etags[currentChunk] = res.headers.etag; + + } + + async function uploadMinioNewMethod(url,e){ + var xhr = new XMLHttpRequest(); + xhr.open('PUT', url, false); + xhr.setRequestHeader('Content-Type', 'text/plain') + xhr.send(e.target.result); + var etagValue = xhr.getResponseHeader('etag'); + //console.log(etagValue); + etags[currentChunk] = etagValue; } async function updateChunk(currentChunk) { @@ -359,8 +370,10 @@ export default { // 获取分片上传url await getUploadChunkUrl(currentChunk, partSize); if (urls[currentChunk] != '') { - // 上传到minio - await uploadMinio(urls[currentChunk], e); + // 上传到minio + //await uploadMinio(urls[currentChunk], e); + await uploadMinioNewMethod(urls[currentChunk], e); + if (etags[currentChunk] != '') { // 更新数据库:分片上传结果 //await updateChunk(currentChunk); @@ -372,8 +385,9 @@ export default { } } } catch (error) { - this.emitDropzoneFailed(file); console.log(error); + //this.emitDropzoneFailed(file); + //console.log(error); } } diff --git a/web_src/js/components/basic/editDialog.vue b/web_src/js/components/basic/editDialog.vue new file mode 100644 index 000000000..ed3225796 --- /dev/null +++ b/web_src/js/components/basic/editDialog.vue @@ -0,0 +1,105 @@ + + + + diff --git a/web_src/js/index.js b/web_src/js/index.js index 1a4326f7d..2de20b0d4 100755 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -6,6 +6,10 @@ import './publicpath.js'; import './polyfills.js'; import Vue from 'vue'; +import ElementUI from 'element-ui'; +import 'element-ui/lib/theme-chalk/index.css'; +import axios from 'axios'; +import qs from 'qs'; import 'jquery.are-you-sure'; import './vendor/semanticdropdown.js'; import {svg} from './utils.js'; @@ -29,8 +33,12 @@ import { } from './features/notification.js'; import {createCodeEditor} from './features/codeeditor.js'; import MinioUploader from './components/MinioUploader.vue'; -import ObsUploader from './components/ObsUploader.vue' +import ObsUploader from './components/ObsUploader.vue'; +import EditAboutInfo from './components/EditAboutInfo.vue'; +Vue.use(ElementUI); +Vue.prototype.$axios = axios; +Vue.prototype.qs = qs; const {AppSubUrl, StaticUrlPrefix, csrf} = window.config; function htmlEncode(text) { @@ -2957,6 +2965,7 @@ $(document).ready(async () => { initVueApp(); initVueUploader(); initObsUploader(); + initVueEditAbout(); initTeamSettings(); initCtrlEnterSubmit(); initNavbarContentToggle(); @@ -3642,6 +3651,19 @@ function initVueUploader() { }); } +function initVueEditAbout() { + const el = document.getElementById('about-desc'); + console.log(el) + if (!el) { + return; + } + + new Vue({ + el: '#about-desc', + render: h => h(EditAboutInfo) + }); +} + // 新增 function initObsUploader() { const el = document.getElementById('obsUploader');