This commit is contained in:
parent
61e4d6fad3
commit
0647b62048
18
go.mod
18
go.mod
@ -5,10 +5,6 @@ go 1.25rc3
|
||||
require (
|
||||
github.com/99designs/gqlgen v0.17.77
|
||||
github.com/aws/aws-sdk-go v1.55.8
|
||||
github.com/aws/aws-sdk-go-v2 v1.38.2
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.4
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.8
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.87.2
|
||||
github.com/caarlos0/env/v8 v8.0.0
|
||||
github.com/go-chi/chi/v5 v5.2.2
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||
@ -26,20 +22,6 @@ require (
|
||||
|
||||
require (
|
||||
github.com/agnivade/levenshtein v1.2.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.28.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.38.1 // indirect
|
||||
github.com/aws/smithy-go v1.23.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
|
||||
@ -140,7 +140,7 @@ type ComplexityRoot struct {
|
||||
ConfirmEmail func(childComplexity int, token string) int
|
||||
CreateChat func(childComplexity int, user1Id int, user2Id int) int
|
||||
CreateComment func(childComplexity int, postID int, content string) int
|
||||
CreatePost func(childComplexity int, input CreatePostInput) int
|
||||
CreatePost func(childComplexity int, title string, content string) int
|
||||
DeletePost func(childComplexity int, id int) int
|
||||
FollowUser func(childComplexity int, followingID int) int
|
||||
LikePost func(childComplexity int, postID int) int
|
||||
@ -180,7 +180,6 @@ type ComplexityRoot struct {
|
||||
|
||||
Query struct {
|
||||
Comments func(childComplexity int, postID int) int
|
||||
GenerateUploadURL func(childComplexity int, filename string) int
|
||||
GetChat func(childComplexity int, user1Id int, user2Id int) int
|
||||
GetChatMessages func(childComplexity int, chatID int, limit int, offset int) int
|
||||
GetFollowers func(childComplexity int, userID int, limit *int, offset *int) int
|
||||
@ -235,12 +234,6 @@ type ComplexityRoot struct {
|
||||
Success func(childComplexity int) int
|
||||
}
|
||||
|
||||
UploadURL struct {
|
||||
ExpiresAt func(childComplexity int) int
|
||||
Key func(childComplexity int) int
|
||||
URL func(childComplexity int) int
|
||||
}
|
||||
|
||||
User struct {
|
||||
Avatar func(childComplexity int) int
|
||||
CreatedAt func(childComplexity int) int
|
||||
@ -284,7 +277,7 @@ type MutationResolver interface {
|
||||
Register(ctx context.Context, input domain.RegisterInput) (*domain.User, error)
|
||||
Login(ctx context.Context, input domain.LoginInput) (*domain.Tokens, error)
|
||||
RefreshTokens(ctx context.Context, refreshToken string) (*domain.Tokens, error)
|
||||
CreatePost(ctx context.Context, input CreatePostInput) (*domain.Post, error)
|
||||
CreatePost(ctx context.Context, title string, content string) (*domain.Post, error)
|
||||
CreateComment(ctx context.Context, postID int, content string) (*domain.Comment, error)
|
||||
LikePost(ctx context.Context, postID int) (*domain.Like, error)
|
||||
UnlikePost(ctx context.Context, postID int) (bool, error)
|
||||
@ -317,7 +310,6 @@ type QueryResolver interface {
|
||||
Post(ctx context.Context, id int) (*domain.Post, error)
|
||||
Posts(ctx context.Context) ([]*domain.Post, error)
|
||||
PostsPaginated(ctx context.Context, limit int, offset int) ([]*domain.Post, error)
|
||||
GenerateUploadURL(ctx context.Context, filename string) (*UploadURL, error)
|
||||
GetUserPosts(ctx context.Context, userID int) ([]*domain.Post, error)
|
||||
User(ctx context.Context, id int) (*domain.User, error)
|
||||
Users(ctx context.Context) ([]*domain.User, error)
|
||||
@ -741,7 +733,7 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return e.complexity.Mutation.CreatePost(childComplexity, args["input"].(CreatePostInput)), true
|
||||
return e.complexity.Mutation.CreatePost(childComplexity, args["title"].(string), args["content"].(string)), true
|
||||
|
||||
case "Mutation.deletePost":
|
||||
if e.complexity.Mutation.DeletePost == nil {
|
||||
@ -1028,18 +1020,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
||||
|
||||
return e.complexity.Query.Comments(childComplexity, args["postID"].(int)), true
|
||||
|
||||
case "Query.generateUploadURL":
|
||||
if e.complexity.Query.GenerateUploadURL == nil {
|
||||
break
|
||||
}
|
||||
|
||||
args, err := ec.field_Query_generateUploadURL_args(ctx, rawArgs)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return e.complexity.Query.GenerateUploadURL(childComplexity, args["filename"].(string)), true
|
||||
|
||||
case "Query.getChat":
|
||||
if e.complexity.Query.GetChat == nil {
|
||||
break
|
||||
@ -1369,27 +1349,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
||||
|
||||
return e.complexity.UnfollowResult.Success(childComplexity), true
|
||||
|
||||
case "UploadURL.expiresAt":
|
||||
if e.complexity.UploadURL.ExpiresAt == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.UploadURL.ExpiresAt(childComplexity), true
|
||||
|
||||
case "UploadURL.key":
|
||||
if e.complexity.UploadURL.Key == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.UploadURL.Key(childComplexity), true
|
||||
|
||||
case "UploadURL.url":
|
||||
if e.complexity.UploadURL.URL == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.UploadURL.URL(childComplexity), true
|
||||
|
||||
case "User.avatar":
|
||||
if e.complexity.User.Avatar == nil {
|
||||
break
|
||||
@ -1509,7 +1468,6 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
|
||||
opCtx := graphql.GetOperationContext(ctx)
|
||||
ec := executionContext{opCtx, e, 0, 0, make(chan graphql.DeferredResult)}
|
||||
inputUnmarshalMap := graphql.BuildUnmarshalerMap(
|
||||
ec.unmarshalInputCreatePostInput,
|
||||
ec.unmarshalInputLoginInput,
|
||||
ec.unmarshalInputRegisterInput,
|
||||
)
|
||||
@ -1727,11 +1685,16 @@ func (ec *executionContext) field_Mutation_createComment_args(ctx context.Contex
|
||||
func (ec *executionContext) field_Mutation_createPost_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
|
||||
var err error
|
||||
args := map[string]any{}
|
||||
arg0, err := processArgField(ctx, rawArgs, "input", ec.unmarshalNCreatePostInput2tailly_back_v2ᚋinternalᚋhttpᚋgraphᚐCreatePostInput)
|
||||
arg0, err := processArgField(ctx, rawArgs, "title", ec.unmarshalNString2string)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args["input"] = arg0
|
||||
args["title"] = arg0
|
||||
arg1, err := processArgField(ctx, rawArgs, "content", ec.unmarshalNString2string)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args["content"] = arg1
|
||||
return args, nil
|
||||
}
|
||||
|
||||
@ -1936,17 +1899,6 @@ func (ec *executionContext) field_Query_comments_args(ctx context.Context, rawAr
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Query_generateUploadURL_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
|
||||
var err error
|
||||
args := map[string]any{}
|
||||
arg0, err := processArgField(ctx, rawArgs, "filename", ec.unmarshalNString2string)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args["filename"] = arg0
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Query_getChatMessages_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
|
||||
var err error
|
||||
args := map[string]any{}
|
||||
@ -4567,7 +4519,7 @@ func (ec *executionContext) _Mutation_createPost(ctx context.Context, field grap
|
||||
}()
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return ec.resolvers.Mutation().CreatePost(rctx, fc.Args["input"].(CreatePostInput))
|
||||
return ec.resolvers.Mutation().CreatePost(rctx, fc.Args["title"].(string), fc.Args["content"].(string))
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
@ -6589,69 +6541,6 @@ func (ec *executionContext) fieldContext_Query_postsPaginated(ctx context.Contex
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Query_generateUploadURL(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_Query_generateUploadURL(ctx, field)
|
||||
if err != nil {
|
||||
return graphql.Null
|
||||
}
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return ec.resolvers.Query().GenerateUploadURL(rctx, fc.Args["filename"].(string))
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(*UploadURL)
|
||||
fc.Result = res
|
||||
return ec.marshalNUploadURL2ᚖtailly_back_v2ᚋinternalᚋhttpᚋgraphᚐUploadURL(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_Query_generateUploadURL(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "Query",
|
||||
Field: field,
|
||||
IsMethod: true,
|
||||
IsResolver: true,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
switch field.Name {
|
||||
case "url":
|
||||
return ec.fieldContext_UploadURL_url(ctx, field)
|
||||
case "key":
|
||||
return ec.fieldContext_UploadURL_key(ctx, field)
|
||||
case "expiresAt":
|
||||
return ec.fieldContext_UploadURL_expiresAt(ctx, field)
|
||||
}
|
||||
return nil, fmt.Errorf("no field named %q was found under type UploadURL", field.Name)
|
||||
},
|
||||
}
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = ec.Recover(ctx, r)
|
||||
ec.Error(ctx, err)
|
||||
}
|
||||
}()
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
if fc.Args, err = ec.field_Query_generateUploadURL_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return fc, err
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Query_getUserPosts(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_Query_getUserPosts(ctx, field)
|
||||
if err != nil {
|
||||
@ -8632,138 +8521,6 @@ func (ec *executionContext) fieldContext_UnfollowResult_message(_ context.Contex
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _UploadURL_url(ctx context.Context, field graphql.CollectedField, obj *UploadURL) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_UploadURL_url(ctx, field)
|
||||
if err != nil {
|
||||
return graphql.Null
|
||||
}
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.URL, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(string)
|
||||
fc.Result = res
|
||||
return ec.marshalNString2string(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_UploadURL_url(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "UploadURL",
|
||||
Field: field,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
return nil, errors.New("field of type String does not have child fields")
|
||||
},
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _UploadURL_key(ctx context.Context, field graphql.CollectedField, obj *UploadURL) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_UploadURL_key(ctx, field)
|
||||
if err != nil {
|
||||
return graphql.Null
|
||||
}
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.Key, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(string)
|
||||
fc.Result = res
|
||||
return ec.marshalNString2string(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_UploadURL_key(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "UploadURL",
|
||||
Field: field,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
return nil, errors.New("field of type String does not have child fields")
|
||||
},
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _UploadURL_expiresAt(ctx context.Context, field graphql.CollectedField, obj *UploadURL) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_UploadURL_expiresAt(ctx, field)
|
||||
if err != nil {
|
||||
return graphql.Null
|
||||
}
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.ExpiresAt, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(string)
|
||||
fc.Result = res
|
||||
return ec.marshalNString2string(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_UploadURL_expiresAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "UploadURL",
|
||||
Field: field,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
return nil, errors.New("field of type String does not have child fields")
|
||||
},
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _User_id(ctx context.Context, field graphql.CollectedField, obj *domain.User) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_User_id(ctx, field)
|
||||
if err != nil {
|
||||
@ -11348,40 +11105,6 @@ func (ec *executionContext) fieldContext___Type_isOneOf(_ context.Context, field
|
||||
|
||||
// region **************************** input.gotpl *****************************
|
||||
|
||||
func (ec *executionContext) unmarshalInputCreatePostInput(ctx context.Context, obj any) (CreatePostInput, error) {
|
||||
var it CreatePostInput
|
||||
asMap := map[string]any{}
|
||||
for k, v := range obj.(map[string]any) {
|
||||
asMap[k] = v
|
||||
}
|
||||
|
||||
fieldsInOrder := [...]string{"title", "s3TempKey"}
|
||||
for _, k := range fieldsInOrder {
|
||||
v, ok := asMap[k]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
switch k {
|
||||
case "title":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("title"))
|
||||
data, err := ec.unmarshalNString2string(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.Title = data
|
||||
case "s3TempKey":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("s3TempKey"))
|
||||
data, err := ec.unmarshalNString2string(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.S3TempKey = data
|
||||
}
|
||||
}
|
||||
|
||||
return it, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalInputLoginInput(ctx context.Context, obj any) (domain.LoginInput, error) {
|
||||
var it domain.LoginInput
|
||||
asMap := map[string]any{}
|
||||
@ -13073,28 +12796,6 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
|
||||
func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
|
||||
}
|
||||
|
||||
out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) })
|
||||
case "generateUploadURL":
|
||||
field := field
|
||||
|
||||
innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
}
|
||||
}()
|
||||
res = ec._Query_generateUploadURL(ctx, field)
|
||||
if res == graphql.Null {
|
||||
atomic.AddUint32(&fs.Invalids, 1)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
rrm := func(ctx context.Context) graphql.Marshaler {
|
||||
return ec.OperationContext.RootResolverMiddleware(ctx,
|
||||
func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
|
||||
}
|
||||
|
||||
out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) })
|
||||
case "getUserPosts":
|
||||
field := field
|
||||
@ -13810,55 +13511,6 @@ func (ec *executionContext) _UnfollowResult(ctx context.Context, sel ast.Selecti
|
||||
return out
|
||||
}
|
||||
|
||||
var uploadURLImplementors = []string{"UploadURL"}
|
||||
|
||||
func (ec *executionContext) _UploadURL(ctx context.Context, sel ast.SelectionSet, obj *UploadURL) graphql.Marshaler {
|
||||
fields := graphql.CollectFields(ec.OperationContext, sel, uploadURLImplementors)
|
||||
|
||||
out := graphql.NewFieldSet(fields)
|
||||
deferred := make(map[string]*graphql.FieldSet)
|
||||
for i, field := range fields {
|
||||
switch field.Name {
|
||||
case "__typename":
|
||||
out.Values[i] = graphql.MarshalString("UploadURL")
|
||||
case "url":
|
||||
out.Values[i] = ec._UploadURL_url(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
}
|
||||
case "key":
|
||||
out.Values[i] = ec._UploadURL_key(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
}
|
||||
case "expiresAt":
|
||||
out.Values[i] = ec._UploadURL_expiresAt(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
}
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
}
|
||||
}
|
||||
out.Dispatch(ctx)
|
||||
if out.Invalids > 0 {
|
||||
return graphql.Null
|
||||
}
|
||||
|
||||
atomic.AddInt32(&ec.deferred, int32(len(deferred)))
|
||||
|
||||
for label, dfs := range deferred {
|
||||
ec.processDeferredGroup(graphql.DeferredGroup{
|
||||
Label: label,
|
||||
Path: graphql.GetPath(ctx),
|
||||
FieldSet: dfs,
|
||||
Context: ctx,
|
||||
})
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
var userImplementors = []string{"User"}
|
||||
|
||||
func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj *domain.User) graphql.Marshaler {
|
||||
@ -14701,11 +14353,6 @@ func (ec *executionContext) marshalNComment2ᚖtailly_back_v2ᚋinternalᚋdomai
|
||||
return ec._Comment(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNCreatePostInput2tailly_back_v2ᚋinternalᚋhttpᚋgraphᚐCreatePostInput(ctx context.Context, v any) (CreatePostInput, error) {
|
||||
res, err := ec.unmarshalInputCreatePostInput(ctx, v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNDevice2tailly_back_v2ᚋinternalᚋdomainᚐDevice(ctx context.Context, sel ast.SelectionSet, v domain.Device) graphql.Marshaler {
|
||||
return ec._Device(ctx, sel, &v)
|
||||
}
|
||||
@ -15260,20 +14907,6 @@ func (ec *executionContext) marshalNUnfollowResult2ᚖtailly_back_v2ᚋinternal
|
||||
return ec._UnfollowResult(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNUploadURL2tailly_back_v2ᚋinternalᚋhttpᚋgraphᚐUploadURL(ctx context.Context, sel ast.SelectionSet, v UploadURL) graphql.Marshaler {
|
||||
return ec._UploadURL(ctx, sel, &v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNUploadURL2ᚖtailly_back_v2ᚋinternalᚋhttpᚋgraphᚐUploadURL(ctx context.Context, sel ast.SelectionSet, v *UploadURL) graphql.Marshaler {
|
||||
if v == nil {
|
||||
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
||||
ec.Errorf(ctx, "the requested element is null which the schema does not allow")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
return ec._UploadURL(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNUser2tailly_back_v2ᚋinternalᚋdomainᚐUser(ctx context.Context, sel ast.SelectionSet, v domain.User) graphql.Marshaler {
|
||||
return ec._User(ctx, sel, &v)
|
||||
}
|
||||
|
||||
@ -9,11 +9,6 @@ import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type CreatePostInput struct {
|
||||
Title string `json:"title"`
|
||||
S3TempKey string `json:"s3TempKey"`
|
||||
}
|
||||
|
||||
type FollowResult struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
@ -78,12 +73,6 @@ type UnfollowResult struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type UploadURL struct {
|
||||
URL string `json:"url"`
|
||||
Key string `json:"key"`
|
||||
ExpiresAt string `json:"expiresAt"`
|
||||
}
|
||||
|
||||
type MessageStatus string
|
||||
|
||||
const (
|
||||
|
||||
@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"tailly_back_v2/internal/domain"
|
||||
"tailly_back_v2/pkg/S3"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -125,38 +124,19 @@ func (r *mutationResolver) UnlikePost(ctx context.Context, postID int) (bool, er
|
||||
}
|
||||
|
||||
// CreatePost is the resolver for the createPost field.
|
||||
func (r *mutationResolver) CreatePost(ctx context.Context, input CreatePostInput) (*domain.Post, error) {
|
||||
func (r *mutationResolver) CreatePost(ctx context.Context, title string, content string) (*domain.Post, error) {
|
||||
userID, err := getUserIDFromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unauthorized: %w", err)
|
||||
}
|
||||
|
||||
post, err := r.Services.Post.CreateWithS3Upload(ctx, userID, input.Title, input.S3TempKey)
|
||||
post, err := r.Services.Post.Create(ctx, userID, title, content)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create post: %w", err)
|
||||
}
|
||||
return post, nil
|
||||
}
|
||||
|
||||
// GenerateUploadURL is the resolver for the generateUploadURL field.
|
||||
func (r *queryResolver) GenerateUploadURL(ctx context.Context, filename string) (*UploadURL, error) {
|
||||
userID, err := getUserIDFromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unauthorized: %w", err)
|
||||
}
|
||||
|
||||
url, key, err := S3.GeneratePresignedUploadURL(userID, filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate upload URL: %w", err)
|
||||
}
|
||||
|
||||
return &UploadURL{
|
||||
URL: url,
|
||||
Key: key,
|
||||
ExpiresAt: time.Now().Add(15 * time.Minute).Format(time.RFC3339),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeletePost is the resolver for the deletePost field.
|
||||
func (r *mutationResolver) DeletePost(ctx context.Context, id int) (bool, error) {
|
||||
userID, err := getUserIDFromContext(ctx)
|
||||
|
||||
@ -170,26 +170,12 @@ type MarkNotificationReadResult {
|
||||
message: String!
|
||||
}
|
||||
|
||||
scalar Upload
|
||||
|
||||
input CreatePostInput {
|
||||
title: String!
|
||||
s3TempKey: String!
|
||||
}
|
||||
|
||||
type UploadURL {
|
||||
url: String!
|
||||
key: String!
|
||||
expiresAt: String!
|
||||
}
|
||||
|
||||
# Запросы (получение данных)
|
||||
type Query {
|
||||
me: User! # Получить текущего пользователя
|
||||
post(id: Int!): Post! # Получить пост по ID
|
||||
posts: [Post!]! # Получить все посты
|
||||
postsPaginated(limit: Int!, offset: Int!): [Post!]!
|
||||
generateUploadURL(filename: String!): UploadURL!
|
||||
getUserPosts(userId: Int!): [Post!]!
|
||||
user(id: Int!): User! # Получить пользователя по ID
|
||||
users: [User!]!
|
||||
@ -200,14 +186,19 @@ type Query {
|
||||
comments(postID: Int!): [Comment!]!
|
||||
# Получить количество подписчиков
|
||||
getFollowersCount(userId: Int!): Int!
|
||||
|
||||
# Получить количество подписок
|
||||
getFollowingCount(userId: Int!): Int!
|
||||
|
||||
# Проверить подписку
|
||||
isFollowing(followingId: Int!): Boolean!
|
||||
|
||||
# Получить список подписчиков
|
||||
getFollowers(userId: Int!, limit: Int = 20, offset: Int = 0): FollowersResponse!
|
||||
|
||||
# Получить список подписок
|
||||
getFollowing(userId: Int!, limit: Int = 20, offset: Int = 0): FollowingResponse!
|
||||
|
||||
# Получить уведомления о подписках
|
||||
getSubscriptionNotifications(
|
||||
unreadOnly: Boolean = false
|
||||
@ -228,7 +219,8 @@ type Mutation {
|
||||
refreshTokens(refreshToken: String!): Tokens!
|
||||
|
||||
# Создание поста
|
||||
createPost(input: CreatePostInput!): Post!
|
||||
createPost(title: String!, content: String!): Post!
|
||||
|
||||
# Создание комментария
|
||||
createComment(postId: Int!, content: String!): Comment!
|
||||
|
||||
|
||||
@ -32,7 +32,6 @@ type PostService interface {
|
||||
Update(ctx context.Context, id int, title, content string) (*domain.Post, error)
|
||||
Delete(ctx context.Context, id int) error
|
||||
GetPaginated(ctx context.Context, limit, offset int) ([]*domain.Post, error)
|
||||
CreateWithS3Upload(ctx context.Context, authorID int, title, s3TempKey string) (*domain.Post, error)
|
||||
}
|
||||
|
||||
// Реализация сервиса постов
|
||||
@ -45,66 +44,6 @@ func NewPostService(postRepo repository.PostRepository) PostService {
|
||||
return &postService{postRepo: postRepo}
|
||||
}
|
||||
|
||||
func (s *postService) CreateWithS3Upload(ctx context.Context, authorID int, title, s3Key string) (*domain.Post, error) {
|
||||
const op = "service/postService.CreateWithS3Upload"
|
||||
|
||||
// Валидация
|
||||
if s3Key == "" {
|
||||
return nil, errors.New("S3 key cannot be empty")
|
||||
}
|
||||
|
||||
// Проверяем что ключ принадлежит пользователю и находится в правильной папке
|
||||
if !isValidUserPostKey(authorID, s3Key) {
|
||||
return nil, errors.New("invalid S3 key for user")
|
||||
}
|
||||
|
||||
// Формируем URL для модерации
|
||||
imageURL := fmt.Sprintf("https://s3.regru.cloud/tailly/%s", s3Key)
|
||||
|
||||
// Модерация изображения по URL
|
||||
modClient, err := moderation.NewModerationClient("tailly_censor:50051")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: failed to create moderation client: %w", op, err)
|
||||
}
|
||||
defer modClient.Close()
|
||||
|
||||
allowed, err := modClient.CheckImageURL(ctx, imageURL)
|
||||
if err != nil {
|
||||
// Удаляем файл при ошибке модерации
|
||||
S3.DeleteFromS3(imageURL)
|
||||
return nil, fmt.Errorf("%s: image moderation failed: %w", op, err)
|
||||
}
|
||||
if !allowed {
|
||||
// Удаляем файл если не прошло модерацию
|
||||
S3.DeleteFromS3(imageURL)
|
||||
return nil, errors.New("image rejected by moderation service")
|
||||
}
|
||||
|
||||
// Создаем пост с уже готовым URL
|
||||
post := &domain.Post{
|
||||
Title: title,
|
||||
Content: imageURL,
|
||||
AuthorID: authorID,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
|
||||
if err := s.postRepo.Create(ctx, post); err != nil {
|
||||
// Если не удалось сохранить пост, удаляем файл из S3
|
||||
S3.DeleteFromS3(imageURL)
|
||||
return nil, fmt.Errorf("%s: failed to save post: %w", op, err)
|
||||
}
|
||||
|
||||
return post, nil
|
||||
}
|
||||
|
||||
// Новая функция проверки ключа
|
||||
func isValidUserPostKey(userID int, key string) bool {
|
||||
// Проверяем что ключ принадлежит пользователю и находится в папке posts
|
||||
// Формат: posts/{userID}/{timestamp}_{filename}
|
||||
return strings.HasPrefix(key, fmt.Sprintf("posts/%d/", userID))
|
||||
}
|
||||
|
||||
// Создание нового поста
|
||||
func (s *postService) Create(ctx context.Context, authorID int, title, content string) (*domain.Post, error) {
|
||||
const op = "service/postService.Create"
|
||||
|
||||
90
pkg/S3/s3.go
90
pkg/S3/s3.go
@ -1,40 +1,15 @@
|
||||
package S3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
awsConfig "github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
)
|
||||
|
||||
// Получаем S3 клиент
|
||||
func getS3Client() (*s3.Client, error) {
|
||||
cfg, err := awsConfig.LoadDefaultConfig(context.TODO(),
|
||||
awsConfig.WithRegion("ru-central1"),
|
||||
awsConfig.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(
|
||||
"TJ946G2S1Z5FEI3I7DQQ",
|
||||
"C2H2aITHRDpek8H921yhnrINZwDoADsjW3F6HURl",
|
||||
"",
|
||||
)),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Используем virtual-hosted-style endpoint
|
||||
client := s3.NewFromConfig(cfg, func(o *s3.Options) {
|
||||
o.BaseEndpoint = aws.String("https://s3.regru.cloud")
|
||||
o.UsePathStyle = false // Отключаем path-style
|
||||
})
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func DeleteFromS3(imageURL string) error {
|
||||
const op = "s3.DeleteFromS3"
|
||||
|
||||
@ -45,12 +20,20 @@ func DeleteFromS3(imageURL string) error {
|
||||
bucket := parts[0]
|
||||
key := parts[1]
|
||||
|
||||
client, err := getS3Client()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: failed to create S3 client: %w", op, err)
|
||||
}
|
||||
sess := session.Must(session.NewSession(&aws.Config{
|
||||
Region: aws.String("ru-central1"),
|
||||
Endpoint: aws.String("https://s3.regru.cloud"),
|
||||
S3ForcePathStyle: aws.Bool(true),
|
||||
Credentials: credentials.NewStaticCredentials(
|
||||
"TJ946G2S1Z5FEI3I7DQQ",
|
||||
"C2H2aITHRDpek8H921yhnrINZwDoADsjW3F6HURl",
|
||||
"",
|
||||
),
|
||||
}))
|
||||
|
||||
_, err = client.DeleteObject(context.TODO(), &s3.DeleteObjectInput{
|
||||
svc := s3.New(sess)
|
||||
|
||||
_, err := svc.DeleteObject(&s3.DeleteObjectInput{
|
||||
Bucket: aws.String(bucket),
|
||||
Key: aws.String(key),
|
||||
})
|
||||
@ -58,48 +41,13 @@ func DeleteFromS3(imageURL string) error {
|
||||
return fmt.Errorf("%s: failed to delete object from S3: %w", op, err)
|
||||
}
|
||||
|
||||
// Ждем пока объект исчезнет
|
||||
waiter := s3.NewObjectNotExistsWaiter(client)
|
||||
err = waiter.Wait(context.TODO(), &s3.HeadObjectInput{
|
||||
err = svc.WaitUntilObjectNotExists(&s3.HeadObjectInput{
|
||||
Bucket: aws.String(bucket),
|
||||
Key: aws.String(key),
|
||||
}, 30*time.Second)
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: failed to wait for object deletion: %w", op, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GeneratePresignedUploadURL(userID int, filename string) (string, string, error) {
|
||||
const op = "s3.GeneratePresignedUploadURL"
|
||||
|
||||
client, err := getS3Client()
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("%s: failed to create S3 client: %w", op, err)
|
||||
}
|
||||
|
||||
uniqueKey := fmt.Sprintf("posts/%d/%d_%s", userID, time.Now().UnixNano(), filename)
|
||||
|
||||
presignClient := s3.NewPresignClient(client)
|
||||
|
||||
result, err := presignClient.PresignPutObject(context.TODO(),
|
||||
&s3.PutObjectInput{
|
||||
Bucket: aws.String("tailly"),
|
||||
Key: aws.String(uniqueKey),
|
||||
ContentLength: aws.Int64(10 * 1024 * 1024),
|
||||
// Добавляем CORS headers через метаданные
|
||||
Metadata: map[string]string{
|
||||
"Access-Control-Allow-Origin": "https://tailly.ru",
|
||||
"Access-Control-Allow-Methods": "PUT, POST, DELETE",
|
||||
"Access-Control-Allow-Headers": "*",
|
||||
},
|
||||
},
|
||||
s3.WithPresignExpires(15*time.Minute),
|
||||
)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("%s: failed to generate presigned URL: %w", op, err)
|
||||
}
|
||||
|
||||
return result.URL, uniqueKey, nil
|
||||
}
|
||||
|
||||
@ -3,9 +3,8 @@ package moderation
|
||||
import (
|
||||
"context"
|
||||
|
||||
pb "tailly_back_v2/pkg/moderation/proto"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
pb "tailly_back_v2/pkg/moderation/proto"
|
||||
)
|
||||
|
||||
type ModerationClient struct {
|
||||
@ -36,16 +35,6 @@ func (c *ModerationClient) CheckImage(ctx context.Context, imageData []byte) (bo
|
||||
return resp.OverallDecision == "allowed", nil
|
||||
}
|
||||
|
||||
func (c *ModerationClient) CheckImageURL(ctx context.Context, imageURL string) (bool, error) {
|
||||
resp, err := c.client.CheckImage(ctx, &pb.ImageRequest{
|
||||
ImageUrl: imageURL, // ← Передаем URL вместо данных!
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return resp.OverallDecision == "allowed", nil
|
||||
}
|
||||
|
||||
func (c *ModerationClient) Close() {
|
||||
c.conn.Close()
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user