Gisle Aune
6 years ago
10 changed files with 261 additions and 0 deletions
-
4graph2/gqlgen.yml
-
4graph2/graph.go
-
34graph2/queries/file.go
-
7graph2/schema/root.gql
-
52graph2/schema/types/File.gql
-
16graph2/types/file.go
-
16models/file.go
-
81models/files/db.go
-
11models/files/find.go
-
36models/files/list.go
@ -0,0 +1,34 @@ |
|||||
|
package queries |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
|
||||
|
"git.aiterp.net/rpdata/api/internal/auth" |
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
"git.aiterp.net/rpdata/api/models/files" |
||||
|
) |
||||
|
|
||||
|
func (r *resolver) File(ctx context.Context, id string) (models.File, error) { |
||||
|
return files.FindID(id) |
||||
|
} |
||||
|
|
||||
|
func (r *resolver) Files(ctx context.Context, filter *files.Filter) ([]models.File, error) { |
||||
|
token := auth.TokenFromContext(ctx) |
||||
|
|
||||
|
if filter == nil { |
||||
|
filter = &files.Filter{} |
||||
|
} |
||||
|
|
||||
|
// Only allow users to view public files that are not their own.
|
||||
|
if token != nil { |
||||
|
if filter.Public == nil || *filter.Public == false { |
||||
|
filter.Author = &token.UserID |
||||
|
} |
||||
|
} else { |
||||
|
filter.Public = &trueValue |
||||
|
} |
||||
|
|
||||
|
return files.List(filter) |
||||
|
} |
||||
|
|
||||
|
var trueValue = true |
@ -0,0 +1,52 @@ |
|||||
|
# A File contains information about a file and where to download it. |
||||
|
type File { |
||||
|
# The file's unique ID |
||||
|
id: String! |
||||
|
|
||||
|
# The kind of file. Most will be "upload", but some that are ported from the wiki will have other values for this. |
||||
|
kind: String! |
||||
|
|
||||
|
# The time of uploading |
||||
|
time: Date! |
||||
|
|
||||
|
# Whether the file is publicly listable. Someone with knowledge of the ID |
||||
|
# will still be able to view it, however. |
||||
|
public: Boolean! |
||||
|
|
||||
|
# The file's name |
||||
|
name: String! |
||||
|
|
||||
|
# The MIME type of the file |
||||
|
mimeType: String! |
||||
|
|
||||
|
# The file's size in bytes |
||||
|
size: Int! |
||||
|
|
||||
|
# The uploader |
||||
|
author: String! |
||||
|
|
||||
|
# The URL where the file is hosted |
||||
|
url: String |
||||
|
} |
||||
|
|
||||
|
# Filter for the files quiery. |
||||
|
input FilesFilter { |
||||
|
# If set, this will limit the results to either public or private files. |
||||
|
public: Boolean |
||||
|
|
||||
|
# Limit the MIME types of the files. |
||||
|
mimeType: [String!] |
||||
|
} |
||||
|
|
||||
|
# Input for editFile mutation |
||||
|
input EditFileInput { |
||||
|
# The file's unique ID |
||||
|
id: String! |
||||
|
|
||||
|
# Whether the file is publicly listable. Someone with knowledge of the ID |
||||
|
# will still be able to view it, however. |
||||
|
public: Boolean |
||||
|
|
||||
|
# The file's name |
||||
|
name: String |
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
package types |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
|
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
) |
||||
|
|
||||
|
type fileResolver struct{} |
||||
|
|
||||
|
func (r *fileResolver) Size(ctx context.Context, file *models.File) (int, error) { |
||||
|
return int(file.Size), nil |
||||
|
} |
||||
|
|
||||
|
// FileResolver is a resolver
|
||||
|
var FileResolver fileResolver |
@ -0,0 +1,16 @@ |
|||||
|
package models |
||||
|
|
||||
|
import "time" |
||||
|
|
||||
|
// A File is a record of a file stored in the Space.
|
||||
|
type File struct { |
||||
|
ID string `bson:"_id" json:"id"` |
||||
|
Time time.Time `bson:"time" json:"time"` |
||||
|
Kind string `bson:"kind" json:"kind"` |
||||
|
Public bool `bson:"public" json:"public"` |
||||
|
Name string `bson:"name" json:"name"` |
||||
|
MimeType string `bson:"mimeType" json:"mimeType"` |
||||
|
Size int64 `bson:"size" json:"size"` |
||||
|
Author string `bson:"author" json:"author"` |
||||
|
URL string `bson:"url,omitempty" json:"url,omitempty"` |
||||
|
} |
@ -0,0 +1,81 @@ |
|||||
|
package files |
||||
|
|
||||
|
import ( |
||||
|
"crypto/rand" |
||||
|
"encoding/binary" |
||||
|
"strconv" |
||||
|
"time" |
||||
|
|
||||
|
"git.aiterp.net/rpdata/api/internal/store" |
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
"github.com/globalsign/mgo" |
||||
|
) |
||||
|
|
||||
|
var collection *mgo.Collection |
||||
|
|
||||
|
func find(query interface{}) (models.File, error) { |
||||
|
file := models.File{} |
||||
|
|
||||
|
err := collection.Find(query).One(&file) |
||||
|
if err != nil { |
||||
|
return models.File{}, err |
||||
|
} |
||||
|
|
||||
|
return file, nil |
||||
|
} |
||||
|
|
||||
|
func list(query interface{}) ([]models.File, error) { |
||||
|
list := make([]models.File, 0, 32) |
||||
|
|
||||
|
err := collection.Find(query).Sort("-time").All(&list) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return list, nil |
||||
|
} |
||||
|
|
||||
|
// makeID makes a random file ID that's 32 characters long
|
||||
|
func makeID() string { |
||||
|
result := "F" + strconv.FormatInt(time.Now().UnixNano(), 36) |
||||
|
offset := 0 |
||||
|
data := make([]byte, 32) |
||||
|
|
||||
|
rand.Read(data) |
||||
|
for len(result) < 32 { |
||||
|
result += strconv.FormatUint(binary.LittleEndian.Uint64(data[offset:]), 36) |
||||
|
offset += 8 |
||||
|
|
||||
|
if offset >= 32 { |
||||
|
rand.Read(data) |
||||
|
offset = 0 |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return result[:32] |
||||
|
} |
||||
|
|
||||
|
func init() { |
||||
|
store.HandleInit(func(db *mgo.Database) { |
||||
|
collection = db.C("file.headers") |
||||
|
|
||||
|
collection.EnsureIndexKey("author") |
||||
|
collection.EnsureIndexKey("public") |
||||
|
collection.EnsureIndexKey("kind", "name", "author") |
||||
|
collection.EnsureIndexKey("author", "public") |
||||
|
collection.EnsureIndexKey("kind") |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
var allowdMimeTypes = map[string]bool{ |
||||
|
"": false, |
||||
|
"image/jpeg": true, |
||||
|
"image/png": true, |
||||
|
"image/gif": true, |
||||
|
"text/plain": true, |
||||
|
"application/json": true, |
||||
|
"application/pdf": false, |
||||
|
"binary/octet-stream": false, |
||||
|
"video/mp4": false, |
||||
|
"audio/mp3": false, |
||||
|
} |
@ -0,0 +1,11 @@ |
|||||
|
package files |
||||
|
|
||||
|
import ( |
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
"github.com/globalsign/mgo/bson" |
||||
|
) |
||||
|
|
||||
|
// FindID finds a file by ID
|
||||
|
func FindID(id string) (models.File, error) { |
||||
|
return find(bson.M{"_id": id}) |
||||
|
} |
@ -0,0 +1,36 @@ |
|||||
|
package files |
||||
|
|
||||
|
import ( |
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
"github.com/globalsign/mgo/bson" |
||||
|
) |
||||
|
|
||||
|
// Filter for files.List
|
||||
|
type Filter struct { |
||||
|
Author *string |
||||
|
Public *bool |
||||
|
MimeType []string |
||||
|
} |
||||
|
|
||||
|
// List lists files according to the standard lookup. By default it's just the author's own files,
|
||||
|
// but if `public` is true it will alos include files made public by other authors. If `mimeTypes` contains
|
||||
|
// any, it will limit the results to that. If `author` is empty, it will only list public files
|
||||
|
func List(filter *Filter) ([]models.File, error) { |
||||
|
query := bson.M{} |
||||
|
|
||||
|
if filter != nil { |
||||
|
if filter.Author != nil { |
||||
|
query["author"] = *filter.Author |
||||
|
} |
||||
|
|
||||
|
if filter.Public != nil { |
||||
|
query["public"] = *filter.Public |
||||
|
} |
||||
|
|
||||
|
if len(filter.MimeType) > 0 { |
||||
|
query["mimeTypes"] = bson.M{"$in": filter.MimeType} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return list(query) |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue