@@ -34,5 +34,9 @@ models:
|
|||||||
fields:
|
fields:
|
||||||
fictionalDate:
|
fictionalDate:
|
||||||
resolver: true
|
resolver: true
|
||||||
|
File:
|
||||||
|
model: git.aiterp.net/rpdata/api/models.File
|
||||||
|
FilesFilter:
|
||||||
|
model: git.aiterp.net/rpdata/api/models/files.Filter
|
||||||
Date:
|
Date:
|
||||||
model: git.aiterp.net/rpdata/api/models/scalars.Date
|
model: git.aiterp.net/rpdata/api/models/scalars.Date
|
||||||
@@ -29,3 +29,7 @@ func (r *rootResolver) Log() LogResolver {
|
|||||||
func (r *rootResolver) Chapter() ChapterResolver {
|
func (r *rootResolver) Chapter() ChapterResolver {
|
||||||
return &types.ChapterResolver
|
return &types.ChapterResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *rootResolver) File() FileResolver {
|
||||||
|
return &types.FileResolver
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -37,6 +37,13 @@ type Query {
|
|||||||
|
|
||||||
# Find all distinct tags used in stories
|
# Find all distinct tags used in stories
|
||||||
tags: [Tag!]!
|
tags: [Tag!]!
|
||||||
|
|
||||||
|
|
||||||
|
# Find file by ID
|
||||||
|
file(id: String!): File!
|
||||||
|
|
||||||
|
# Find files
|
||||||
|
files(filter: FilesFilter): [File!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
# A Date represents a RFC3339 encoded date with up to millisecond precision.
|
# A Date represents a RFC3339 encoded date with up to millisecond precision.
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user