API: implement user account deletion
This commit is contained in:
parent
a26c7a828d
commit
0fb8803a60
|
@ -0,0 +1,53 @@
|
|||
package account
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"git.sr.ht/~sircmpwn/core-go/database"
|
||||
work "git.sr.ht/~sircmpwn/dowork"
|
||||
)
|
||||
|
||||
type contextKey struct {
|
||||
name string
|
||||
}
|
||||
|
||||
var ctxKey = &contextKey{"account"}
|
||||
|
||||
func Middleware(queue *work.Queue) func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := context.WithValue(r.Context(), ctxKey, queue)
|
||||
r = r.WithContext(ctx)
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Schedules a user account deletion.
|
||||
func Delete(ctx context.Context, userID int, username string) {
|
||||
queue, ok := ctx.Value(ctxKey).(*work.Queue)
|
||||
if !ok {
|
||||
panic("No account worker for this context")
|
||||
}
|
||||
|
||||
task := work.NewTask(func(ctx context.Context) error {
|
||||
log.Printf("Processing deletion of user account %d %s", userID, username)
|
||||
|
||||
if err := database.WithTx(ctx, nil, func(tx *sql.Tx) error {
|
||||
_, err := tx.ExecContext(ctx, `
|
||||
DELETE FROM "user" WHERE id = $1;
|
||||
`, userID)
|
||||
return err
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("Deletion of user account %d %s complete", userID, username)
|
||||
return nil
|
||||
})
|
||||
queue.Enqueue(task)
|
||||
log.Printf("Enqueued deletion of user account %d %s", userID, username)
|
||||
}
|
|
@ -4,6 +4,7 @@ go 1.17
|
|||
|
||||
require (
|
||||
git.sr.ht/~sircmpwn/core-go v0.0.0-20221025082458-3e69641ef307
|
||||
git.sr.ht/~sircmpwn/dowork v0.0.0-20210820133136-d3970e97def3
|
||||
github.com/99designs/gqlgen v0.17.20
|
||||
github.com/Masterminds/squirrel v1.5.0
|
||||
github.com/go-chi/chi v4.1.2+incompatible
|
||||
|
@ -14,7 +15,6 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
git.sr.ht/~sircmpwn/dowork v0.0.0-20210820133136-d3970e97def3 // indirect
|
||||
git.sr.ht/~sircmpwn/getopt v0.0.0-20191230200459-23622cc906b3 // indirect
|
||||
git.sr.ht/~sircmpwn/go-bare v0.0.0-20210227202403-5dae5c48f917 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3 // indirect
|
||||
|
|
|
@ -21,6 +21,12 @@ directive @private on FIELD_DEFINITION
|
|||
"Used to provide a human-friendly description of an access scope."
|
||||
directive @scopehelp(details: String!) on ENUM_VALUE
|
||||
|
||||
"""
|
||||
This used to decorate fields which are for internal use, and are not
|
||||
available to normal API users.
|
||||
"""
|
||||
directive @internal on FIELD_DEFINITION
|
||||
|
||||
enum AccessScope {
|
||||
PROFILE @scopehelp(details: "profile information")
|
||||
PASTES @scopehelp(details: "pastes")
|
||||
|
@ -288,4 +294,9 @@ type Mutation {
|
|||
unexpected behavior with the third-party integration.
|
||||
"""
|
||||
deleteUserWebhook(id: Int!): WebhookSubscription!
|
||||
|
||||
"""
|
||||
Deletes the authenticated user's account. Internal use only.
|
||||
"""
|
||||
deleteUser: Int! @internal
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"git.sr.ht/~sircmpwn/core-go/server"
|
||||
"git.sr.ht/~sircmpwn/core-go/valid"
|
||||
corewebhooks "git.sr.ht/~sircmpwn/core-go/webhooks"
|
||||
"git.sr.ht/~sircmpwn/paste.sr.ht/api/account"
|
||||
"git.sr.ht/~sircmpwn/paste.sr.ht/api/graph/api"
|
||||
"git.sr.ht/~sircmpwn/paste.sr.ht/api/graph/model"
|
||||
"git.sr.ht/~sircmpwn/paste.sr.ht/api/loaders"
|
||||
|
@ -350,6 +351,13 @@ func (r *mutationResolver) DeleteUserWebhook(ctx context.Context, id int) (model
|
|||
return &sub, nil
|
||||
}
|
||||
|
||||
// DeleteUser is the resolver for the deleteUser field.
|
||||
func (r *mutationResolver) DeleteUser(ctx context.Context) (int, error) {
|
||||
user := auth.ForContext(ctx)
|
||||
account.Delete(ctx, user.UserID, user.Username)
|
||||
return user.UserID, nil
|
||||
}
|
||||
|
||||
// Files is the resolver for the files field.
|
||||
func (r *pasteResolver) Files(ctx context.Context, obj *model.Paste) ([]*model.File, error) {
|
||||
var files []*model.File
|
||||
|
|
|
@ -9,9 +9,11 @@ import (
|
|||
"git.sr.ht/~sircmpwn/core-go/database"
|
||||
"git.sr.ht/~sircmpwn/core-go/server"
|
||||
"git.sr.ht/~sircmpwn/core-go/webhooks"
|
||||
work "git.sr.ht/~sircmpwn/dowork"
|
||||
"github.com/99designs/gqlgen/graphql"
|
||||
"github.com/go-chi/chi"
|
||||
|
||||
"git.sr.ht/~sircmpwn/paste.sr.ht/api/account"
|
||||
"git.sr.ht/~sircmpwn/paste.sr.ht/api/graph"
|
||||
"git.sr.ht/~sircmpwn/paste.sr.ht/api/graph/api"
|
||||
"git.sr.ht/~sircmpwn/paste.sr.ht/api/graph/model"
|
||||
|
@ -23,6 +25,7 @@ func main() {
|
|||
|
||||
gqlConfig := api.Config{Resolvers: &graph.Resolver{}}
|
||||
gqlConfig.Directives.Private = server.Private
|
||||
gqlConfig.Directives.Internal = server.Internal
|
||||
gqlConfig.Directives.Access = func(ctx context.Context, obj interface{},
|
||||
next graphql.Resolver, scope model.AccessScope,
|
||||
kind model.AccessKind) (interface{}, error) {
|
||||
|
@ -35,13 +38,18 @@ func main() {
|
|||
scopes[i] = s.String()
|
||||
}
|
||||
|
||||
accountQueue := work.NewQueue("account")
|
||||
webhookQueue := webhooks.NewQueue(schema)
|
||||
|
||||
gsrv := server.NewServer("paste.sr.ht", appConfig).
|
||||
WithDefaultMiddleware().
|
||||
WithMiddleware(loaders.Middleware, webhooks.Middleware(webhookQueue)).
|
||||
WithMiddleware(
|
||||
loaders.Middleware,
|
||||
account.Middleware(accountQueue),
|
||||
webhooks.Middleware(webhookQueue),
|
||||
).
|
||||
WithSchema(schema, scopes).
|
||||
WithQueues(webhookQueue.Queue)
|
||||
WithQueues(accountQueue, webhookQueue.Queue)
|
||||
|
||||
// Bulk transfer endpoints
|
||||
gsrv.Router().Get("/query/blob/{id}", func(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
Loading…
Reference in New Issue