| @@ -72,11 +72,11 @@ type CloudBrainLoginResult struct { | |||||
| type TaskRole struct { | type TaskRole struct { | ||||
| Name string `json:"name"` | 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"` | MemoryMB int `json:"memoryMB"` | ||||
| ShmMB int `json:"shmMB"` | ShmMB int `json:"shmMB"` | ||||
| Command string `json:"command"` | Command string `json:"command"` | ||||
| @@ -286,6 +286,18 @@ type GpuInfo struct { | |||||
| Queue string `json:"queue"` | 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 { | type CommitImageParams struct { | ||||
| Ip string `json:"ip"` | Ip string `json:"ip"` | ||||
| TaskContainerId string `json:"taskContainerId"` | TaskContainerId string `json:"taskContainerId"` | ||||
| @@ -976,12 +976,12 @@ func CreateUser(u *User) (err error) { | |||||
| result, err := blockchain.CreateBlockchainAccount() | result, err := blockchain.CreateBlockchainAccount() | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("createBlockchainAccount failed:", err.Error()) | 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() | sess := x.NewSession() | ||||
| defer sess.Close() | defer sess.Close() | ||||
| if err = sess.Begin(); err != nil { | if err = sess.Begin(); err != nil { | ||||
| @@ -80,6 +80,18 @@ func GetEmailAddressByID(uid, id int64) (*EmailAddress, error) { | |||||
| return email, nil | 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) { | func isEmailActive(e Engine, email string, userID, emailID int64) (bool, error) { | ||||
| if len(email) == 0 { | if len(email) == 0 { | ||||
| return true, nil | return true, nil | ||||
| @@ -13,6 +13,7 @@ type CreateCloudBrainForm struct { | |||||
| JobType string `form:"job_type" binding:"Required"` | JobType string `form:"job_type" binding:"Required"` | ||||
| BenchmarkCategory string `form:"get_benchmark_category"` | BenchmarkCategory string `form:"get_benchmark_category"` | ||||
| GpuType string `form:"gpu_type"` | GpuType string `form:"gpu_type"` | ||||
| ResourceSpecId int `form:"resource_spec_id" binding:"Required"` | |||||
| } | } | ||||
| type CommitImageCloudBrainForm struct { | type CommitImageCloudBrainForm struct { | ||||
| @@ -23,12 +23,29 @@ const ( | |||||
| Success = "S000" | 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 + | dataActualPath := setting.Attachment.Minio.RealPath + | ||||
| setting.Attachment.Minio.Bucket + "/" + | setting.Attachment.Minio.Bucket + "/" + | ||||
| setting.Attachment.Minio.BasePath + | setting.Attachment.Minio.BasePath + | ||||
| models.AttachmentRelativePath(uuid) + | models.AttachmentRelativePath(uuid) + | ||||
| 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{ | jobResult, err := CreateJob(jobName, models.CreateJobParams{ | ||||
| JobName: jobName, | JobName: jobName, | ||||
| RetryCount: 1, | RetryCount: 1, | ||||
| @@ -40,10 +57,10 @@ func GenerateTask(ctx *context.Context, jobName, image, command, uuid, codePath, | |||||
| TaskNumber: 1, | TaskNumber: 1, | ||||
| MinSucceededTaskCount: 1, | MinSucceededTaskCount: 1, | ||||
| MinFailedTaskCount: 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, | Command: command, | ||||
| NeedIBDevice: false, | NeedIBDevice: false, | ||||
| IsMainRole: false, | IsMainRole: false, | ||||
| @@ -335,6 +335,7 @@ func ToUser(user *models.User, signed, authed bool) *api.User { | |||||
| AvatarURL: user.AvatarLink(), | AvatarURL: user.AvatarLink(), | ||||
| FullName: markup.Sanitize(user.FullName), | FullName: markup.Sanitize(user.FullName), | ||||
| Created: user.CreatedUnix.AsTime(), | Created: user.CreatedUnix.AsTime(), | ||||
| IsActive: user.IsActive, | |||||
| } | } | ||||
| // hide primary email if API caller is anonymous or user keep email private | // hide primary email if API caller is anonymous or user keep email private | ||||
| if signed && (!user.KeepEmailPrivate || authed) { | if signed && (!user.KeepEmailPrivate || authed) { | ||||
| @@ -439,6 +439,7 @@ var ( | |||||
| JobType string | JobType string | ||||
| GpuTypes string | GpuTypes string | ||||
| DebugServerHost string | DebugServerHost string | ||||
| ResourceSpecs string | |||||
| //benchmark config | //benchmark config | ||||
| IsBenchmarkEnabled bool | IsBenchmarkEnabled bool | ||||
| @@ -1147,7 +1148,8 @@ func NewContext() { | |||||
| JobPath = sec.Key("JOB_PATH").MustString("/datasets/minio/data/opendata/jobs/") | JobPath = sec.Key("JOB_PATH").MustString("/datasets/minio/data/opendata/jobs/") | ||||
| DebugServerHost = sec.Key("DEBUG_SERVER_HOST").MustString("http://192.168.202.73") | DebugServerHost = sec.Key("DEBUG_SERVER_HOST").MustString("http://192.168.202.73") | ||||
| JobType = sec.Key("GPU_TYPE_DEFAULT").MustString("openidebug") | 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") | sec = Cfg.Section("benchmark") | ||||
| IsBenchmarkEnabled = sec.Key("ENABLED").MustBool(false) | IsBenchmarkEnabled = sec.Key("ENABLED").MustBool(false) | ||||
| @@ -26,6 +26,8 @@ type User struct { | |||||
| Language string `json:"language"` | Language string `json:"language"` | ||||
| // Is the user an administrator | // Is the user an administrator | ||||
| IsAdmin bool `json:"is_admin"` | IsAdmin bool `json:"is_admin"` | ||||
| // Is the user active | |||||
| IsActive bool `json:"is_active"` | |||||
| // swagger:strfmt date-time | // swagger:strfmt date-time | ||||
| LastLogin time.Time `json:"last_login,omitempty"` | LastLogin time.Time `json:"last_login,omitempty"` | ||||
| // swagger:strfmt date-time | // swagger:strfmt date-time | ||||
| @@ -90,7 +90,7 @@ loading = Loading… | |||||
| error404_index = Request forbidden by administrative rules | error404_index = Request forbidden by administrative rules | ||||
| error500_index = Internal Server Error | error500_index = Internal Server Error | ||||
| error404 = The page you are trying to reach either <strong>does not exist</strong> or <strong>you are not authorized</strong> to view it. | error404 = The page you are trying to reach either <strong>does not exist</strong> or <strong>you are not authorized</strong> to view it. | ||||
| error500= Sorry, the site has encountered some problems, we are trying to <strong>fix the page</strong>, please try again later. | |||||
| [error] | [error] | ||||
| occurred = An error has occurred | occurred = An error has occurred | ||||
| report_message = An error has occurred | report_message = An error has occurred | ||||
| @@ -90,6 +90,7 @@ loading=正在加载... | |||||
| error404_index = 您的访问受限! | error404_index = 您的访问受限! | ||||
| error500_index = 抱歉!您指定的网页无法访问。 | error500_index = 抱歉!您指定的网页无法访问。 | ||||
| error404=您正尝试访问的页面 <strong>不存在</strong> 或 <strong>您尚未被授权</strong> 查看该页面。 | error404=您正尝试访问的页面 <strong>不存在</strong> 或 <strong>您尚未被授权</strong> 查看该页面。 | ||||
| error500=抱歉,站点遇到一些问题,我们正尝试<strong>修复网页</strong>,请您稍后再试。 | |||||
| [error] | [error] | ||||
| occurred=发生错误 | occurred=发生错误 | ||||
| @@ -1841,6 +1841,14 @@ | |||||
| "async-done": "^1.2.2" | "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": { | "asynckit": { | ||||
| "version": "0.4.0", | "version": "0.4.0", | ||||
| "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", | "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": { | "babel-loader": { | ||||
| "version": "8.1.0", | "version": "8.1.0", | ||||
| "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz", | "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", | "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.437.tgz", | ||||
| "integrity": "sha512-PBQn2q68ErqMyBUABh9Gh8R6DunGky8aB5y3N5lPM7OVpldwyUbAK5AX9WcwE/5F6ceqvQ+iQLYkJYRysAs6Bg==" | "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": { | "elliptic": { | ||||
| "version": "6.5.4", | "version": "6.5.4", | ||||
| "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", | "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", | "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", | ||||
| "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==" | "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": { | "now-and-later": { | ||||
| "version": "2.0.1", | "version": "2.0.1", | ||||
| "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", | "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", | "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.0.0.tgz", | ||||
| "integrity": "sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA==" | "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": { | "resolve": { | ||||
| "version": "1.17.0", | "version": "1.17.0", | ||||
| "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", | "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", | "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-2.6.0.tgz", | ||||
| "integrity": "sha512-49WtAWS+tcsy93dRt6P0P3AMD2m5PvXRhuEA0kaXos5ZLlujtYmpmFsB+QvWUSxE1ZsstmYXfQ7L40+EcQgpAQ==" | "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": { | "through": { | ||||
| "version": "2.3.8", | "version": "2.3.8", | ||||
| "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", | ||||
| @@ -19,6 +19,7 @@ | |||||
| "cssnano": "4.1.10", | "cssnano": "4.1.10", | ||||
| "domino": "2.1.5", | "domino": "2.1.5", | ||||
| "dropzone": "5.7.2", | "dropzone": "5.7.2", | ||||
| "element-ui": "2.15.5", | |||||
| "esdk-obs-browserjs": "3.20.7", | "esdk-obs-browserjs": "3.20.7", | ||||
| "esdk-obs-nodejs": "3.20.11", | "esdk-obs-nodejs": "3.20.11", | ||||
| "fast-glob": "3.2.2", | "fast-glob": "3.2.2", | ||||
| @@ -151,18 +151,20 @@ func DownloadUserIsOrgOrCollaboration(ctx *context.Context, attach *models.Attac | |||||
| log.Info("query repo error.") | log.Info("query repo error.") | ||||
| } else { | } else { | ||||
| repo.GetOwner() | 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 | return true | ||||
| } | } | ||||
| } | } | ||||
| isCollaborator, _ := repo.IsCollaborator(ctx.User.ID) | |||||
| if isCollaborator { | |||||
| log.Info("Collaborator user may visit the attach.") | |||||
| return true | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| return false | return false | ||||
| @@ -192,19 +194,29 @@ func GetAttachment(ctx *context.Context) { | |||||
| ctx.ServerError("LinkedRepository", err) | ctx.ServerError("LinkedRepository", err) | ||||
| return | 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 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) && 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) | ctx.Error(http.StatusNotFound) | ||||
| return | return | ||||
| } | } | ||||
| } else { //If we have the repository we check access | } 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 | return | ||||
| } | } | ||||
| if !perm.CanRead(unitType) { | 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 { | if dataSet != nil { | ||||
| isPermit, err := models.GetUserDataSetPermission(dataSet, ctx.User) | isPermit, err := models.GetUserDataSetPermission(dataSet, ctx.User) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -148,6 +148,11 @@ func CloudBrainNew(ctx *context.Context) { | |||||
| json.Unmarshal([]byte(setting.GpuTypes), &gpuInfos) | json.Unmarshal([]byte(setting.GpuTypes), &gpuInfos) | ||||
| } | } | ||||
| ctx.Data["gpu_types"] = gpuInfos.GpuInfo | 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["snn4imagenet_path"] = cloudbrain.Snn4imagenetMountPath | ||||
| ctx.Data["is_snn4imagenet_enabled"] = setting.IsSnn4imagenetEnabled | ctx.Data["is_snn4imagenet_enabled"] = setting.IsSnn4imagenetEnabled | ||||
| ctx.HTML(200, tplCloudBrainNew) | ctx.HTML(200, tplCloudBrainNew) | ||||
| @@ -162,6 +167,7 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { | |||||
| jobType := form.JobType | jobType := form.JobType | ||||
| gpuQueue := setting.JobType | gpuQueue := setting.JobType | ||||
| codePath := setting.JobPath + jobName + cloudbrain.CodeMountPath | codePath := setting.JobPath + jobName + cloudbrain.CodeMountPath | ||||
| resourceSpecId := form.ResourceSpecId | |||||
| if jobType != string(models.JobTypeBenchmark) && jobType != string(models.JobTypeDebug) && jobType != string(models.JobTypeSnn4imagenet) { | if jobType != string(models.JobTypeBenchmark) && jobType != string(models.JobTypeDebug) && jobType != string(models.JobTypeSnn4imagenet) { | ||||
| log.Error("jobtype error:", jobType, ctx.Data["msgID"]) | 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, "", "") | 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 { | if err != nil { | ||||
| ctx.RenderWithErr(err.Error(), tplCloudBrainNew, &form) | ctx.RenderWithErr(err.Error(), tplCloudBrainNew, &form) | ||||
| return | return | ||||
| @@ -32,13 +32,13 @@ func newFilterPrivateAttachments(ctx *context.Context, list []*models.Attachment | |||||
| repo.GetOwner() | repo.GetOwner() | ||||
| } | } | ||||
| permission := false | permission := false | ||||
| if repo.Owner.IsOrganization() { | |||||
| if repo.Owner.IsOrganization() && ctx.User != nil { | |||||
| if repo.Owner.IsUserPartOfOrg(ctx.User.ID) { | if repo.Owner.IsUserPartOfOrg(ctx.User.ID) { | ||||
| log.Info("user is member of org.") | log.Info("user is member of org.") | ||||
| permission = true | permission = true | ||||
| } | } | ||||
| } | } | ||||
| if !permission { | |||||
| if !permission && ctx.User != nil { | |||||
| isCollaborator, _ := repo.IsCollaborator(ctx.User.ID) | isCollaborator, _ := repo.IsCollaborator(ctx.User.ID) | ||||
| if isCollaborator { | if isCollaborator { | ||||
| log.Info("Collaborator user may visit the attach.") | log.Info("Collaborator user may visit the attach.") | ||||
| @@ -1129,7 +1129,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| //secure api, | //secure api, | ||||
| m.Group("/secure", func() { | m.Group("/secure", func() { | ||||
| m.Post("/user", binding.Bind(structs.CreateUserOption{}), secure.CreateUser) | |||||
| m.Post("/user", binding.BindIgnErr(structs.CreateUserOption{}), secure.CreateUser) | |||||
| }, reqBasicAuth) | }, reqBasicAuth) | ||||
| m.Group("/api/internal", func() { | m.Group("/api/internal", func() { | ||||
| @@ -7,6 +7,7 @@ package secure | |||||
| import ( | import ( | ||||
| "net/http" | "net/http" | ||||
| "strings" | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/context" | "code.gitea.io/gitea/modules/context" | ||||
| @@ -75,11 +76,19 @@ func CreateUser(ctx *context.Context, form api.CreateUserOption) { | |||||
| u.MustChangePassword = *form.MustChangePassword | 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) | parseLoginSource(ctx, u, form.SourceID, form.LoginName) | ||||
| if ctx.Written() { | if ctx.Written() { | ||||
| return | return | ||||
| } | } | ||||
| if !password.IsComplexEnough(form.Password) { | |||||
| if !password.IsComplexEnough(form.Password) || len(form.Password) < setting.MinPasswordLength { | |||||
| log.Error("CreateUser failed: PasswordComplexity", ctx.Data["MsgID"]) | log.Error("CreateUser failed: PasswordComplexity", ctx.Data["MsgID"]) | ||||
| ctx.JSON(http.StatusBadRequest, map[string]string{ | ctx.JSON(http.StatusBadRequest, map[string]string{ | ||||
| "error_msg": "PasswordComplexity", | "error_msg": "PasswordComplexity", | ||||
| @@ -119,6 +128,14 @@ func CreateUser(ctx *context.Context, form api.CreateUserOption) { | |||||
| return | 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"]) | log.Trace("Account created (%s): %s", ctx.User.Name, u.Name, ctx.Data["MsgID"]) | ||||
| // Send email notification. | // Send email notification. | ||||
| @@ -1266,6 +1266,15 @@ func Activate(ctx *context.Context) { | |||||
| log.Error("Error storing session: %v", err) | 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.Flash.Success(ctx.Tr("auth.account_activated")) | ||||
| ctx.Redirect(setting.AppSubURL + "/") | ctx.Redirect(setting.AppSubURL + "/") | ||||
| return | return | ||||
| @@ -96,6 +96,18 @@ func ProfilePost(ctx *context.Context, form auth.UpdateProfileForm) { | |||||
| ctx.User.Location = form.Location | ctx.User.Location = form.Location | ||||
| ctx.User.Language = form.Language | ctx.User.Language = form.Language | ||||
| ctx.User.Description = form.Description | 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 err := models.UpdateUserSetting(ctx.User); err != nil { | ||||
| if _, ok := err.(models.ErrEmailAlreadyUsed); ok { | if _, ok := err.(models.ErrEmailAlreadyUsed); ok { | ||||
| ctx.Flash.Error(ctx.Tr("form.email_been_used")) | ctx.Flash.Error(ctx.Tr("form.email_been_used")) | ||||
| @@ -127,7 +127,7 @@ | |||||
| <img src="/img/i-pic-01.svg"> | <img src="/img/i-pic-01.svg"> | ||||
| </div> | </div> | ||||
| <div class="content"> | <div class="content"> | ||||
| <a class="ui centered header">开发要素统一管理</a> | |||||
| <h3 class="ui centered header">开发要素统一管理</h3> | |||||
| <div class="description"> | <div class="description"> | ||||
| 平台提供了AI开发四大要素:模型代码、数据集、模型和执行环境的统一管理 | 平台提供了AI开发四大要素:模型代码、数据集、模型和执行环境的统一管理 | ||||
| </div> | </div> | ||||
| @@ -138,7 +138,7 @@ | |||||
| <img src="/img/i-pic-02.svg"> | <img src="/img/i-pic-02.svg"> | ||||
| </div> | </div> | ||||
| <div class="content"> | <div class="content"> | ||||
| <a class="ui centered header">数据协同与共享</a> | |||||
| <h3 class="ui centered header">数据协同与共享</h3> | |||||
| <div class="description"> | <div class="description"> | ||||
| 通过在项目中上传数据集,项目成员多人协作完成数据预处理;也可以通过将数据设置为公有数据集,与社区开发者共同建立更好的模型 | 通过在项目中上传数据集,项目成员多人协作完成数据预处理;也可以通过将数据设置为公有数据集,与社区开发者共同建立更好的模型 | ||||
| </div> | </div> | ||||
| @@ -149,7 +149,7 @@ | |||||
| <img src="/img/i-pic-03.svg"> | <img src="/img/i-pic-03.svg"> | ||||
| </div> | </div> | ||||
| <div class="content"> | <div class="content"> | ||||
| <a class="ui centered header">模型管理与共享</a> | |||||
| <h3 class="ui centered header">模型管理与共享</h3> | |||||
| <div class="description"> | <div class="description"> | ||||
| 将模型与代码版本建立关联,可以基于代码历史版本,使用不同的方式调整模型,并将结果保存下来;训练好的模型可以开放共享,让更多人的使用模型测试并提出反馈 | 将模型与代码版本建立关联,可以基于代码历史版本,使用不同的方式调整模型,并将结果保存下来;训练好的模型可以开放共享,让更多人的使用模型测试并提出反馈 | ||||
| </div> | </div> | ||||
| @@ -160,7 +160,7 @@ | |||||
| <img src="/img/i-pic-04.svg"> | <img src="/img/i-pic-04.svg"> | ||||
| </div> | </div> | ||||
| <div class="content"> | <div class="content"> | ||||
| <a class="ui centered header">一次配置,多次使用</a> | |||||
| <h3 class="ui centered header">一次配置,多次使用</h3> | |||||
| <div class="description"> | <div class="description"> | ||||
| 提供执行环境共享,一次配置,多次使用,降低模型开发门槛,避免花费重复的时间配置复杂的环境 | 提供执行环境共享,一次配置,多次使用,降低模型开发门槛,避免花费重复的时间配置复杂的环境 | ||||
| </div> | </div> | ||||
| @@ -161,14 +161,15 @@ | |||||
| <div class="inline required field"> | <div class="inline required field"> | ||||
| <label>镜像</label> | <label>镜像</label> | ||||
| <select class="ui search" id="cloudbrain_image" placeholder="选择镜像" style='width:385px;' name="image"> | |||||
| <input type="text" list="cloudbrain_image" placeholder="选择镜像" name="image"> | |||||
| <datalist class="ui search" id="cloudbrain_image" style='width:385px;' name="image"> | |||||
| {{range .images}} | {{range .images}} | ||||
| <option name="image" value="{{.Place}}">{{.PlaceView}}</option> | <option name="image" value="{{.Place}}">{{.PlaceView}}</option> | ||||
| {{end}} | {{end}} | ||||
| {{range .public_images}} | {{range .public_images}} | ||||
| <option name="image" value="{{.Place}}">{{.PlaceView}}</option> | <option name="image" value="{{.Place}}">{{.PlaceView}}</option> | ||||
| {{end}} | {{end}} | ||||
| </select> | |||||
| </datalist> | |||||
| </div> | </div> | ||||
| <div class="inline required field"> | <div class="inline required field"> | ||||
| @@ -180,6 +181,15 @@ | |||||
| </select> | </select> | ||||
| </div> | </div> | ||||
| <div class="inline required field"> | |||||
| <label>资源规格</label> | |||||
| <select id="cloudbrain_resource_spec" class="ui search dropdown" placeholder="选择资源规格" style='width:385px' name="resource_spec_id"> | |||||
| {{range .resource_specs}} | |||||
| <option name="resource_spec_id" value="{{.Id}}">GPU数:{{.GpuNum}},CPU数:{{.CpuNum}},内存(MB):{{.MemMiB}},共享内存(MB):{{.ShareMemMiB}}</option> | |||||
| {{end}} | |||||
| </select> | |||||
| </div> | |||||
| <div class="inline required field"> | <div class="inline required field"> | ||||
| <label>数据集存放路径</label> | <label>数据集存放路径</label> | ||||
| <input name="dataset_path" id="cloudbrain_dataset_path" value="{{.dataset_path}}" tabindex="3" autofocus required maxlength="255" readonly="readonly"> | <input name="dataset_path" id="cloudbrain_dataset_path" value="{{.dataset_path}}" tabindex="3" autofocus required maxlength="255" readonly="readonly"> | ||||
| @@ -242,9 +252,9 @@ | |||||
| .dropdown(); | .dropdown(); | ||||
| $('#cloudbrain_image').select2({ | |||||
| placeholder: "选择镜像" | |||||
| }); | |||||
| // $('#cloudbrain_image').select2({ | |||||
| // placeholder: "选择镜像" | |||||
| // }); | |||||
| $(".ui.button.reset").click(function(e){ | $(".ui.button.reset").click(function(e){ | ||||
| @@ -12,6 +12,15 @@ | |||||
| float: left; | float: left; | ||||
| margin: .25em; | 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{ | #contributorInfo > a.circular{ | ||||
| height: 2.0em; | height: 2.0em; | ||||
| padding: 0; | padding: 0; | ||||
| @@ -197,37 +206,55 @@ | |||||
| </div> | </div> | ||||
| <div class="ui six wide tablet four wide computer column"> | <div class="ui six wide tablet four wide computer column"> | ||||
| <div id="repo-desc"> | <div id="repo-desc"> | ||||
| <h4 class="ui header">简介</h4> | |||||
| <h4 id="about-desc" class="ui header">简介 | |||||
| <!-- <a class="edit-icon" href="javascript:void(0)"> | |||||
| <i class="gray edit outline icon"></i> | |||||
| </a> --> | |||||
| </h4> | |||||
| <p> | <p> | ||||
| {{if .Repository.DescriptionHTML}} | |||||
| <span class="description">{{.Repository.DescriptionHTML}}</span> | |||||
| {{else}} | |||||
| <span class="no-description text-italic">{{.i18n.Tr "repo.no_desc"}}</span> | |||||
| {{end}} | |||||
| <a class="link" href="{{.Repository.Website}}">{{.Repository.Website}}</a> | |||||
| {{if .Repository.DescriptionHTML}} | |||||
| <span class="description" style="word-break:break-all">{{.Repository.DescriptionHTML}}</span> | |||||
| {{else}} | |||||
| <span class="no-description text-italic">{{.i18n.Tr "repo.no_desc"}}</span> | |||||
| {{end}} | |||||
| </p> | </p> | ||||
| </div> | </div> | ||||
| {{if .Repository.Website}} | |||||
| <p class="ui"> | |||||
| <i class="gray linkify icon"></i> | |||||
| <a class="link edit-link" target="_blank" title="{{.Repository.Website}}" href="{{.Repository.Website}}">{{.Repository.Website}}</a> | |||||
| </p> | |||||
| {{end}} | |||||
| <p class="ui" id="repo-topics"> | <p class="ui" id="repo-topics"> | ||||
| <i class="grey bookmark icon"></i> | <i class="grey bookmark icon"></i> | ||||
| {{range .Topics}}<a class="ui repo-topic small label topic" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=1">{{.Name}}</a>{{end}} | {{range .Topics}}<a class="ui repo-topic small label topic" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=1">{{.Name}}</a>{{end}} | ||||
| {{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<a id="manage_topic">{{.i18n.Tr "repo.topic.manage_topics"}}</a>{{end}} | {{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<a id="manage_topic">{{.i18n.Tr "repo.topic.manage_topics"}}</a>{{end}} | ||||
| </p> | </p> | ||||
| <p class="ui"> | <p class="ui"> | ||||
| <i class="grey code icon"></i> | <i class="grey code icon"></i> | ||||
| {{range .LanguageStats}} | {{range .LanguageStats}} | ||||
| {{.Language}} | {{.Language}} | ||||
| {{end}} | {{end}} | ||||
| </p> | </p> | ||||
| {{if .LICENSE}} | |||||
| <p class="ui"> | <p class="ui"> | ||||
| <i class="grey clone icon"></i> | <i class="grey clone icon"></i> | ||||
| {{if .LICENSE}} | |||||
| {{.LICENSE}} | |||||
| {{end}} | |||||
| {{.LICENSE}} | |||||
| </p> | </p> | ||||
| {{end}} | |||||
| <div class="ui divider"></div> | <div class="ui divider"></div> | ||||
| @@ -6,7 +6,7 @@ | |||||
| <img class="ui centered medium image" src="{{StaticUrlPrefix}}/img/icon-500@2x.png"> | <img class="ui centered medium image" src="{{StaticUrlPrefix}}/img/icon-500@2x.png"> | ||||
| <h2>{{.i18n.Tr "error500_index"}}</h2> | <h2>{{.i18n.Tr "error500_index"}}</h2> | ||||
| <p>{{.i18n.Tr "error404" | Safe}}</p> | |||||
| <p>{{.i18n.Tr "error500" | Safe}}</p> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -23,7 +23,7 @@ | |||||
| </div> | </div> | ||||
| <div class="required field {{if .Err_Email}}error{{end}}"> | <div class="required field {{if .Err_Email}}error{{end}}"> | ||||
| <label for="email">{{.i18n.Tr "email"}}</label> | <label for="email">{{.i18n.Tr "email"}}</label> | ||||
| <input id="email" name="email" value="{{.SignedUser.Email}}"> | |||||
| <input id="email" name="email" disabled value="{{.SignedUser.Email}}"> | |||||
| </div> | </div> | ||||
| <div class="inline field"> | <div class="inline field"> | ||||
| <div class="ui checkbox" id="keep-email-private"> | <div class="ui checkbox" id="keep-email-private"> | ||||
| @@ -0,0 +1,144 @@ | |||||
| <template> | |||||
| <div> | |||||
| <h4 id="about-desc" class="ui header">简介 | |||||
| <!-- <a class="edit-icon" href="javascript:void(0)" @click="editClick"> | |||||
| <i class="gray edit outline icon"></i> | |||||
| </a> --> | |||||
| </h4> | |||||
| <edit-dialog-cmpt | |||||
| :vmContext="vmContext" | |||||
| dialogTitle="编辑仓库信息" | |||||
| v-model="editDataDialog" | |||||
| :deleteCallback="editDataFunc" | |||||
| :deleteLoading ="editDataListLoading" | |||||
| deleteParam = "ruleForm" | |||||
| @input="initForm" | |||||
| > | |||||
| <div slot="title"> | |||||
| </div> | |||||
| <div slot="content"> | |||||
| <el-form label-position="top" :model="info" :rules="rule" ref="ruleForm"> | |||||
| <el-form-item label="简介" prop="desc"> | |||||
| <el-input v-model="info.desc" type="textarea" :autosize="{minRows:2,maxRows:6}"></el-input> | |||||
| </el-form-item> | |||||
| <el-form-item label="主页" prop="index_web" > | |||||
| <el-input v-model="info.index_web" placeholder="主页(eg: https://git.openi.org.cn)"></el-input> | |||||
| </el-form-item> | |||||
| </el-form> | |||||
| </div> | |||||
| </edit-dialog-cmpt> | |||||
| </div> | |||||
| </template> | |||||
| <script> | |||||
| const {_AppSubUrl, _StaticUrlPrefix, csrf} = window.config; | |||||
| import editDialogCmpt from './basic/editDialog.vue'; | |||||
| export default { | |||||
| components: { | |||||
| editDialogCmpt, | |||||
| }, | |||||
| data() { | |||||
| return { | |||||
| vmContext: this, | |||||
| editDataDialog: false, | |||||
| editDataListLoading: false, | |||||
| url: '', | |||||
| info: { | |||||
| desc: '', | |||||
| index_web: '', | |||||
| repo_name_name: '', | |||||
| }, | |||||
| // rule1:[{min:3,max:5,message:'1',trigger:"blur"}], | |||||
| rule: { | |||||
| index_web: [ | |||||
| {required: false, pattern: /(^$)|(^(http|https):\/\/(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).*)|(^(http|https):\/\/[a-zA-Z0-9]+([_\-\.][a-zA-Z0-9]+)*\.[a-zA-Z]{2,10}(:[0-9]{1,10})?(\?.*)?(\/.*)?$)/,message:'请输入有效的URL',tigger:['change','blur']} | |||||
| ] | |||||
| } | |||||
| }; | |||||
| }, | |||||
| methods: { | |||||
| editClick() { | |||||
| this.editDataDialog = true; | |||||
| }, | |||||
| getDesc() { | |||||
| const el = $('span.description').text(); | |||||
| this.info.desc = el; | |||||
| }, | |||||
| getWeb() { | |||||
| const el = $('a.link').text(); | |||||
| this.info.index_web = el; | |||||
| }, | |||||
| getRepoName() { | |||||
| const el = this.url.split('/')[2]; | |||||
| this.info.repo_name = el; | |||||
| }, | |||||
| initForm(diaolog) { | |||||
| if (diaolog === false) { | |||||
| console.log("--watch----------") | |||||
| this.getRepoName(); | |||||
| this.getDesc(); | |||||
| this.getWeb(); | |||||
| } | |||||
| }, | |||||
| editDataFunc(formName) { | |||||
| this.$refs[formName].validate((valid)=>{ | |||||
| if (valid) { | |||||
| this.$axios({ | |||||
| method: 'post', | |||||
| url: this.url, | |||||
| header: {'content-type': 'application/x-www-form-urlencoded'}, | |||||
| data: this.qs.stringify({ | |||||
| _csrf: csrf, | |||||
| action: 'update', | |||||
| repo_name: this.info.repo_name, | |||||
| description: this.info.desc, | |||||
| website: this.info.index_web | |||||
| }) | |||||
| }).then((res) => { | |||||
| location.reload(); | |||||
| this.editDataDialog = false; | |||||
| }).catch((error) => { | |||||
| this.editDataDialog = false; | |||||
| }) | |||||
| } | |||||
| else { | |||||
| return false; | |||||
| } | |||||
| }) | |||||
| }, | |||||
| getUrl() { | |||||
| const url = `${window.location.pathname}/settings`; | |||||
| this.url = url; | |||||
| } | |||||
| }, | |||||
| mounted() { | |||||
| this.getUrl(); | |||||
| this.getRepoName(); | |||||
| this.getDesc(); | |||||
| this.getWeb(); | |||||
| }, | |||||
| created() { | |||||
| } | |||||
| }; | |||||
| </script> | |||||
| <style scoped> | |||||
| .edit-icon{ | |||||
| float: right; | |||||
| font-size: 16px; | |||||
| display: block; | |||||
| top: -2px; | |||||
| color: #8c92a4; | |||||
| background-color: transparent; | |||||
| } | |||||
| </style> | |||||
| @@ -335,8 +335,19 @@ export default { | |||||
| async function uploadMinio(url, e) { | async function uploadMinio(url, e) { | ||||
| const res = await axios.put(url, e.target.result); | const res = await axios.put(url, e.target.result); | ||||
| delete e.target.result | |||||
| delete e.target.result | |||||
| etags[currentChunk] = res.headers.etag; | 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) { | async function updateChunk(currentChunk) { | ||||
| @@ -359,8 +370,10 @@ export default { | |||||
| // 获取分片上传url | // 获取分片上传url | ||||
| await getUploadChunkUrl(currentChunk, partSize); | await getUploadChunkUrl(currentChunk, partSize); | ||||
| if (urls[currentChunk] != '') { | if (urls[currentChunk] != '') { | ||||
| // 上传到minio | |||||
| await uploadMinio(urls[currentChunk], e); | |||||
| // 上传到minio | |||||
| //await uploadMinio(urls[currentChunk], e); | |||||
| await uploadMinioNewMethod(urls[currentChunk], e); | |||||
| if (etags[currentChunk] != '') { | if (etags[currentChunk] != '') { | ||||
| // 更新数据库:分片上传结果 | // 更新数据库:分片上传结果 | ||||
| //await updateChunk(currentChunk); | //await updateChunk(currentChunk); | ||||
| @@ -372,8 +385,9 @@ export default { | |||||
| } | } | ||||
| } | } | ||||
| } catch (error) { | } catch (error) { | ||||
| this.emitDropzoneFailed(file); | |||||
| console.log(error); | console.log(error); | ||||
| //this.emitDropzoneFailed(file); | |||||
| //console.log(error); | |||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,105 @@ | |||||
| <template> | |||||
| <el-dialog :close-on-click-modal="!deleteLoading" v-dlg-drag :title="dialogTitle" :visible.sync="deleteDialog"> | |||||
| <div class="message-box__content"> | |||||
| <div class="message-box-title" > | |||||
| <slot name="title"></slot> | |||||
| </div> | |||||
| <div class="message-box-info"> | |||||
| <slot name="info"></slot> | |||||
| </div> | |||||
| </div> | |||||
| <slot name="content"></slot> | |||||
| <div slot="footer" class="dialog-footer"> | |||||
| <el-button @click="deleteDialog = false">{{"取消"}}</el-button> | |||||
| <el-button type="primary" @click="deleteCallback.call(vmContext,deleteParam)">{{"确定"}}</el-button> | |||||
| </div> | |||||
| </el-dialog> | |||||
| </template> | |||||
| <script type="text/javascript"> | |||||
| export default { | |||||
| data() { | |||||
| return { | |||||
| deleteDialog: false, | |||||
| }; | |||||
| }, | |||||
| props: { | |||||
| vmContext: { | |||||
| type: Object, | |||||
| default() { | |||||
| return {}; | |||||
| } | |||||
| }, | |||||
| dialogTitle: { | |||||
| type: String, | |||||
| default: '', | |||||
| }, | |||||
| deleteLoading: { | |||||
| type: Boolean, | |||||
| default: false, | |||||
| }, | |||||
| deleteCallback: { | |||||
| type: Function, | |||||
| default() { | |||||
| return () => {}; | |||||
| } | |||||
| }, | |||||
| deleteParam: { | |||||
| type: String, | |||||
| default: '' | |||||
| }, | |||||
| value: { | |||||
| type: Boolean, | |||||
| default: false, | |||||
| } | |||||
| }, | |||||
| computed: { | |||||
| }, | |||||
| watch: { | |||||
| deleteDialog() { | |||||
| this.$emit('input', this.deleteDialog); | |||||
| }, | |||||
| value() { | |||||
| this.deleteDialog = this.value; | |||||
| }, | |||||
| }, | |||||
| created() { | |||||
| this.deleteDialog = this.value; | |||||
| } | |||||
| }; | |||||
| </script> | |||||
| <style scoped> | |||||
| .el-message-box__content .icon{float:left;} | |||||
| .message-box__content{} | |||||
| .message-box__content .icon{float:left;margin-right:20px;} | |||||
| .message-box__content .message-box-title{font-size:16px;padding:2px 0;color:#333;} | |||||
| .message-box__content .message-box-p{font-size:16px;padding:20px 0;color:#333;} | |||||
| .message-box__content .message-box-info{color:#999;padding:2px 0;} | |||||
| .dialog-footer,.el-message-box__btns{text-align:center;} | |||||
| /deep/ .el-dialog__header { | |||||
| background: #0067b3; | |||||
| padding: 12px 30px; | |||||
| line-height: 25px;} | |||||
| /deep/ .el-dialog__title { | |||||
| font-family: PingFangSC-Regular; | |||||
| font-size: 18px; | |||||
| color: #fff; | |||||
| font-weight: 200; | |||||
| line-height: 25px; | |||||
| height: 25px; | |||||
| } | |||||
| /deep/ .el-dialog__footer { | |||||
| background: #eff3f9; | |||||
| padding: 20px 30px; | |||||
| } | |||||
| /deep/ .el-dialog{ | |||||
| width: 40%; | |||||
| } | |||||
| </style> | |||||
| @@ -6,6 +6,10 @@ import './publicpath.js'; | |||||
| import './polyfills.js'; | import './polyfills.js'; | ||||
| import Vue from 'vue'; | 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 'jquery.are-you-sure'; | ||||
| import './vendor/semanticdropdown.js'; | import './vendor/semanticdropdown.js'; | ||||
| import {svg} from './utils.js'; | import {svg} from './utils.js'; | ||||
| @@ -29,8 +33,12 @@ import { | |||||
| } from './features/notification.js'; | } from './features/notification.js'; | ||||
| import {createCodeEditor} from './features/codeeditor.js'; | import {createCodeEditor} from './features/codeeditor.js'; | ||||
| import MinioUploader from './components/MinioUploader.vue'; | 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; | const {AppSubUrl, StaticUrlPrefix, csrf} = window.config; | ||||
| function htmlEncode(text) { | function htmlEncode(text) { | ||||
| @@ -2957,6 +2965,7 @@ $(document).ready(async () => { | |||||
| initVueApp(); | initVueApp(); | ||||
| initVueUploader(); | initVueUploader(); | ||||
| initObsUploader(); | initObsUploader(); | ||||
| initVueEditAbout(); | |||||
| initTeamSettings(); | initTeamSettings(); | ||||
| initCtrlEnterSubmit(); | initCtrlEnterSubmit(); | ||||
| initNavbarContentToggle(); | 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() { | function initObsUploader() { | ||||
| const el = document.getElementById('obsUploader'); | const el = document.getElementById('obsUploader'); | ||||