package httpapi import ( "errors" "net/http" "strings" "time" "data-terminal-cloud/internal/concurrency" "data-terminal-cloud/internal/sharing" ) func CreateSharingGrantHandler(service *sharing.Service) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { principal, ok := webPrincipal(w, r) if !ok { return } key := strings.TrimSpace(r.Header.Get("Idempotency-Key")) if len(key) < 16 || len(key) > 128 { writeValidation(w, r, "缺少有效的重复请求识别码", FieldError{Field: "Idempotency-Key", Message: "长度必须为16至128字符"}) return } var body struct { ResourceType string `json:"resource_type"` ResourceID string `json:"resource_id"` GranteeUserID string `json:"grantee_user_id"` Permission string `json:"permission"` Purpose string `json:"purpose"` ExpiresAt time.Time `json:"expires_at"` LoginRequired *bool `json:"login_required"` } if !decodeWebJSON(w, r, &body, "分享授权参数不正确") { return } loginRequired := true if body.LoginRequired != nil { loginRequired = *body.LoginRequired } input := sharing.CreateInput{ResourceType: body.ResourceType, ResourceID: body.ResourceID, GranteeUserID: body.GranteeUserID, Permission: body.Permission, Purpose: body.Purpose, ExpiresAt: body.ExpiresAt, LoginRequired: loginRequired} if !validUUID(input.ResourceID) || !validUUID(input.GranteeUserID) || input.ExpiresAt.IsZero() { writeValidation(w, r, "分享授权参数不正确", FieldError{Field: "grant", Message: "资源、被分享用户和到期时间均为必填"}) return } item, created, err := service.Create(r.Context(), principal, key, RequestID(r.Context()), input) if err != nil { writeSharingError(w, r, err) return } status := http.StatusCreated if !created { status = http.StatusOK w.Header().Set("Idempotency-Replayed", "true") } w.Header().Set("Cache-Control", "no-store") WriteJSON(w, r, status, item) }) } func ListSharingGrantsHandler(service *sharing.Service) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { principal, ok := webPrincipal(w, r) if !ok { return } subjectID := strings.TrimSpace(r.URL.Query().Get("subject_id")) if !validUUID(subjectID) { writeValidation(w, r, "分享授权查询参数不正确", FieldError{Field: "subject_id", Message: "必须是有效UUID"}) return } items, err := service.List(r.Context(), principal, subjectID) if err != nil { writeSharingError(w, r, err) return } w.Header().Set("Cache-Control", "no-store") WriteJSON(w, r, http.StatusOK, map[string]any{"items": items}) }) } func RevokeSharingGrantHandler(service *sharing.Service) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { principal, ok := resourcePrincipal(w, r, "id") if !ok { return } item, err := service.Revoke(r.Context(), principal, r.PathValue("id"), RequestID(r.Context())) if err != nil { writeSharingError(w, r, err) return } w.Header().Set("Cache-Control", "no-store") WriteJSON(w, r, http.StatusOK, item) }) } func AccessSharedResourceHandler(service *sharing.Service) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { principal, ok := resourcePrincipal(w, r, "id") if !ok { return } item, err := service.Access(r.Context(), principal, r.PathValue("id"), RequestID(r.Context())) if err != nil { writeSharingError(w, r, err) return } w.Header().Set("Cache-Control", "no-store") WriteJSON(w, r, http.StatusOK, item) }) } func AccessPublicSharedResourceHandler(service *sharing.Service) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { id := strings.TrimSpace(r.PathValue("id")) token := strings.TrimSpace(r.Header.Get("X-Share-Token")) if !validUUID(id) || len(token) < 32 || len(token) > 256 { writeValidation(w, r, "分享链接参数不正确", FieldError{Field: "share", Message: "分享编号或访问口令无效"}) return } item, err := service.AccessByToken(r.Context(), id, token, RequestID(r.Context())) if err != nil { writeSharingError(w, r, err) return } w.Header().Set("Cache-Control", "no-store") WriteJSON(w, r, http.StatusOK, item) }) } func EditSharedStoryHandler(service *sharing.Service) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { principal, ok := resourcePrincipal(w, r, "id") if !ok { return } expected, err := concurrency.ParseIfMatch(r) if err != nil { writeETagError(w, r, err) return } var input struct { Content string `json:"content"` Reason string `json:"reason"` } if !decodeWebJSON(w, r, &input, "共享故事修改参数不正确") { return } item, err := service.EditStory(r.Context(), principal, r.PathValue("id"), input.Content, input.Reason, RequestID(r.Context()), expected) if err != nil { writeSharingError(w, r, err) return } w.Header().Set("ETag", concurrency.FormatETag(item.Version)) w.Header().Set("Cache-Control", "no-store") WriteJSON(w, r, http.StatusOK, item) }) } func writeSharingError(w http.ResponseWriter, r *http.Request, err error) { switch { case errors.Is(err, sharing.ErrInvalid): writeValidation(w, r, "分享授权参数不正确", FieldError{Field: "grant", Message: "资源类型、权限、用途或到期时间不符合要求"}) case errors.Is(err, sharing.ErrAccessDenied): WriteError(w, r, http.StatusForbidden, "SHARING_ACCESS_DENIED", "当前账号没有创建、管理或访问该分享的权限") case errors.Is(err, sharing.ErrExpired): WriteError(w, r, http.StatusGone, "SHARING_GRANT_INACTIVE", "分享已经撤销或过期") case errors.Is(err, sharing.ErrNotFound): WriteError(w, r, http.StatusNotFound, "RESOURCE_NOT_FOUND", "分享授权或目标资源不存在") case errors.Is(err, sharing.ErrVersionConflict): WriteError(w, r, http.StatusPreconditionFailed, "VERSION_CONFLICT", "共享资源已经被其他用户修改,请刷新后重试") default: WriteError(w, r, http.StatusInternalServerError, "INTERNAL_ERROR", "分享授权处理失败") } }