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