Compare commits

...
84 Commits
Author SHA1 Message Date
gisle b7d5059d60 graph2: Fixed chapter using story permissions.
the build was successful
2018-10-16 19:01:11 +02:00
gisle 9fbdc767ad models2: Added 'post.remove' to default user permissionss.
the build was successful
2018-10-16 18:49:14 +02:00
gisle daeaaf5fa9 rpdata-as2storyimport: Fixed category vailidty check failing when it is valid, not when it isn't.
the build was successful
2018-10-16 18:30:24 +02:00
gisle d3277c7476 models2: Fixed text search not working.
the build was successful
2018-10-13 14:04:02 +02:00
gisle 90681c67b0 models2: Added post removal on log removal. 2018-10-13 11:43:53 +02:00
gisle bebd99833b models2: Fixed incorrect db mappings, fixed Edit not returning mutated model. 2018-10-11 22:26:08 +02:00
gisle 34f86d5037 graph2: Added logging changes for Channel, Log, Post and Character.
the build failed
2018-10-09 20:25:55 +02:00
gisle 8a85313762 docker: Updated DockerFile.
the build failed
2018-10-09 19:33:51 +02:00
gisle 017e68eae7 rpdata-ensurechannels: Ported to use new models package.
the build failed
2018-10-07 14:48:23 +02:00
gisle 1955ad0517 rpdata-lb2logimport: Changed script to use new models package.
the build failed
2018-10-07 14:45:44 +02:00
gisle 34ba5d5d3a models2: Fixed issue caused by last commit removing packages that were still barely in use. 2018-10-07 13:41:39 +02:00
gisle 8c8a251080 package: Removed old models packages that are ported over to the new format.
the build failed
2018-10-07 12:18:55 +02:00
gisle 73fe125870 models2: Added log character updates.
the build failed
2018-10-07 12:16:53 +02:00
gisle 04ec85aadf graph2: Removed limit on posts returned for Log.posts field. 2018-10-07 12:16:12 +02:00
gisle 59233ee549 models2: Fixed characters using wrong auto increment
the build failed
2018-10-07 10:56:29 +02:00
gisle 7aa0de9135 models2: Fixed error caused by empty change set in edit
the build failed
2018-10-06 14:02:39 +02:00
gisle 6dfbee7ff2 models2: Fixed editing characters erasing all unedited data.
the build failed
2018-10-06 13:55:16 +02:00
gisle 8a07c2ffdb graph2: Remove CharactersFilter logged property.
the build failed
2018-10-06 10:53:15 +02:00
gisle 0e9536b0a8 models2: Fixed StoryCategory UnmarshalGQL validating before writing the value.
the build failed
2018-10-03 20:56:18 +02:00
gisle b8e5ed5813 graph2: Added channel mutations.
the build failed
2018-10-01 22:20:52 +02:00
gisle 2317f66920 graph2: Added character mutations
the build failed
2018-10-01 22:05:12 +02:00
gisle fc488b938d graph2: Fixed last change closing all logs upon opening one, even the one just opened.
the build failed
2018-10-01 21:01:43 +02:00
gisle ceb2617571 graph2: Added closing of all other logs in the same channel if a log is opened.
the build failed
2018-10-01 20:22:37 +02:00
gisle aeb5b26627 graph2: Added Log mutations
the build failed
2018-09-30 17:05:26 +02:00
gisle 6aafc67790 rpdata-server: >_>
the build failed
2018-09-30 11:28:25 +02:00
gisle c6f8fd9faa git: readme
the build failed
2018-09-30 11:14:01 +02:00
gisle efa98b80cc rpdata-as2storyimport: Changed to use new models.
the build failed
2018-09-29 16:38:36 +02:00
gisle 707dcb43eb models2: Added IsValid() method to StoryCategory 2018-09-29 16:38:21 +02:00
gisle 9bcc28a0ea rpdata-graphiql: removed command
the build failed
2018-09-29 16:21:36 +02:00
gisle 2fceaa62ed rpdata-lb2charimport: Ported to using new models package
the build failed
2018-09-29 16:20:36 +02:00
gisle 86125c7ce9 graph2: Added add/remove nick mutations to character. 2018-09-29 16:20:08 +02:00
gisle bb28795cd5 graph2: Added post mutations. general: Did some cleaning.
the build failed
2018-09-29 15:54:22 +02:00
gisle 815f30597e graph2: Added removal of chapters upon story being deleted.
the build failed
2018-09-23 13:53:13 +02:00
gisle cd1f122502 graph2: Fixed inverse permission checks in story mutations.
the build failed
2018-09-22 15:31:53 +02:00
gisle 0e0d802a32 graph2: Added Story mutations
the build failed
2018-09-20 20:50:22 +02:00
gisle 3c42043ab6 models: Added Equals method to tag model. 2018-09-20 20:49:57 +02:00
gisle 8388ee2446 graph2: Added clearFictionalDate option to editChapter mutation input
the build failed
2018-09-19 22:19:39 +02:00
gisle 260c2e7f76 auth: Changed token checking to ignore disallowed permissions instead of invalidate the key
the build failed
2018-09-18 20:24:04 +02:00
gisle a4034b0faa graph2: Added Chapter mutations
the build failed
2018-09-17 20:11:57 +02:00
gisle c3a6798050 rpdata-server: Changed endpoint from /query to /graphql 2018-09-16 23:52:45 +02:00
gisle 2ab933149e auth: Moved token and user to models package, added graphql endpoint to check token
the build failed
2018-09-16 16:57:40 +02:00
gisle 07b94b3cab auth: Added list of permissions
the build failed
2018-09-16 12:55:28 +02:00
gisle 79171bf9a2 graph2: Added Story type and queries. 2018-09-16 12:55:14 +02:00
gisle bac57bc378 graph2: Added File type and queries.
the build failed
2018-09-16 11:30:18 +02:00
gisle 875dac9ecb graph2: Added Chapter type and queries, moved loader out of old graphql pkg.
the build failed
2018-09-16 10:33:55 +02:00
gisle 0768cfd0bd models: Painstakingly converted half the model package to a models & repostiry stucture. All binaries are probably dead at this point.
the build failed
2018-09-16 00:13:12 +02:00
gisle 2b05d90f4a graph2: Added Log type and queries, moved loader out of old graphql pkg.
the build failed
2018-09-15 13:14:05 +02:00
gisle 0444911e73 graph2: Added Post type and queries 2018-09-15 09:29:04 +02:00
gisle 1be4088a8f graph2: Added Channel type and queries
the build failed
2018-09-13 22:49:36 +02:00
gisle 9978d7380b graph, rpdata-server: Started on new GraphQL implementation.
the build failed
2018-09-13 21:13:05 +02:00
gisle 2e5162a29b auth: Fixed incorrect error reporting on permissions mismatching, added ensuring of user in auth.FindUser.
the build failed
2018-09-13 20:08:37 +02:00
gisle dac0060155 auth: Replaced old system with api key-based system.
the build failed
2018-09-12 21:13:14 +02:00
gisle 9d7fd498a9 Auth: Removed session/auth system from GraphQL API 2018-09-07 17:59:57 +02:00
gisle e0af2685ee Logs: Moved filtering logic out of GraphQL query resolver to the Log model.
the build failed
2018-09-02 13:46:12 +02:00
gisle 3184b60310 GraphQL: Optimized dataloaders by adding a 'priming' mechanic to make fewer trips to the database.
the build failed
2018-09-02 12:38:17 +02:00
gisle 675c09ef04 git: removed binary file from project root
the build failed
2018-08-30 20:32:05 +02:00
gisle 9707320960 git: updated gitignore
the build failed
2018-08-30 20:29:50 +02:00
gisle 20e53b4f5e GraphQL: Fixed stories resolver crashing if filter is nil.
the build failed
2018-08-30 20:21:00 +02:00
gisle 8bb4dea358 UI: Started on story content 2018-08-25 10:35:29 +02:00
gisle 75b2382f09 Changed colors to more closely match aitestory
the build was successful
2018-08-01 22:26:18 +02:00
gisle 1ca25eff8e Fixed warnings
the build was successful
2018-08-01 21:49:58 +02:00
gisle bf10ed1fe3 Added ListItem and changed StoryList ot use it, reorganized StoryMenu, added scrollbar styling, added moment dependency
the build was successful
2018-08-01 18:02:01 +02:00
gisle 54136be912 Removed leftover console.log()s 2018-08-01 15:51:38 +02:00
gisle 228c2bfeb4 Removed docker build on non-tag push from CI
the build was successful
2018-08-01 15:47:27 +02:00
gisle 2f98be5747 Removed default limit for stories query
the build was successful
2018-08-01 15:19:08 +02:00
gisle 2a40f97282 Added order to stories query response
the build was successful
2018-08-01 15:04:16 +02:00
gisle e5b4184106 Fixed query for category lists not getting parameters 2018-08-01 14:57:49 +02:00
gisle c348a90b70 Fixed nil deref error on no filter, fixed no results
the build was successful
2018-08-01 14:51:45 +02:00
gisle 132cf28f0f Added category to StoriesFilter
the build was successful
2018-08-01 14:38:09 +02:00
gisle d5df624188 Added open and unlsited to StoriesFilter
the build was successful
2018-08-01 14:27:28 +02:00
gisle ca98648274 Added router for story menu and queries for the lists 2018-08-01 14:07:54 +02:00
gisle 4642a8abec Added missing dpeencency (apollo-cache-inmemory), changed proxy to work within the local network.
the build was successful
2018-08-01 11:23:15 +02:00
gisle 0ec923d686 Added logged filter to characters query: character appears in logs.
the build was successful
2018-07-18 00:13:19 +02:00
gisle 388d5378e7 Cleaned up the ui code, addressed warnings
the build was successful
2018-07-16 20:24:01 +02:00
gisle 67e9e8f1e6 Renamed LogsIndex -> LogsRoot, removed hardcoded bg image from LogsRoot
the build was successful
2018-07-15 20:08:32 +02:00
gisle 06bc7e5a1b Updated StoryRoot graphql query according to api changes 2018-07-15 19:59:23 +02:00
gisle 237190e498 Added LoadingScreen for full-size loading placeholder 2018-07-15 19:58:12 +02:00
gisle b9f0fef966 Fixed Background landscape detection always returning the intiial result, reacitified the Background class 2018-07-15 19:57:28 +02:00
gisle ba1c1e0a4b Changed characters graphql query to use the filter format, changed description for files query to match the others'
the build was successful
2018-07-15 17:55:21 +02:00
gisle 18e77b8205 Changed .gitignore to include new bindata.go location 2018-07-15 17:14:35 +02:00
gisle d8871259a1 Changed channels graphql query to use filter, added eventName and locationName filters 2018-07-15 16:58:17 +02:00
gisle 26d7e36631 Changed files graphql query to use a filter argument to look more like other queries. 2018-07-15 15:34:58 +02:00
gisle 51489ed901 Changed log query's ID parameter to be compulsory 2018-07-15 15:16:47 +02:00
gisle ed3e384d55 Moved query and mutation resolvers into individual files
the build was successful
2018-07-15 15:10:35 +02:00
217 changed files with 7337 additions and 4195 deletions
+4 -4
View File
@@ -6,16 +6,16 @@ pipeline:
docker-release:
image: plugins/docker
repo: vmaple/rpdata
tags: latest
auto_tag: true
secrets: [ docker_username, docker_password ]
when:
event: tag
ref: refs/tags/*
docker-dev:
docker-release-latest:
image: plugins/docker
repo: vmaple/rpdata
tags: trunk
tags: latest
secrets: [ docker_username, docker_password ]
when:
event: push
event: tag
ref: refs/tags/*
+8 -2
View File
@@ -1,7 +1,13 @@
vendor
schema/bindata.go
graphql/schema/bindata.go
config.json
api
debug
build
.vscode
rpdata-graphiql
rpdata-*
!rpdata-*/
.vscode
generated.gql
generated.go
+9 -22
View File
@@ -1,41 +1,29 @@
## 1. Server
## 1. Build Server
# Use Go 1.10
FROM golang:1.10 AS build-server
FROM golang:1.11 AS build-server
# Load repository into docker
WORKDIR /go/src/git.aiterp.net/rpdata/api/
COPY . .
RUN rm -rf ./vendor
# Load build tools and dependencies
RUN go get -u github.com/golang/dep/cmd/dep
RUN go get -u github.com/vektah/gorunpkg
RUN go get -u github.com/jteeuwen/go-bindata/...
RUN dep ensure
RUN go generate ./...
# Build server and tools (CGO disabled to make them 100% static)
ENV CGO_ENABLED 0
RUN go build -a -installsuffix cgo -ldflags="-s -w" -o /binaries/rpdata-graphiql ./cmd/rpdata-graphiql
RUN go build -a -installsuffix cgo -ldflags="-s -w" -o /binaries/rpdata-server ./cmd/rpdata-server
RUN go build -a -installsuffix cgo -ldflags="-s -w" -o /binaries/rpdata-lb2charimport ./cmd/rpdata-lb2charimport
RUN go build -a -installsuffix cgo -ldflags="-s -w" -o /binaries/rpdata-lb2logimport ./cmd/rpdata-lb2logimport
RUN go build -a -installsuffix cgo -ldflags="-s -w" -o /binaries/rpdata-wikifileimport ./cmd/rpdata-wikifileimport
# TODO: RUN go build -a -installsuffix cgo -ldflags="-s -w" -o /binaries/rpdata-wikifileimport ./cmd/rpdata-wikifileimport
RUN go build -a -installsuffix cgo -ldflags="-s -w" -o /binaries/rpdata-ensurechannels ./cmd/rpdata-ensurechannels
RUN go build -a -installsuffix cgo -ldflags="-s -w" -o /binaries/rpdata-as2storyimport ./cmd/rpdata-as2storyimport
## 2. React UI
# Use Node.JS 18
FROM node:10.5-alpine AS build-id
# The content is already there
WORKDIR /rpdata-ui
COPY ./rpdata-ui /rpdata-ui
# Prepare dependencies
RUN npm install
# Run build
RUN npm run build
## 3. Regroup
## 2. Distribute
# Use alpine linux
FROM alpine:3.7
@@ -44,7 +32,6 @@ RUN apk add --no-cache ca-certificates
# Copy build files into final container
COPY --from=build-server /binaries/* /usr/local/bin/
COPY --from=build-id /rpdata-ui/build /var/www/
# TODO: Use proper server instead of grapihql server
CMD ["/usr/local/bin/rpdata-graphiql"]
# The server is the main entry point
CMD ["/usr/local/bin/rpdata-server"]
Generated
+83 -7
View File
@@ -2,10 +2,32 @@
[[projects]]
branch = "master"
name = "git.aiterp.net/aiterp/wikiauth"
name = "github.com/99designs/gqlgen"
packages = [
".",
"cmd",
"codegen",
"codegen/templates",
"complexity",
"graphql",
"graphql/introspection",
"handler",
"internal/gopath"
]
revision = "636435b68700211441303f1a5ed92f3768ba5774"
version = "v0.5.1"
[[projects]]
name = "github.com/agnivade/levenshtein"
packages = ["."]
revision = "33860de804ddcec4fc17998745233b4ec477e7c2"
revision = "3d21ba515fe27b856f230847e856431ae1724adc"
version = "v1.0.0"
[[projects]]
name = "github.com/dgrijalva/jwt-go"
packages = ["."]
revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
version = "v3.2.0"
[[projects]]
branch = "master"
@@ -37,6 +59,12 @@
revision = "d523deb1b23d913de5bdada721a6071e71283618"
version = "v1.4.0"
[[projects]]
name = "github.com/gorilla/websocket"
packages = ["."]
revision = "66b9c49e59c6c48f0ffce28c2d8b8a5678502c6d"
version = "v1.4.0"
[[projects]]
name = "github.com/graph-gophers/dataloader"
packages = ["."]
@@ -64,6 +92,15 @@
]
revision = "9ebf33af539ab8cb832c7107bc0a978ca8dbc0de"
[[projects]]
name = "github.com/hashicorp/golang-lru"
packages = [
".",
"simplelru"
]
revision = "20f1fb78b0740ba8c3cb143a61e86ba5c8669768"
version = "v0.5.0"
[[projects]]
branch = "master"
name = "github.com/jmoiron/sqlx"
@@ -103,10 +140,10 @@
version = "v1.0.2"
[[projects]]
name = "github.com/sadbox/mediawiki"
name = "github.com/pkg/errors"
packages = ["."]
revision = "39fea8a1336076a961a300d1d95765dcd17e8a3c"
version = "v0.1"
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
name = "github.com/sirupsen/logrus"
@@ -114,6 +151,26 @@
revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc"
version = "v1.0.5"
[[projects]]
name = "github.com/urfave/cli"
packages = ["."]
revision = "cfb38830724cc34fedffe9a2a29fb54fa9169cd1"
version = "v1.20.0"
[[projects]]
branch = "master"
name = "github.com/vektah/gqlparser"
packages = [
".",
"ast",
"gqlerror",
"lexer",
"parser",
"validator",
"validator/rules"
]
revision = "14e83ae06ec152e6d0afb9766a00e0c0918aa8fc"
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
@@ -164,15 +221,34 @@
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[[projects]]
branch = "master"
name = "golang.org/x/tools"
packages = [
"go/ast/astutil",
"go/buildutil",
"go/internal/cgo",
"go/loader",
"imports",
"internal/fastwalk"
]
revision = "677d2ff680c188ddb7dcd2bfa6bc7d3f2f2f75b2"
[[projects]]
name = "google.golang.org/appengine"
packages = ["cloudsql"]
revision = "b1f26356af11148e710935ed1ac8a7f5702c7612"
version = "v1.1.0"
[[projects]]
name = "gopkg.in/yaml.v2"
packages = ["."]
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
version = "v2.2.1"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "16fd84401bc5796ffeaa895f31d099e1a23b363b15b87386877de0829ac9575b"
inputs-digest = "3c9430afa8b9260026925c8f496af934e2e573a2bd4587bb634fd12a20ce6c2c"
solver-name = "gps-cdcl"
solver-version = 1
+3 -1
View File
@@ -24,6 +24,8 @@
# go-tests = true
# unused-packages = true
required = [ "github.com/99designs/gqlgen" ]
[[constraint]]
branch = "master"
name = "github.com/globalsign/mgo"
@@ -38,4 +40,4 @@
[[constraint]]
branch = "master"
name = "github.com/graph-gophers/graphql-go"
name = "github.com/graph-gophers/graphql-go"
+2 -15
View File
@@ -1,18 +1,5 @@
# RPData API
Project RPData aims to centralize the AiteRP applications, and partially involve the wiki, into a common API. This will allow for easy linking and inlining between them. The primary consumers of this API will be the Logbot and the upcoming RPData website.
Project RPData aims to centralize the AiteRP applications, and partially involve the wiki into a common API. This will allow for easy linking and inlining between them. The primary consumers of this API will be the Logbot and the upcoming RPData website.
## Progress
### Complete
* Session
* Character
* Log
* Post
* File
### Remaining
* Story
### Future
* User (manual DB edits will suffice for now)
There is some renovation going on, and the packages `model` and `graphql` are going to be superceded by `models` and `graph2`
+13 -13
View File
@@ -8,7 +8,9 @@ import (
"time"
"git.aiterp.net/rpdata/api/internal/store"
"git.aiterp.net/rpdata/api/model/story"
"git.aiterp.net/rpdata/api/models"
"git.aiterp.net/rpdata/api/models/chapters"
"git.aiterp.net/rpdata/api/models/stories"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
)
@@ -71,14 +73,20 @@ func main() {
log.Fatalln(err)
}
tags := make([]story.Tag, 0, 8)
tags := make([]models.Tag, 0, 8)
for _, tagResult := range tagResults {
if tagResult.PageID == result.ID {
tags = append(tags, story.Tag{Kind: tagResult.Type, Name: tagResult.Name})
tags = append(tags, models.Tag{Kind: models.TagKind(tagResult.Type), Name: tagResult.Name})
}
}
story, err := story.New(result.Name, result.Author[5:], result.Category, false, false, tags, publishDate, fictionalDate)
category := models.StoryCategory(result.Category)
if !category.IsValid() {
log.Println(result.Name, "does not have a valid category:", result.Category)
continue
}
story, err := stories.Add(result.Name, result.Author[5:], category, true, false, tags, publishDate, fictionalDate)
if err != nil {
log.Fatalln(err)
}
@@ -96,19 +104,11 @@ func main() {
}
}
chapter, err := story.AddChapter(title, result.Author[5:], result.Source, publishDate, fictionalDate)
chapter, err := chapters.Add(story, title, result.Author[5:], result.Source, publishDate, &fictionalDate)
if err != nil {
log.Fatalln(err)
}
if !result.Unlisted {
listed := true
err := story.Edit(nil, nil, &listed, nil, nil)
if err != nil {
log.Fatalln(err)
}
}
fmt.Println(result.ID, "->", story.ID, chapter.ID)
}
}
+7 -8
View File
@@ -4,10 +4,9 @@ import (
"fmt"
"os"
"git.aiterp.net/rpdata/api/model/channel"
"git.aiterp.net/rpdata/api/internal/store"
"git.aiterp.net/rpdata/api/model/log"
"git.aiterp.net/rpdata/api/models/channels"
"git.aiterp.net/rpdata/api/models/logs"
)
func main() {
@@ -17,7 +16,7 @@ func main() {
return
}
logs, err := log.List(32768)
logs, err := logs.List(nil)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
@@ -25,18 +24,18 @@ func main() {
added := make(map[string]bool, 1024)
for _, log := range logs {
if added[log.Channel] {
if added[log.ChannelName] {
continue
}
_, err := channel.Ensure(log.Channel, false)
_, err := channels.Ensure(log.ChannelName, false)
if err != nil {
fmt.Fprintln(os.Stderr, log.ID, err)
continue
}
added[log.Channel] = true
added[log.ChannelName] = true
fmt.Println(log.Channel, "ensured")
fmt.Println(log.ChannelName, "ensured")
}
}
-2
View File
@@ -1,2 +0,0 @@
config.json
debug
-150
View File
@@ -1,150 +0,0 @@
package main
import (
"context"
"encoding/json"
"log"
"net/http"
"git.aiterp.net/rpdata/api/graphql/loader"
"git.aiterp.net/rpdata/api/graphql/resolver"
"git.aiterp.net/rpdata/api/graphql/schema"
"git.aiterp.net/rpdata/api/internal/session"
"git.aiterp.net/rpdata/api/internal/store"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/file"
logModel "git.aiterp.net/rpdata/api/model/log"
graphql "github.com/graph-gophers/graphql-go"
"github.com/graph-gophers/graphql-go/relay"
)
func main() {
err := store.Init()
if err != nil {
log.Fatalln("Failed to init store:", err)
}
n, err := logModel.UpdateAllCharacters()
if err != nil {
log.Println("Charcter updated stopped:", err)
}
log.Println("Updated characters on", n, "logs")
schema, err := graphql.ParseSchema(schema.String(), &resolver.RootResolver{}, graphql.MaxParallelism(48))
if err != nil {
log.Fatalln("Failed to parse schema:", err)
}
http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
w.Write(page)
}))
relayHandler := &relay.Handler{Schema: schema}
http.HandleFunc("/graphql", func(w http.ResponseWriter, r *http.Request) {
r = session.Load(w, r)
l := loader.New()
r = r.WithContext(l.ToContext(r.Context()))
relayHandler.ServeHTTP(w, r)
})
http.HandleFunc("/upload", func(w http.ResponseWriter, r *http.Request) {
r = session.Load(w, r)
sess := session.FromContext(r.Context())
user := sess.User()
if user == nil || !user.Permitted("file.upload") {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
if err := r.ParseMultipartForm(16384); err != nil {
http.Error(w, "Internal (1): "+err.Error(), http.StatusInternalServerError)
return
}
formFile, header, err := r.FormFile("file")
if err != nil || header == nil || formFile == nil {
http.Error(w, "No file provided", http.StatusBadRequest)
return
}
file, err := file.Upload(r.Context(), header.Filename, header.Header.Get("Content-Type"), user.ID, header.Size, formFile)
if err != nil {
http.Error(w, "Internal (2): "+err.Error(), http.StatusInternalServerError)
return
}
json, err := json.Marshal(file)
if err != nil {
http.Error(w, "Internal (3): "+err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(json)
})
change.InitializeTimeline()
go func() {
index := int64(0)
for {
index++
ch, err := change.Timeline.Get(context.Background(), index)
if err == change.ErrExpired {
continue
} else if err != nil {
log.Println("Timeline:", err)
break
}
log.Printf("Change: time=%s model=%s op=%s id=%s author=%s\n", ch.Time.Local().Format("[Mon Jan 02 15:04 MST]"), ch.Model, ch.Op, ch.ObjectID, ch.Author)
}
}()
log.Fatal(http.ListenAndServe(":17000", nil))
}
var page = []byte(`
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/graphiql/0.10.2/graphiql.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/1.1.0/fetch.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.5.4/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.5.4/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/graphiql/0.10.2/graphiql.js"></script>
</head>
<body style="width: 100%; height: 100%; margin: 0; overflow: hidden;">
<div id="graphiql" style="height: 100vh;">Loading...</div>
<script>
function graphQLFetcher(graphQLParams) {
return fetch("/graphql", {
method: "post",
body: JSON.stringify(graphQLParams),
credentials: "include",
}).then(function (response) {
return response.text();
}).then(function (responseBody) {
try {
return JSON.parse(responseBody);
} catch (error) {
return responseBody;
}
});
}
ReactDOM.render(
React.createElement(GraphiQL, {fetcher: graphQLFetcher}),
document.getElementById("graphiql")
);
</script>
</body>
</html>
`)
+4 -4
View File
@@ -6,7 +6,7 @@ import (
"os"
"git.aiterp.net/rpdata/api/internal/store"
"git.aiterp.net/rpdata/api/model/character"
"git.aiterp.net/rpdata/api/models/characters"
)
var fileName = flag.String("file", "./characters.json", "json file to load")
@@ -31,12 +31,12 @@ func main() {
nicksAdded, nicksFailed := 0, 0
for _, info := range infos {
char, err := character.New(info.Nicks[0], info.Name, info.ShortName, info.Author, "")
char, err := characters.Add(info.Nicks[0], info.Name, info.ShortName, info.Author, "")
if err != nil {
log.Println(info.Nicks[0], "failed to insert:", err)
charsFailed++
char, err = character.FindNick(info.Nicks[0])
char, err = characters.FindNick(info.Nicks[0])
if err != nil {
continue
}
@@ -46,7 +46,7 @@ func main() {
}
for _, alt := range info.Nicks[1:] {
err := char.AddNick(alt)
char, err = characters.AddNick(char, alt)
if err != nil {
log.Println(info.Nicks[0], "failed to add nick", alt, "error:", err)
nicksFailed++
+8 -6
View File
@@ -8,8 +8,10 @@ import (
"strings"
"time"
"git.aiterp.net/rpdata/api/models/posts"
"git.aiterp.net/rpdata/api/internal/store"
logModel "git.aiterp.net/rpdata/api/model/log"
"git.aiterp.net/rpdata/api/models/logs"
)
var prefixReplacer = strings.NewReplacer("+", "", "@", "", "!", "", "%", "")
@@ -47,13 +49,13 @@ func main() {
log.Fatalln(err)
}
l, err := logModel.New(date, channel, title, event, "", false)
l, err := logs.Add(date, channel, title, event, "", false)
if err != nil {
log.Println(err)
continue
}
_, err = l.NewPost(time.Now(), "annotation.info", "rpdata-lb2logimport", "This logfile is imported from aitelogs2 and may contain errors or wrong timestamps.")
_, err = posts.Add(l, time.Now(), "annotation.info", "rpdata-lb2logimport", "This logfile is imported from aitelogs2 and may contain errors or wrong timestamps.")
if err != nil {
log.Println(err)
}
@@ -63,7 +65,7 @@ func main() {
continue
}
_, err = l.NewPost(l.Date, "chars", prefixReplacer.Replace(line.Args[0]), line.Text)
_, err = posts.Add(l, l.Date, "chars", prefixReplacer.Replace(line.Args[0]), line.Text)
if err != nil {
log.Println(err)
}
@@ -92,13 +94,13 @@ func main() {
line.Verb = "SCENE"
}
_, err = l.NewPost(postTime, strings.ToLower(line.Verb), prefixReplacer.Replace(line.Args[0]), line.Text)
_, err = posts.Add(l, postTime, strings.ToLower(line.Verb), prefixReplacer.Replace(line.Args[0]), line.Text)
if err != nil {
log.Println(err)
}
}
err = l.UpdateCharacters()
l, err = logs.UpdateCharacters(l)
if err != nil {
log.Println(err)
}
+68
View File
@@ -0,0 +1,68 @@
package main
import (
"context"
"fmt"
"log"
"net/http"
"runtime/debug"
"strings"
"git.aiterp.net/rpdata/api/models/logs"
"git.aiterp.net/rpdata/api/graph2"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/internal/loader"
"git.aiterp.net/rpdata/api/internal/store"
"github.com/99designs/gqlgen/handler"
)
func main() {
err := store.Init()
if err != nil {
log.Fatalln("Failed to init store:", err)
}
http.Handle("/", handler.Playground("RPData API", "/graphql"))
http.Handle("/graphql", queryHandler())
go func() {
err := logs.RunFullUpdate()
if err != nil {
log.Println(err)
}
log.Println("Characters updated")
}()
log.Fatal(http.ListenAndServe(":8081", nil))
}
func queryHandler() http.HandlerFunc {
handler := handler.GraphQL(
graph2.New(),
handler.RecoverFunc(func(ctx context.Context, err interface{}) error {
// send this panic somewhere
log.Println(err)
log.Println(string(debug.Stack()))
return fmt.Errorf("shit")
}),
)
return func(w http.ResponseWriter, r *http.Request) {
l := loader.New()
r = r.WithContext(l.ToContext(r.Context()))
// >_>
if strings.HasPrefix(r.Header.Get("Authorization"), "Bearer of the curse") {
w.Header().Set("X-Emerald-Herald", "Seek souls. Larger, more powerful souls.")
w.Header().Add("X-Emerald-Herald", "Seek the king, that is the only way.")
w.Header().Add("X-Emerald-Herald", "Lest this land swallow you whole... As it has so many others.")
}
r = auth.RequestWithToken(r)
handler.ServeHTTP(w, r)
}
}
+4
View File
@@ -0,0 +1,4 @@
#!/bin/sh
echo "# Generated by stiching together the files under schema/ – DO NOT EDIT" >generated.gql
cat schema/root.gql schema/**/*.gql >>generated.gql
+57
View File
@@ -0,0 +1,57 @@
schema: generated.gql
exec:
filename: generated.go
package: graph2
model:
filename: input/generated.go
package: input
models:
Tag:
model: git.aiterp.net/rpdata/api/models.Tag
TagInput: #It's the same as tags
model: git.aiterp.net/rpdata/api/models.Tag
TagKind:
model: git.aiterp.net/rpdata/api/models.TagKind
Character:
model: git.aiterp.net/rpdata/api/models.Character
CharactersFilter:
model: git.aiterp.net/rpdata/api/models/characters.Filter
Channel:
model: git.aiterp.net/rpdata/api/models.Channel
ChannelsFilter:
model: git.aiterp.net/rpdata/api/models/channels.Filter
Post:
model: git.aiterp.net/rpdata/api/models.Post
PostsFilter:
model: git.aiterp.net/rpdata/api/models/posts.Filter
Log:
model: git.aiterp.net/rpdata/api/models.Log
LogsFilter:
model: git.aiterp.net/rpdata/api/models/logs.Filter
Chapter:
model: git.aiterp.net/rpdata/api/models.Chapter
fields:
fictionalDate:
resolver: true
Story:
model: git.aiterp.net/rpdata/api/models.Story
fields:
fictionalDate:
resolver: true
StoryCategory:
model: git.aiterp.net/rpdata/api/models.StoryCategory
StoriesFilter:
model: git.aiterp.net/rpdata/api/models/stories.Filter
File:
model: git.aiterp.net/rpdata/api/models.File
FilesFilter:
model: git.aiterp.net/rpdata/api/models/files.Filter
Token:
model: git.aiterp.net/rpdata/api/models.Token
User:
model: git.aiterp.net/rpdata/api/models.User
Date:
model: git.aiterp.net/rpdata/api/models/scalars.Date
+47
View File
@@ -0,0 +1,47 @@
package graph2
import (
"git.aiterp.net/rpdata/api/graph2/queries"
"git.aiterp.net/rpdata/api/graph2/types"
graphql "github.com/99designs/gqlgen/graphql"
)
//go:generate ./combine.sh
//go:generate gorunpkg github.com/99designs/gqlgen -v
// New creates a new GraphQL schema.
func New() graphql.ExecutableSchema {
return NewExecutableSchema(Config{
Resolvers: &rootResolver{},
})
}
type rootResolver struct{}
func (r *rootResolver) Query() QueryResolver {
return &queries.Resolver
}
func (r *rootResolver) Mutation() MutationResolver {
return queries.MutationResolver
}
func (r *rootResolver) Log() LogResolver {
return &types.LogResolver
}
func (r *rootResolver) Chapter() ChapterResolver {
return &types.ChapterResolver
}
func (r *rootResolver) Story() StoryResolver {
return &types.StoryResolver
}
func (r *rootResolver) File() FileResolver {
return &types.FileResolver
}
func (r *rootResolver) Token() TokenResolver {
return &types.TokenResolver
}
+78
View File
@@ -0,0 +1,78 @@
package queries
import (
"context"
"errors"
"git.aiterp.net/rpdata/api/graph2/input"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/models"
"git.aiterp.net/rpdata/api/models/changes"
"git.aiterp.net/rpdata/api/models/channels"
)
// Queries
func (r *resolver) Channel(ctx context.Context, name string) (models.Channel, error) {
return channels.FindName(name)
}
func (r *resolver) Channels(ctx context.Context, filter *channels.Filter) ([]models.Channel, error) {
return channels.List(filter)
}
// Mutations
func (r *mutationResolver) AddChannel(ctx context.Context, input input.ChannelAddInput) (models.Channel, error) {
token := auth.TokenFromContext(ctx)
if !token.Authenticated() || !token.Permitted("channel.add") {
return models.Channel{}, errors.New("You are not permitted to add channels")
}
logged := false
if input.Logged != nil {
logged = *input.Logged
}
hub := false
if input.Hub != nil {
hub = *input.Hub
}
eventName := ""
if input.EventName != nil {
eventName = *input.EventName
}
locationName := ""
if input.LocationName != nil {
locationName = *input.LocationName
}
channel, err := channels.Add(input.Name, logged, hub, eventName, locationName)
if err != nil {
return models.Channel{}, errors.New("Failed to add channel: " + err.Error())
}
go changes.Submit("Channel", "add", token.UserID, channel)
return channel, nil
}
func (r *mutationResolver) EditChannel(ctx context.Context, input input.ChannelEditInput) (models.Channel, error) {
token := auth.TokenFromContext(ctx)
if !token.Authenticated() || !token.Permitted("channel.edit") {
return models.Channel{}, errors.New("You are not permitted to edit channels")
}
channel, err := channels.FindName(input.Name)
if err != nil {
return models.Channel{}, errors.New("Channel not found")
}
channel, err = channels.Edit(channel, input.Logged, input.Hub, input.EventName, input.LocationName)
if err != nil {
return models.Channel{}, errors.New("Failed to edit channel: " + err.Error())
}
go changes.Submit("Channel", "edit", token.UserID, channel)
return channel, nil
}
+81
View File
@@ -0,0 +1,81 @@
package queries
import (
"context"
"errors"
"time"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/models/stories"
"git.aiterp.net/rpdata/api/graph2/input"
"git.aiterp.net/rpdata/api/models"
"git.aiterp.net/rpdata/api/models/chapters"
)
// Queries
func (r *resolver) Chapter(ctx context.Context, id string) (models.Chapter, error) {
return chapters.FindID(id)
}
// Mutations
func (r *mutationResolver) AddChapter(ctx context.Context, input input.ChapterAddInput) (models.Chapter, error) {
story, err := stories.FindID(input.StoryID)
if err != nil {
return models.Chapter{}, errors.New("Story not found")
}
token := auth.TokenFromContext(ctx)
if !token.Permitted("member", "story.add") {
return models.Chapter{}, errors.New("Unauthorized")
}
author := token.UserID
if input.Author != nil && *input.Author != author {
if !token.Permitted("story.add") {
return models.Chapter{}, errors.New("False pretender")
}
author = *input.Author
}
if !story.Open && story.Author != author {
return models.Chapter{}, errors.New("Story is not open")
}
return chapters.Add(story, input.Title, author, input.Source, time.Now(), input.FictionalDate)
}
func (r *mutationResolver) EditChapter(ctx context.Context, input input.ChapterEditInput) (models.Chapter, error) {
chapter, err := chapters.FindID(input.ID)
if err != nil {
return models.Chapter{}, errors.New("Chapter not found")
}
token := auth.TokenFromContext(ctx)
if !token.Authenticated() || !token.PermittedUser(chapter.Author, "member", "chapter.edit") {
return models.Chapter{}, errors.New("Unauthorized")
}
if input.ClearFictionalDate != nil && *input.ClearFictionalDate == true {
input.FictionalDate = &time.Time{}
}
return chapters.Edit(chapter, input.Title, input.Source, input.FictionalDate)
}
func (r *mutationResolver) RemoveChapter(ctx context.Context, input input.ChapterRemoveInput) (models.Chapter, error) {
chapter, err := chapters.FindID(input.ID)
if err != nil {
return models.Chapter{}, errors.New("Chapter not found")
}
token := auth.TokenFromContext(ctx)
if !token.Authenticated() || !token.PermittedUser(chapter.Author, "member", "chapter.remove") {
return models.Chapter{}, errors.New("Unauthorized")
}
return chapters.Remove(chapter)
}
+173
View File
@@ -0,0 +1,173 @@
package queries
import (
"context"
"errors"
"strings"
"git.aiterp.net/rpdata/api/graph2/input"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/models"
"git.aiterp.net/rpdata/api/models/changes"
"git.aiterp.net/rpdata/api/models/characters"
"git.aiterp.net/rpdata/api/models/logs"
)
// Queries
func (r *resolver) Character(ctx context.Context, id *string, nick *string) (models.Character, error) {
if id != nil {
return characters.FindID(*id)
} else if nick != nil {
return characters.FindNick(*nick)
} else {
return models.Character{}, errors.New("You must specify either an ID or a nick")
}
}
func (r *resolver) Characters(ctx context.Context, filter *characters.Filter) ([]models.Character, error) {
return characters.List(filter)
}
// Mutations
func (r *mutationResolver) AddCharacter(ctx context.Context, input input.CharacterAddInput) (models.Character, error) {
token := auth.TokenFromContext(ctx)
if !token.Permitted("member", "character.add") {
return models.Character{}, errors.New("You are not permitted to add characters")
}
if len(input.Name) < 2 || len(input.Nick) < 2 {
return models.Character{}, errors.New("You need to provide a name and a nick (min length: 2)")
}
shortName := ""
if input.ShortName != nil {
shortName = *input.ShortName
} else {
shortName = strings.SplitN(input.Name, " ", 2)[0]
}
description := ""
if input.Description != nil {
description = *input.Description
}
author := token.UserID
if input.Author != nil && *input.Author != author {
if !token.Permitted("character.add") {
return models.Character{}, errors.New("You are only permitted to add your own characters")
}
author = *input.Author
}
logs.ScheduleFullUpdate()
character, err := characters.Add(input.Nick, input.Name, shortName, author, description)
if err != nil {
return models.Character{}, errors.New("Adding character failed: " + err.Error())
}
go changes.Submit("Character", "add", token.UserID, character)
return character, nil
}
func (r *mutationResolver) AddCharacterNick(ctx context.Context, input input.CharacterNickInput) (models.Character, error) {
character, err := characters.FindID(input.ID)
if err != nil {
return models.Character{}, errors.New("Character not found")
}
if len(input.Nick) < 2 {
return models.Character{}, errors.New("You need to provide a valid nick (min length: 2)")
}
token := auth.TokenFromContext(ctx)
if !token.PermittedUser(character.Author, "member", "character.edit") {
return models.Character{}, errors.New("You are not permitted to edit this character")
}
logs.ScheduleFullUpdate()
character, err = characters.AddNick(character, input.Nick)
if err != nil {
return models.Character{}, errors.New("Failed to add nick: " + err.Error())
}
go logs.ScheduleFullUpdate()
go changes.Submit("Character", "edit", token.UserID, character)
return character, nil
}
func (r *mutationResolver) RemoveCharacterNick(ctx context.Context, input input.CharacterNickInput) (models.Character, error) {
character, err := characters.FindID(input.ID)
if err != nil {
return models.Character{}, errors.New("Character not found")
}
token := auth.TokenFromContext(ctx)
if !token.PermittedUser(character.Author, "member", "character.edit") {
return models.Character{}, errors.New("You are not permitted to edit this character")
}
character, err = characters.RemoveNick(character, input.Nick)
if err != nil {
return models.Character{}, errors.New("Failed to remove nick: " + err.Error())
}
go logs.ScheduleFullUpdate()
go changes.Submit("Character", "edit", token.UserID, character)
return character, nil
}
func (r *mutationResolver) EditCharacter(ctx context.Context, input input.CharacterEditInput) (models.Character, error) {
character, err := characters.FindID(input.ID)
if err != nil {
return models.Character{}, errors.New("Character not found")
}
if input.Name != nil && len(*input.Name) < 2 {
return models.Character{}, errors.New("You need to provide a valid name (min length: 2)")
}
if input.ShortName != nil && len(*input.ShortName) < 2 {
return models.Character{}, errors.New("You need to provide a valid short name (min length: 2)")
}
token := auth.TokenFromContext(ctx)
if !token.PermittedUser(character.Author, "member", "character.edit") {
return models.Character{}, errors.New("You are not permitted to edit this character")
}
character, err = characters.Edit(character, input.Name, input.ShortName, input.Description)
if err != nil {
return models.Character{}, errors.New("Failed to edit character: " + err.Error())
}
go changes.Submit("Character", "edit", token.UserID, character)
return character, nil
}
func (r *mutationResolver) RemoveCharacter(ctx context.Context, input input.CharacterRemoveInput) (models.Character, error) {
character, err := characters.FindID(input.ID)
if err != nil {
return models.Character{}, errors.New("Character not found")
}
token := auth.TokenFromContext(ctx)
if !token.PermittedUser(character.Author, "member", "character.remove") {
return models.Character{}, errors.New("You are not permitted to remove this character")
}
character, err = characters.Remove(character)
if err != nil {
return models.Character{}, errors.New("Failed to remove character: " + err.Error())
}
go changes.Submit("Character", "remove", token.UserID, character)
return character, nil
}
+34
View File
@@ -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
+125
View File
@@ -0,0 +1,125 @@
package queries
import (
"context"
"errors"
"strings"
"git.aiterp.net/rpdata/api/graph2/input"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/internal/loader"
"git.aiterp.net/rpdata/api/models"
"git.aiterp.net/rpdata/api/models/changes"
"git.aiterp.net/rpdata/api/models/logs"
"github.com/99designs/gqlgen/graphql"
)
// Queries
func (r *resolver) Log(ctx context.Context, id string) (models.Log, error) {
return logs.FindID(id)
}
func (r *resolver) Logs(ctx context.Context, filter *logs.Filter) ([]models.Log, error) {
logs, err := logs.List(filter)
if err != nil {
return nil, err
}
reqCtx := graphql.GetRequestContext(ctx)
maybeCharacters := strings.Contains(reqCtx.RawQuery, "characters")
maybeChannels := strings.Contains(reqCtx.RawQuery, "channels")
if len(logs) >= 100 && (maybeCharacters || maybeChannels) {
loader := loader.FromContext(ctx)
if loader == nil {
return nil, errors.New("no loader")
}
for _, log := range logs {
if maybeChannels {
loader.PrimeChannels("name", log.ChannelName)
}
if maybeCharacters {
loader.PrimeCharacters("id", log.CharacterIDs...)
}
}
}
return logs, nil
}
// Mutations
func (r *mutationResolver) AddLog(ctx context.Context, input input.LogAddInput) (models.Log, error) {
token := auth.TokenFromContext(ctx)
if !token.Authenticated() || !token.Permitted("log.add") {
return models.Log{}, errors.New("You are not permitted to add logs")
}
open := input.Open != nil && *input.Open == true
title := ""
if input.Title != nil {
title = *input.Title
}
event := ""
if input.Event != nil {
event = *input.Event
}
description := ""
if input.Description != nil {
description = *input.Description
}
log, err := logs.Add(input.Date, input.Channel, title, event, description, open)
if !token.Authenticated() || !token.Permitted("log.add") {
return models.Log{}, errors.New("Failed to create log: " + err.Error())
}
go changes.Submit("Log", "add", token.UserID, log)
return log, nil
}
func (r *mutationResolver) EditLog(ctx context.Context, input input.LogEditInput) (models.Log, error) {
token := auth.TokenFromContext(ctx)
if !token.Authenticated() || !token.Permitted("log.edit") {
return models.Log{}, errors.New("You are not permitted to edit logs")
}
log, err := logs.FindID(input.ID)
if err != nil {
return models.Log{}, errors.New("Log not found")
}
log, err = logs.Edit(log, input.Title, input.Event, input.Description, input.Open)
if err != nil {
return models.Log{}, errors.New("Failed to edit log: " + err.Error())
}
go changes.Submit("Log", "edit", token.UserID, log)
return log, nil
}
func (r *mutationResolver) RemoveLog(ctx context.Context, input input.LogRemoveInput) (models.Log, error) {
token := auth.TokenFromContext(ctx)
if !token.Authenticated() || !token.Permitted("log.remove") {
return models.Log{}, errors.New("You are not permitted to remove logs")
}
log, err := logs.FindID(input.ID)
if err != nil {
return models.Log{}, errors.New("Log not found")
}
log, err = logs.Remove(log)
if err != nil {
return models.Log{}, errors.New("Failed to remove log: " + err.Error())
}
go changes.Submit("Log", "remove", token.UserID, log)
return log, nil
}
+151
View File
@@ -0,0 +1,151 @@
package queries
import (
"context"
"errors"
"git.aiterp.net/rpdata/api/models/changes"
"git.aiterp.net/rpdata/api/models/logs"
"git.aiterp.net/rpdata/api/graph2/input"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/models"
"git.aiterp.net/rpdata/api/models/posts"
)
// Queries
func (r *resolver) Post(ctx context.Context, id string) (models.Post, error) {
return posts.FindID(id)
}
func (r *resolver) Posts(ctx context.Context, filter *posts.Filter) ([]models.Post, error) {
// Some sanity checks to avoid querying an insame amount of posts.
if filter == nil {
filter = &posts.Filter{Limit: 256}
} else {
if (filter.Limit <= 0 || filter.Limit > 256) && filter.LogID == nil {
return nil, errors.New("a limit of 0 (no limit) or >256 without a logId is not allowed")
}
if len(filter.Kind) > 32 {
return nil, errors.New("You cannot specify more than 32 kinds")
}
if len(filter.ID) > 32 {
return nil, errors.New("You cannot specify more than 32 IDs")
}
}
return posts.List(filter)
}
// Mutation
func (r *mutationResolver) AddPost(ctx context.Context, input input.PostAddInput) (models.Post, error) {
token := auth.TokenFromContext(ctx)
if !token.Authenticated() || !token.Permitted("post.add") {
return models.Post{}, errors.New("You are not permitted to edit logs")
}
log, err := logs.FindID(input.LogID)
if err != nil {
return models.Post{}, err
}
post, err := posts.Add(log, input.Time, input.Kind, input.Nick, input.Text)
if err != nil {
return models.Post{}, err
}
go logs.UpdateCharacters(log)
go changes.Submit("Post", "add", token.UserID, post)
return post, nil
}
func (r *mutationResolver) EditPost(ctx context.Context, input input.PostEditInput) (models.Post, error) {
token := auth.TokenFromContext(ctx)
if !token.Authenticated() || !token.Permitted("post.edit") {
return models.Post{}, errors.New("You are not permitted to edit logs")
}
post, err := posts.FindID(input.ID)
if err != nil {
return models.Post{}, errors.New("Post not found")
}
if input.Nick != nil {
go func() {
log, err := logs.FindID(post.LogID)
if err != nil {
return
}
logs.UpdateCharacters(log)
}()
}
post, err = posts.Edit(post, input.Time, input.Kind, input.Nick, input.Text)
if err != nil {
return models.Post{}, errors.New("Adding post failed: " + err.Error())
}
go changes.Submit("Post", "edit", token.UserID, post)
return post, nil
}
func (r *mutationResolver) MovePost(ctx context.Context, input input.PostMoveInput) ([]models.Post, error) {
token := auth.TokenFromContext(ctx)
if !token.Authenticated() || !token.Permitted("post.move") {
return nil, errors.New("You are not permitted to edit logs")
}
post, err := posts.FindID(input.ID)
if err != nil {
return nil, errors.New("Post not found")
}
posts, err := posts.Move(post, input.ToPosition)
if err != nil {
return nil, errors.New("Moving posts failed: " + err.Error())
}
posts2 := make([]interface{}, len(posts))
for i := range posts {
posts2[i] = posts[i]
}
go changes.Submit("Post", "move", token.UserID, posts2...)
return posts, nil
}
func (r *mutationResolver) RemovePost(ctx context.Context, input input.PostRemoveInput) (models.Post, error) {
token := auth.TokenFromContext(ctx)
if !token.Authenticated() || !token.Permitted("post.remove") {
return models.Post{}, errors.New("You are not permitted to edit logs")
}
post, err := posts.FindID(input.ID)
if err != nil {
return models.Post{}, errors.New("Post not found (before removing, of course)")
}
post, err = posts.Remove(post)
if err != nil {
return models.Post{}, errors.New("Could not remove post: " + err.Error())
}
go func() {
log, err := logs.FindID(post.LogID)
if err != nil {
return
}
logs.UpdateCharacters(log)
}()
go changes.Submit("Post", "remove", token.UserID, post)
return post, nil
}
+10
View File
@@ -0,0 +1,10 @@
package queries
type resolver struct{}
type mutationResolver struct{}
// Resolver has all the queries
var Resolver resolver
// MutationResolver brings the mutagens.
var MutationResolver *mutationResolver
+136
View File
@@ -0,0 +1,136 @@
package queries
import (
"context"
"errors"
"time"
"git.aiterp.net/rpdata/api/graph2/input"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/models"
"git.aiterp.net/rpdata/api/models/chapters"
"git.aiterp.net/rpdata/api/models/stories"
)
func (r *resolver) Story(ctx context.Context, id string) (models.Story, error) {
return stories.FindID(id)
}
func (r *resolver) Stories(ctx context.Context, filter *stories.Filter) ([]models.Story, error) {
if filter != nil {
if filter.Unlisted != nil && *filter.Unlisted == true {
token := auth.TokenFromContext(ctx)
if !token.Authenticated() {
return nil, errors.New("You are not permitted to view unlisted stories")
}
if !token.Permitted("story.unlisted") {
filter.Author = &token.UserID
}
}
}
return stories.List(filter)
}
// Mutations
func (r *mutationResolver) AddStory(ctx context.Context, input input.StoryAddInput) (models.Story, error) {
token := auth.TokenFromContext(ctx)
if token == nil || !token.Permitted("member", "story.add") {
return models.Story{}, errors.New("Permission denied")
}
author := token.UserID
if input.Author != nil && *input.Author != author {
if !token.Permitted("story.add") {
return models.Story{}, errors.New("You are not permitted to add a story in another author's name")
}
author = *input.Author
}
fictionalDate := time.Time{}
if input.FictionalDate != nil {
fictionalDate = *input.FictionalDate
}
listed := input.Listed != nil && *input.Listed
open := input.Open != nil && *input.Open
return stories.Add(input.Name, author, input.Category, listed, open, input.Tags, time.Now(), fictionalDate)
}
func (r *mutationResolver) AddStoryTag(ctx context.Context, input input.StoryTagAddInput) (models.Story, error) {
token := auth.TokenFromContext(ctx)
story, err := stories.FindID(input.ID)
if err != nil {
return models.Story{}, errors.New("Story not found")
}
if !token.PermittedUser(story.Author, "member", "story.edit") {
return models.Story{}, errors.New("You are not permitted to edit this story")
}
return stories.AddTag(story, input.Tag)
}
func (r *mutationResolver) RemoveStoryTag(ctx context.Context, input input.StoryTagRemoveInput) (models.Story, error) {
token := auth.TokenFromContext(ctx)
story, err := stories.FindID(input.ID)
if err != nil {
return models.Story{}, errors.New("Story not found")
}
if !token.PermittedUser(story.Author, "member", "story.edit") {
return models.Story{}, errors.New("You are not permitted to edit this story")
}
return stories.RemoveTag(story, input.Tag)
}
func (r *mutationResolver) EditStory(ctx context.Context, input input.StoryEditInput) (models.Story, error) {
token := auth.TokenFromContext(ctx)
story, err := stories.FindID(input.ID)
if err != nil {
return models.Story{}, errors.New("Story not found")
}
if !token.PermittedUser(story.Author, "member", "story.edit") {
return models.Story{}, errors.New("You are not permitted to remove this story")
}
if input.ClearFictionalDate != nil && *input.ClearFictionalDate {
input.FictionalDate = &time.Time{}
}
return stories.Edit(story, input.Name, input.Category, input.Listed, input.Open, input.FictionalDate)
}
func (r *mutationResolver) RemoveStory(ctx context.Context, input input.StoryRemoveInput) (models.Story, error) {
token := auth.TokenFromContext(ctx)
story, err := stories.FindID(input.ID)
if err != nil {
return models.Story{}, errors.New("Story not found")
}
if !token.PermittedUser(story.Author, "member", "story.remove") {
return models.Story{}, errors.New("You are not permitted to remove this story")
}
story, err = stories.Remove(story)
if err != nil {
return models.Story{}, err
}
err = chapters.RemoveStory(story)
if err != nil {
return models.Story{}, errors.New("Failed to remove chapters, but story is removed: " + err.Error())
}
return story, nil
}
+12
View File
@@ -0,0 +1,12 @@
package queries
import (
"context"
"git.aiterp.net/rpdata/api/models"
"git.aiterp.net/rpdata/api/models/tags"
)
func (r *resolver) Tags(ctx context.Context) ([]models.Tag, error) {
return tags.List()
}
+18
View File
@@ -0,0 +1,18 @@
package queries
import (
"context"
"errors"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/models"
)
func (r *resolver) Token(ctx context.Context) (models.Token, error) {
token := auth.TokenFromContext(ctx)
if !token.Authenticated() {
return models.Token{}, errors.New("No (valid) token")
}
return *token, nil
}
+136
View File
@@ -0,0 +1,136 @@
schema {
query: Query
mutation: Mutation
}
type Query {
# Find character by either an ID or a nick.
character(id: String, nick: String): Character!
# Find characters
characters(filter: CharactersFilter): [Character!]!
# Find channel by name
channel(name: String!): Channel!
# Find channels
channels(filter: ChannelsFilter): [Channel!]!
# Find post by ID.
post(id: String!): Post!
# Find posts
posts(filter: PostsFilter): [Post!]!
# Find log by ID
log(id: String!): Log!
# Find logs
logs(filter: LogsFilter): [Log!]!
# Find story chapter by ID
chapter(id: String!): Chapter!
# Find all distinct tags used in stories
tags: [Tag!]!
# Find story by ID
story(id: String!): Story!
# Find stories
stories(filter: StoriesFilter): [Story!]!
# Find file by ID
file(id: String!): File!
# Find files
files(filter: FilesFilter): [File!]!
# Get information about the token, useful for debugging.
token: Token!
}
type Mutation {
# Add a story
addStory(input: StoryAddInput!): Story!
# Add a story tag
addStoryTag(input: StoryTagAddInput!): Story!
# Remove a story tag
removeStoryTag(input: StoryTagRemoveInput!): Story!
# Edit a story
editStory(input: StoryEditInput!): Story!
# Remove a story
removeStory(input: StoryRemoveInput!): Story!
# Add a chapter to a story
addChapter(input: ChapterAddInput!): Chapter!
# Edit a chapter
editChapter(input: ChapterEditInput!): Chapter!
# Remove a chapter
removeChapter(input: ChapterRemoveInput!): Chapter!
# Add a new log
addLog(input: LogAddInput!): Log!
# Edit a log
editLog(input: LogEditInput!): Log!
# Remove a log
removeLog(input: LogRemoveInput!): Log!
# Add a post
addPost(input: PostAddInput!): Post!
# Edit a post
editPost(input: PostEditInput!): Post!
# Move a post. All affected posts will be returned.
movePost(input: PostMoveInput!): [Post!]!
# Remove a post
removePost(input: PostRemoveInput!): Post!
# Add a new character
addCharacter(input: CharacterAddInput!): Character!
# Add nick to character
addCharacterNick(input: CharacterNickInput!): Character!
# Remove nick from character
removeCharacterNick(input: CharacterNickInput!): Character!
# Edit character
editCharacter(input: CharacterEditInput!): Character!
# Remove a character
removeCharacter(input: CharacterRemoveInput!): Character!
# Add a channel
addChannel(input: ChannelAddInput!): Channel!
# Edit a channel
editChannel(input: ChannelEditInput!): Channel!
}
# A Date represents a RFC3339 encoded date with up to millisecond precision.
scalar Date
+63
View File
@@ -0,0 +1,63 @@
# Information about an IRC channel
type Channel {
# The channel's name
name: String!
# Whether the channel should be logged
logged: Boolean!
# Whether the channel is a hub channel
hub: Boolean!
# The event name, or `null` if none is specified
eventName: String
# The location name, or `null` if none is specified
locationName: String
}
# Filters for the channels query
input ChannelsFilter {
# Filter to either logged or unlogged channels
logged: Boolean
# Filter by event name
eventName: String
# Filter by location name
locationName: String
}
input ChannelAddInput {
# The channel's name
name: String!
# Whether the channel should be logged
logged: Boolean
# Whether the channel is a hub channel
hub: Boolean
# The event name, or `null` if none is specified
eventName: String
# The location name, or `null` if none is specified
locationName: String
}
input ChannelEditInput {
# The channel's name
name: String!
# Whether the channel should be logged
logged: Boolean
# Whether the channel is a hub channel
hub: Boolean
# The event name, or `null` if none is specified
eventName: String
# The location name, or `null` if none is specified
locationName: String
}
+68
View File
@@ -0,0 +1,68 @@
# A Chapter is the main content body of a story.
type Chapter {
# The chapter's ID
id: String!
# The chapter's title
title: String!
# The chapter's author
author: String!
# The chapter's source
source: String!
# When the chapter was posted initialy
createdDate: Date!
# The in-universe date and time of the chapter
fictionalDate: Date
# The date of edit.
editedDate: Date!
}
# Input for addChapter mutation
input ChapterAddInput {
# The story to add a chapter to
storyId: String!
# The title to give the chapter. It can not be left empty
title: String!
# The author to set for the chapter. It will use the logged in user ID, and only
# users with appropriate permissions can post as another author. Even then, it's only
# for importing/moving content. This author name will not be used when logging the
# submission
author: String
# The markdown source for the content
source: String!
# Optionally, assign a fictional date for the chapter
fictionalDate: Date
}
# Input for editChapter mutation
input ChapterEditInput {
# The chapter to edit
id: String!
# Change the chapter's title
title: String
# Change the source
source: String
# Set the fictional date for a chapter
fictionalDate: Date
# Remove the fictional date for a chapter
clearFictionalDate: Boolean
}
# Input for removeChapter mutation
input ChapterRemoveInput {
# The chapter to remove
id: String!
}
+91
View File
@@ -0,0 +1,91 @@
# A Character represents an RP character
type Character {
# A unique identifier for the character
id: String!
# The primary IRC nick belonging to the character
nick: String
# All IRC nicks associated with this character
nicks: [String!]!
# The character's author
author: String!
# The character's name
name: String!
# The name to display when space is scarce, usually the first/given name
shortName: String!
# A short description of the character
description: String!
}
# Filter for characters query
input CharactersFilter {
# Filter by character IDs
ids: [String!]
# Filter by nicks
nicks: [String!]
# Filter by names
names: [String!]
# Filter by author
author: String
# Filter by text search matching against the character's description
search: String
}
# Input for adding characters
input CharacterAddInput {
# The primary IRC nick name to recognize this character by
nick: String!
# The character's name
name: String!
# Optioanl shortened name. By default, it uses the first token in the name
shortName: String
# Description for a character.
description: String
# Optioanlly, specify another author. This needs special permissions if it's not
# your own username
author: String
}
# Input for addNick and removeNick mutation
input CharacterNickInput {
# The ID of the character
id: String!
# The nick to add or remove
nick: String!
}
# Inpuit for editCharacter mutation
input CharacterEditInput {
# The id for the character to edit
id: String!
# The full name of the character -- not the salarian full name!
name: String
# The character's short name that is used in compact lists
shortName: String
# A short description for the character
description: String
}
# Input for removeCharacter mutation
input CharacterRemoveInput {
# The id for the character to edit
id: String!
}
+52
View File
@@ -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
}
+103
View File
@@ -0,0 +1,103 @@
# A Log is the "file" of an RP session
type Log {
# A unique identifier for the log.
id: String!
# A secondary unique identifier for the log. This is a lot shorter and more suitable for storage when
# the link doesn't need to be as expressive.
shortId: String!
# The date for a log.
date: Date!
# The channel of a log.
channelName: String!
# Information about the channel this log is in.
channel: Channel!
# The session's title.
title: String!
# The log's event, which is the same as the tags in the previous logbot site.
# Empty string means that it's no event.
eventName: String!
# The description of a session, which is empty if unset.
description: String!
# Whether the log session is open.
open: Boolean!
# The characters involved in the log file.
characters: [Character!]!
# The posts of the logfile, which can be filtered by kinds.
posts(kinds:[String!]): [Post!]!
}
# Filter for logs query
input LogsFilter {
# Channels to limit results to (inclusive)
channels: [String!]
# Events to limit results to (inclusive)
events: [String!]
# Characters to limit results to (exclusive)
characters: [String!]
# Search post content
search: String
# Limit by whether it's open or not
open: Boolean
# Limit the amount of results you get back
limit: Int
}
# Input for addLog mutation
input LogAddInput {
# The date of the log.
date: Date!
# The channel where the log is set
channel: String!
# Optional: The log title
title: String
# Optional: Whether the log is open
open: Boolean
# Optional: The log event name
event: String
# Optional: A short description of the log
description: String
}
# Input for addLog mutation
input LogEditInput {
# The id of the log
id: String!
# The log title
title: String
# The log event name
event: String
# A short description of the log
description: String
# Open/close the log
open: Boolean
}
# Input for removeLog mutation
input LogRemoveInput {
# The id of the log to remove
id: String!
}
+82
View File
@@ -0,0 +1,82 @@
# A Post is a part of a log
type Post {
# The post's ID
id: String!
# The post's Log ID. This is the closest thing to a link back since this API graph doesn't have any cycles.
logId: String!
# The date and time of posting
time: Date!
# The kind of post this is. Only "text", "scene" and "action" are RP, while others are annotations and 'commands'.
kind: String!
# The character nick
nick: String!
# The post's text, which purpose depends on the kind
text: String!
# The post's position, used for reordering
position: Int!
}
# Input for the addPost mutation
input PostAddInput {
# The log's ID that this post should be a part of
logId: String!
# The date and time of posting, in a RFC3339 format with up to a nanosecond's precision
time: Date!
# The kind of post this is. Only "text", "scene" and "action" are RP, while others are annotations and 'commands'.
kind: String!
# The character nick, or command invoker for non-RP stuff
nick: String!
# The post's text, which purpose depends on the kind
text: String!
}
# Input for the editPost mutation
input PostEditInput {
# The Post ID
id: String!
# The date and time of posting, in a RFC3339 format with up to a nanosecond's precision
time: Date
# The kind of post this is. Only "text", "scene" and "action" are RP, while others are annotations and 'commands'.
kind: String
# The character nick, or command invoker for non-RP stuff
nick: String
# The post's text, which purpose depends on the kind
text: String
}
# Input for the movePost mutation
input PostMoveInput {
# The Post ID
id: String!
# Target index
toPosition: Int!
}
# Input for the removePost mutation
input PostRemoveInput {
# The Post ID
id: String!
}
# Filter for posts query
input PostsFilter {
id: [String!]
kind: [String!]
logId: String
limit: Int
}
+160
View File
@@ -0,0 +1,160 @@
# A Story is a piece of content that, unlike wiki articles, are not something that should be ordered by time instead of title. It can
# contain multiple chapters by one or more autohrs
type Story {
# The story's ID
id: String!
# The story's name, which will show up in the lists.
name: String!
# The story's author
author: String!
# Whether other users may add/edit chapters to the story.
open: Boolean!
# Whether users without a direct link can find this story.
listed: Boolean!
# The category of the story
category: StoryCategory!
# The tags for this tory
tags: [Tag!]!
# The chapters of this story
chapters: [Chapter!]!
# The date the story was created.
createdDate: Date!
# The date the story is set in.
fictionalDate: Date
# The date of the last major update to the story. This being bumped means a new chapter has been added.
updatedDate: Date!
}
# Filter for stories query.
input StoriesFilter {
# What author to query for
author: String
# What category to query for
category: StoryCategory
# What tags to query for
tags: [TagInput!]
# If true, it will only show unlisted page you have access to
unlisted: Boolean
# Whether the page is open for additions by other users
open: Boolean
# The earliest fictionalDate
earliestFictionalDate: Date
# The latest fictionalDate
latestFictionalDate: Date
# The max amount of stories to get (default: 30)
limit: Int
}
# Input for the addStory mutation
input StoryAddInput {
# Set the name of the story, which will show up as the title, and as a suggestion for
# the first chapter being added.
name: String!
# Set the category for the new story.
category: StoryCategory!
# Add the story under another name. This requires the story.add permission, and should not be
# abused. The action will be logged with the actual creator's user ID.
author: String
# Allow other users to post chapters to the story.
open: Boolean
# Allow other users to see this page in any lists or search results.
listed: Boolean
# Set which tags the story should have.
tags: [TagInput!]
# Set the fictional date of the story.
fictionalDate: Date
}
# Input for the editStory mutation
input StoryEditInput {
# What story to edit
id: String!
# Set the name of the story, which will show up as the title, and as a suggestion for
# the first chapter being added.
name: String
# Set the category for the new story.
category: StoryCategory
# Add the story under another name. This requires the story.add permission, and should not be
# abused. The action will be logged with the actual creator's user ID.
author: String
# Change whether to allow others to add chapters
open: Boolean
# Change whether to show this story in the list and search results
listed: Boolean
# Set the fictional date of the story.
fictionalDate: Date
# Clear the fictional date of the story.
clearFictionalDate: Boolean
}
# Input for the addStoryTag mutation
input StoryTagAddInput {
# What story to add the tag to.
id: String!
# The tag to add.
tag: TagInput!
}
# Input for the removeStoryTag mutation
input StoryTagRemoveInput {
# What story to remove the tag from.
id: String!
# The tag to remove.
tag: TagInput!
}
# Input for the removeStory mutation
input StoryRemoveInput {
# What story to remove.
id: String!
}
# Possible values for Story.category
enum StoryCategory {
# General information
Info
# News stories
News
# Description and content of a document or item
Document
# Information about something going on in the background that may or may not inform RP
Background
# A short story
Story
}
+37
View File
@@ -0,0 +1,37 @@
# A Tag is a means of associating stories that have details in common with one another.
type Tag {
# The tag's kind
kind: TagKind!
# The tag's name
name: String!
}
# A Tag is a means of associating stories that have details in common with one another.
input TagInput {
# The tag's kind
kind: TagKind!
# The tag's name
name: String!
}
# Allowed values for Tag.kind
enum TagKind {
# An organization is a catch all term for in-universe corporations, teams, groups, cults, forces, etc...
Organization
# A character tag should have the exact full name of the character.
Character
# A location is anything from a planet to an establishment. This may overlap with an organization, and if so, both
# kinds of tags should be used.
Location
# An event is a plot or a part of a plot.
Event
# None of the above, but it does still tie multiple stories together. The new story/chapter format may obsolete this tag kind.
Series
}
+8
View File
@@ -0,0 +1,8 @@
# Token represents the data parsed and validated from the bearer token.
type Token {
# The user represented in the token.
user: User!
# The permissions for the token.
permissions: [String!]!
}
+8
View File
@@ -0,0 +1,8 @@
# A user represents a member on the RPData platform.
type User {
# The user's unique ID / username
id: String!
# What the user is permitted to do.
permissions: [String!]!
}
+21
View File
@@ -0,0 +1,21 @@
package types
import (
"context"
"time"
"git.aiterp.net/rpdata/api/models"
)
type chapterResolver struct{}
func (r *chapterResolver) FictionalDate(ctx context.Context, chapter *models.Chapter) (*time.Time, error) {
if chapter.FictionalDate.IsZero() {
return nil, nil
}
return &chapter.FictionalDate, nil
}
// ChapterResolver is a resolver
var ChapterResolver chapterResolver
+16
View File
@@ -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
+41
View File
@@ -0,0 +1,41 @@
package types
import (
"context"
"errors"
"git.aiterp.net/rpdata/api/internal/loader"
"git.aiterp.net/rpdata/api/models"
"git.aiterp.net/rpdata/api/models/posts"
)
type logResolver struct{}
func (r *logResolver) Channel(ctx context.Context, log *models.Log) (models.Channel, error) {
loader := loader.FromContext(ctx)
if loader == nil {
return models.Channel{}, errors.New("no loader")
}
return loader.Channel("name", log.ChannelName)
}
func (r *logResolver) Characters(ctx context.Context, log *models.Log) ([]models.Character, error) {
loader := loader.FromContext(ctx)
if loader == nil {
return nil, errors.New("no loader")
}
return loader.Characters("id", log.CharacterIDs...)
}
func (r *logResolver) Posts(ctx context.Context, log *models.Log, kinds []string) ([]models.Post, error) {
return posts.List(&posts.Filter{
LogID: &log.ShortID,
Kind: kinds,
Limit: 0,
})
}
// LogResolver is a resolver
var LogResolver logResolver
+27
View File
@@ -0,0 +1,27 @@
package types
import (
"context"
"time"
"git.aiterp.net/rpdata/api/models/chapters"
"git.aiterp.net/rpdata/api/models"
)
type storyResolver struct{}
func (r *storyResolver) FictionalDate(ctx context.Context, story *models.Story) (*time.Time, error) {
if story.FictionalDate.IsZero() {
return nil, nil
}
return &story.FictionalDate, nil
}
func (r *storyResolver) Chapters(ctx context.Context, story *models.Story) ([]models.Chapter, error) {
return chapters.ListStoryID(story.ID)
}
// StoryResolver is a resolver
var StoryResolver storyResolver
+17
View File
@@ -0,0 +1,17 @@
package types
import (
"context"
"git.aiterp.net/rpdata/api/models"
"git.aiterp.net/rpdata/api/models/users"
)
type tokenResolver struct{}
func (r *tokenResolver) User(ctx context.Context, token *models.Token) (models.User, error) {
return users.Find(token.UserID)
}
// TokenResolver is a resolver
var TokenResolver tokenResolver
-156
View File
@@ -1,156 +0,0 @@
package resolver
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/session"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/channel"
)
// ChannelArgs is args for channel query
type ChannelArgs struct {
Name string
}
// Channel implements the channel query
func (r *QueryResolver) Channel(ctx context.Context, args *ChannelArgs) (*types.ChannelResolver, error) {
channel, err := channel.FindName(args.Name)
if err != nil {
return nil, err
}
return &types.ChannelResolver{C: channel}, nil
}
// ChannelsArgs is args for channel query
type ChannelsArgs struct {
Logged *bool
}
// Channels implements the channels query
func (r *QueryResolver) Channels(ctx context.Context, args *ChannelsArgs) ([]*types.ChannelResolver, error) {
channels, err := channel.List(args.Logged != nil && *args.Logged)
if err != nil {
return nil, err
}
resolvers := make([]*types.ChannelResolver, len(channels))
for i := range channels {
resolvers[i] = &types.ChannelResolver{C: channels[i]}
}
return resolvers, nil
}
// ChannelAddArgs is input for the addChannel mutation
type ChannelAddArgs struct {
Input *struct {
Name string
Logged *bool
Hub *bool
EventName *string
LocationName *string
}
}
// AddChannel resolves the addChannel mutation
func (r *MutationResolver) AddChannel(ctx context.Context, args *ChannelAddArgs) (*types.ChannelResolver, error) {
input := args.Input
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("channel.add") {
return nil, ErrUnauthorized
}
logged := input.Logged != nil && *input.Logged
hub := input.Hub != nil && *input.Hub
eventName := ""
if input.EventName != nil {
eventName = *input.EventName
}
locationName := ""
if input.LocationName != nil {
locationName = *input.LocationName
}
channel, err := channel.New(input.Name, logged, hub, eventName, locationName)
if err != nil {
return nil, err
}
go change.Submit("Channel", "add", user.ID, channel.Name, map[string]interface{}{
"logged": channel.Logged,
"hub": channel.Hub,
"location": input.LocationName,
"event": input.EventName,
})
return &types.ChannelResolver{C: channel}, nil
}
// ChannelEditArgs is input for the editChannel mutation
type ChannelEditArgs struct {
Input *struct {
Name string
Logged *bool
Hub *bool
EventName *string
LocationName *string
}
}
// EditChannel resolves the editChannel mutation
func (r *MutationResolver) EditChannel(ctx context.Context, args *ChannelEditArgs) (*types.ChannelResolver, error) {
input := args.Input
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("channel.edit") {
return nil, ErrUnauthorized
}
channel, err := channel.FindName(input.Name)
if err != nil {
return nil, err
}
err = channel.Edit(input.Logged, input.Hub, input.EventName, input.LocationName)
if err != nil {
return nil, err
}
go change.Submit("Channel", "edit", user.ID, channel.Name, map[string]interface{}{
"logged": input.Logged,
"hub": input.Hub,
"location": input.LocationName,
"event": input.EventName,
})
return &types.ChannelResolver{C: channel}, nil
}
// RemoveChannel resolves the editChannel mutation
func (r *MutationResolver) RemoveChannel(ctx context.Context, args ChannelArgs) (*types.ChannelResolver, error) {
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("channel.remove") {
return nil, ErrUnauthorized
}
channel, err := channel.FindName(args.Name)
if err != nil {
return nil, err
}
err = channel.Remove()
if err != nil {
return nil, err
}
go change.Submit("Channel", "remove", user.ID, channel.Name, nil)
return &types.ChannelResolver{C: channel}, nil
}
-161
View File
@@ -1,161 +0,0 @@
package resolver
import (
"context"
"time"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/session"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/story"
)
// ChapterArgs is args for chapter query
type ChapterArgs struct {
ID string
}
// Chapter implements the chapter query
func (r *QueryResolver) Chapter(ctx context.Context, args *ChapterArgs) (*types.ChapterResolver, error) {
chapter, err := story.FindChapterID(args.ID)
if err != nil {
return nil, err
}
return &types.ChapterResolver{C: chapter}, nil
}
// AddChapterArgs is args for the addChapter mutation
type AddChapterArgs struct {
Input *struct {
StoryID string
Title string
Author *string
Source string
FictionalDate *string
}
}
// AddChapter implements the addChapter mutation
func (r *MutationResolver) AddChapter(ctx context.Context, args *AddChapterArgs) (*types.ChapterResolver, error) {
input := args.Input
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("member", "chapter.add") {
return nil, ErrUnauthorized
}
story, err := story.FindID(input.StoryID)
if err != nil {
return nil, err
}
author := user.ID
if input.Author != nil {
author = *input.Author
if user.ID != author && !user.Permitted("chapter.add") {
return nil, ErrPermissionDenied
}
}
fictionalDate := time.Time{}
if input.FictionalDate != nil {
fictionalDate, err = time.Parse(time.RFC3339Nano, *input.FictionalDate)
if err != nil {
return nil, err
}
}
chapter, err := story.AddChapter(input.Title, author, input.Source, time.Now(), fictionalDate)
if err != nil {
return nil, err
}
go change.Submit("Chapter", "add", user.ID, chapter.ID, map[string]interface{}{
"title": input.Title,
"author": author,
"fictionalDate": fictionalDate,
})
return &types.ChapterResolver{C: chapter}, nil
}
// EditChapterArgs is args for the editChapter mutation
type EditChapterArgs struct {
Input *struct {
ID string
Title *string
Source *string
FictionalDate *string
}
}
// EditChapter implements the editChapter mutation
func (r *MutationResolver) EditChapter(ctx context.Context, args *EditChapterArgs) (*types.ChapterResolver, error) {
input := args.Input
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("member", "chapter.edit") {
return nil, ErrUnauthorized
}
chapter, err := story.FindChapterID(input.ID)
if err != nil {
return nil, err
}
if chapter.Author != user.ID && !user.Permitted("chapter.edit") {
return nil, ErrPermissionDenied
}
var fictionalDate *time.Time
if input.FictionalDate != nil {
date, err := time.Parse(time.RFC3339Nano, *input.FictionalDate)
if err != nil {
return nil, err
}
fictionalDate = &date
}
err = chapter.Edit(input.Title, input.Source, fictionalDate)
if err != nil {
return nil, err
}
go change.Submit("Chapter", "edit", user.ID, chapter.ID, map[string]interface{}{
"title": input.Title,
"source": input.Source,
"fictionalDate": fictionalDate,
})
return &types.ChapterResolver{C: chapter}, nil
}
// DeleteChapterArgs is args for the addChapter mutation
type DeleteChapterArgs struct {
ID string
}
// RemoveChapter implements the removeChapter mutation
func (r *MutationResolver) RemoveChapter(ctx context.Context, args *DeleteChapterArgs) (*types.ChapterResolver, error) {
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("member", "chapter.edit") {
return nil, ErrUnauthorized
}
chapter, err := story.FindChapterID(args.ID)
if err != nil {
return nil, err
}
err = chapter.Remove()
if err != nil {
return nil, err
}
go change.Submit("Chapter", "remove", user.ID, chapter.ID, nil)
return &types.ChapterResolver{C: chapter}, nil
}
-299
View File
@@ -1,299 +0,0 @@
package resolver
import (
"context"
"errors"
"strings"
"git.aiterp.net/rpdata/api/graphql/loader"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/session"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/character"
"git.aiterp.net/rpdata/api/model/log"
)
// CharacterArgs is an arg
type CharacterArgs struct {
ID *string
Nick *string
}
// Character resolver
func (r *QueryResolver) Character(ctx context.Context, args *CharacterArgs) (*types.CharacterResolver, error) {
var char character.Character
var err error
loader := loader.FromContext(ctx)
if loader == nil {
return nil, errors.New("no loader")
}
switch {
case args.ID != nil:
char, err = character.FindID(*args.ID)
case args.Nick != nil:
char, err = character.FindNick(*args.Nick)
default:
err = ErrCannotResolve
}
if err != nil {
return nil, err
}
return &types.CharacterResolver{C: char}, nil
}
// CharactersArgs is an arg
type CharactersArgs struct {
IDs *[]string
Nicks *[]string
Author *string
}
// Characters resolves the characters query
func (r *QueryResolver) Characters(ctx context.Context, args *CharactersArgs) ([]*types.CharacterResolver, error) {
var chars []character.Character
var err error
loader := loader.FromContext(ctx)
if loader == nil {
return nil, errors.New("no loader")
}
switch {
case args.IDs != nil:
chars, err = character.ListIDs(*args.IDs...)
case args.Nicks != nil:
chars, err = character.ListNicks(*args.Nicks...)
case args.Author != nil:
chars, err = character.ListAuthor(*args.Author)
default:
chars, err = character.List()
}
if err != nil {
return nil, err
}
resolvers := make([]*types.CharacterResolver, 0, len(chars))
for i := range chars {
if chars[i].ID == "" {
continue
}
resolvers = append(resolvers, &types.CharacterResolver{C: chars[i]})
}
return resolvers, nil
}
// AddCharacterInput is args for the addCharacter mutation
type AddCharacterInput struct {
Nick string
Name string
ShortName *string
Author *string
Description *string
}
// AddCharacter resolves the addCharacter mutation
func (r *MutationResolver) AddCharacter(ctx context.Context, args struct{ Input *AddCharacterInput }) (*types.CharacterResolver, error) {
input := args.Input
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("member", "character.add") {
return nil, ErrUnauthorized
}
nick := input.Nick
name := input.Name
shortName := ""
if input.ShortName != nil {
shortName = *input.ShortName
} else {
shortName = strings.SplitN(input.Name, " ", 2)[0]
}
author := user.ID
if input.Author != nil {
author = *input.Author
if author != user.ID && !user.Permitted("character.add") {
return nil, ErrPermissionDenied
}
}
description := ""
if input.Description != nil {
description = *input.Description
}
character, err := character.New(nick, name, shortName, author, description)
if err != nil {
return nil, err
}
go change.Submit("Character", "add", user.ID, character.ID, map[string]interface{}{
"name": character.Name,
"nick": character.Nicks[0],
"author": character.Author,
})
log.ScheduleCharacterUpdate()
return &types.CharacterResolver{C: character}, nil
}
// CharacterNickInput is args for mutation addCharacterNick/removeCharacterNick
type CharacterNickInput struct {
ID string
Nick string
}
// AddCharacterNick resolves the addCharacterNick mutation
func (r *MutationResolver) AddCharacterNick(ctx context.Context, args struct{ Input *CharacterNickInput }) (*types.CharacterResolver, error) {
input := args.Input
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("member") {
return nil, ErrUnauthorized
}
character, err := character.FindID(input.ID)
if err != nil {
return nil, err
}
if character.Author != user.ID && !user.Permitted("character.edit") {
return nil, ErrPermissionDenied
}
err = character.AddNick(input.Nick)
if err != nil {
return nil, err
}
go change.Submit("Character", "add.nick", user.ID, character.ID, map[string]interface{}{
"nick": input.Nick,
})
log.ScheduleCharacterUpdate()
return &types.CharacterResolver{C: character}, nil
}
// RemoveCharacterNick resolves the removeCharacterNick mutation
func (r *MutationResolver) RemoveCharacterNick(ctx context.Context, args struct{ Input *CharacterNickInput }) (*types.CharacterResolver, error) {
input := args.Input
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("member") {
return nil, ErrUnauthorized
}
character, err := character.FindID(input.ID)
if err != nil {
return nil, err
}
if character.Author != user.ID && !user.Permitted("character.edit") {
return nil, ErrPermissionDenied
}
err = character.RemoveNick(input.Nick)
if err != nil {
return nil, err
}
go change.Submit("Character", "remove.nick", user.ID, character.ID, map[string]interface{}{
"nick": input.Nick,
})
log.ScheduleCharacterUpdate()
return &types.CharacterResolver{C: character}, nil
}
// CharacterEditInput is args for mutation addCharacterNick/removeCharacterNick
type CharacterEditInput struct {
ID string
Name *string
ShortName *string
Description *string
}
// EditCharacter resolves the editCharacter mutation
func (r *MutationResolver) EditCharacter(ctx context.Context, args struct{ Input *CharacterEditInput }) (*types.CharacterResolver, error) {
input := args.Input
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("member") {
return nil, ErrUnauthorized
}
character, err := character.FindID(input.ID)
if err != nil {
return nil, err
}
if character.Author != user.ID && !user.Permitted("character.edit") {
return nil, ErrPermissionDenied
}
name := ""
if input.Name != nil {
name = *input.Name
}
shortName := ""
if input.ShortName != nil {
shortName = *input.ShortName
}
description := ""
if input.Description != nil {
description = *input.Description
}
err = character.Edit(name, shortName, description)
if err != nil {
return nil, err
}
go change.Submit("Character", "edit", user.ID, character.ID, map[string]interface{}{
"name": character.Name,
"shortName": character.ShortName,
"description": character.Description,
})
return &types.CharacterResolver{C: character}, nil
}
// RemoveCharacter resolves the removeCharacter mutation
func (r *MutationResolver) RemoveCharacter(ctx context.Context, args struct{ ID string }) (*types.CharacterResolver, error) {
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("member") {
return nil, ErrUnauthorized
}
character, err := character.FindID(args.ID)
if err != nil {
return nil, err
}
if character.Author != user.ID && !user.Permitted("character.remove") {
return nil, ErrPermissionDenied
}
err = character.Remove()
if err != nil {
return nil, err
}
go change.Submit("Character", "remove", user.ID, character.ID, map[string]interface{}{
"name": character.Name,
"author": character.Author,
"nicks": character.Nicks,
})
return &types.CharacterResolver{C: character}, nil
}
-115
View File
@@ -1,115 +0,0 @@
package resolver
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/session"
"git.aiterp.net/rpdata/api/model/file"
)
// FileArgs is an arg
type FileArgs struct {
ID string
}
// File implements the file query
func (r *QueryResolver) File(ctx context.Context, args *FileArgs) (*types.FileResolver, error) {
file, err := file.FindID(args.ID)
if err != nil {
return nil, err
}
return &types.FileResolver{F: file}, nil
}
// FilesArgs is an arg
type FilesArgs struct {
IncludePublic *bool
MimeTypes *[]string
}
// Files implements the file query
func (r *QueryResolver) Files(ctx context.Context, args *FilesArgs) ([]*types.FileResolver, error) {
user := session.FromContext(ctx).User()
author := ""
if user != nil {
author = user.ID
}
public := args.IncludePublic != nil && *args.IncludePublic == true
mimeTypes := []string(nil)
if args.MimeTypes != nil {
mimeTypes = *args.MimeTypes
}
files, err := file.List(author, public, mimeTypes)
if err != nil {
return nil, err
}
resolvers := make([]*types.FileResolver, len(files))
for i := range files {
resolvers[i] = &types.FileResolver{F: files[i]}
}
return resolvers, nil
}
// FileEditArgs is args for the editFile mutation
type FileEditArgs struct {
Input *struct {
ID string
Name *string
Public *bool
}
}
// EditFile resolves the editFile mutation
func (r *MutationResolver) EditFile(ctx context.Context, args *FileEditArgs) (*types.FileResolver, error) {
input := args.Input
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("member") {
return nil, ErrUnauthorized
}
file, err := file.FindID(input.ID)
if err != nil {
return nil, err
}
if file.Author != user.ID && !user.Permitted("file.edit") {
return nil, ErrUnauthorized
}
err = file.Edit(input.Name, input.Public)
if err != nil {
return nil, err
}
return &types.FileResolver{F: file}, nil
}
// RemoveFile resolves the removeFIle mutation
func (r *MutationResolver) RemoveFile(ctx context.Context, args *FileArgs) (*types.FileResolver, error) {
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("member") {
return nil, ErrUnauthorized
}
file, err := file.FindID(args.ID)
if err != nil {
return nil, err
}
if file.Author != user.ID && !user.Permitted("file.remove") {
return nil, ErrUnauthorized
}
err = file.Delete()
if err != nil {
return nil, err
}
return &types.FileResolver{F: file}, nil
}
-229
View File
@@ -1,229 +0,0 @@
package resolver
import (
"context"
"time"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/internal/session"
"git.aiterp.net/rpdata/api/model/log"
)
// LogArgs is an arg
type LogArgs struct {
ID *string
}
// Log finds log
func (r *QueryResolver) Log(ctx context.Context, args *LogArgs) (*types.LogResolver, error) {
var l log.Log
var err error
switch {
case args.ID != nil:
l, err = log.FindID(*args.ID)
default:
err = ErrCannotResolve
}
if err != nil {
return nil, err
}
return &types.LogResolver{L: l}, nil
}
// LogQueryArgs is args for the logs query
type LogQueryArgs struct {
Filter *struct {
Search *string
Characters *[]string
Channels *[]string
Events *[]string
Open *bool
Limit *int32
}
}
// Logs resolves the logs query
func (r *QueryResolver) Logs(ctx context.Context, args *LogQueryArgs) ([]*types.LogResolver, error) {
var logs []log.Log
var err error
filter := args.Filter
if filter != nil {
limit := 100
search := ""
if filter.Search != nil {
search = *filter.Search
limit = 0
}
channels := []string(nil)
if filter.Channels != nil {
channels = *filter.Channels
limit = 0
}
characters := []string(nil)
if filter.Characters != nil {
characters = *filter.Characters
limit = 0
}
events := []string(nil)
if filter.Events != nil {
events = *filter.Events
limit = 0
}
if filter.Limit != nil {
limit = int(*filter.Limit)
}
open := filter.Open != nil && *filter.Open == true
logs, err = log.ListSearch(search, channels, characters, events, open, limit)
if err != nil {
return nil, err
}
} else {
logs, err = log.List(100)
if err != nil {
return nil, err
}
}
resolvers := make([]*types.LogResolver, len(logs))
for i := range logs {
resolvers[i] = &types.LogResolver{L: logs[i]}
}
return resolvers, nil
}
// LogAddArgs is args for the addLog mutation
type LogAddArgs struct {
Input *struct {
Date string
Channel string
Title *string
Open *bool
Event *string
Description *string
}
}
// AddLog resolves the addLog mutation
func (r *MutationResolver) AddLog(ctx context.Context, args *LogAddArgs) (*types.LogResolver, error) {
input := args.Input
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("log.add") {
return nil, ErrUnauthorized
}
date, err := time.Parse(time.RFC3339Nano, args.Input.Date)
if err != nil {
return nil, err
}
title := ""
if input.Title != nil {
title = *input.Title
}
event := ""
if input.Event != nil {
event = *input.Event
}
description := ""
if input.Description != nil {
description = *input.Description
}
open := input.Open != nil && *input.Open == true
log, err := log.New(date, input.Channel, title, event, description, open)
if err != nil {
return nil, err
}
go change.Submit("Log", "add", user.ID, log.ID, map[string]interface{}{
"channel": log.Channel,
"title": log.Title,
"event": log.Event,
"description": log.Description,
"open": log.Open,
})
return &types.LogResolver{L: log}, nil
}
// LogEditArgs is an input
type LogEditArgs struct {
Input *struct {
ID string
Title *string
Event *string
Description *string
Open *bool
}
}
// EditLog resolves the editLog mutation
func (r *MutationResolver) EditLog(ctx context.Context, args *LogEditArgs) (*types.LogResolver, error) {
input := args.Input
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("log.edit") {
return nil, ErrUnauthorized
}
log, err := log.FindID(input.ID)
if err != nil {
return nil, err
}
err = log.Edit(input.Title, input.Event, input.Description, input.Open)
if err != nil {
return nil, err
}
go change.Submit("Log", "edit", user.ID, log.ID, map[string]interface{}{
"channel": log.Channel,
"title": input.Title,
"event": input.Event,
"description": input.Description,
"open": input.Open,
})
return &types.LogResolver{L: log}, nil
}
// RemoveLog resolves the removeLog mutation
func (r *MutationResolver) RemoveLog(ctx context.Context, args *struct{ ID string }) (*types.LogResolver, error) {
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("log.remove") {
return nil, ErrUnauthorized
}
log, err := log.FindID(args.ID)
if err != nil {
return nil, err
}
err = log.Remove()
if err != nil {
return nil, err
}
go change.Submit("Log", "remove", user.ID, log.ID, nil)
return &types.LogResolver{L: log}, nil
}
+59
View File
@@ -0,0 +1,59 @@
package mutations
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/channel"
)
// ChannelAddArgs is input for the addChannel mutation
type ChannelAddArgs struct {
Input *struct {
Name string
Logged *bool
Hub *bool
EventName *string
LocationName *string
}
}
// AddChannel resolves the addChannel mutation
func (r *MutationResolver) AddChannel(ctx context.Context, args *ChannelAddArgs) (*types.ChannelResolver, error) {
input := args.Input
token := auth.TokenFromContext(ctx)
if !token.Permitted("channel.add") {
return nil, ErrUnauthorized
}
logged := input.Logged != nil && *input.Logged
hub := input.Hub != nil && *input.Hub
eventName := ""
if input.EventName != nil {
eventName = *input.EventName
}
locationName := ""
if input.LocationName != nil {
locationName = *input.LocationName
}
channel, err := channel.New(input.Name, logged, hub, eventName, locationName)
if err != nil {
return nil, err
}
go change.Submit("Channel", "add", token.UserID, channel.Name, map[string]interface{}{
"logged": channel.Logged,
"hub": channel.Hub,
"location": input.LocationName,
"event": input.EventName,
})
return &types.ChannelResolver{C: channel}, nil
}
+67
View File
@@ -0,0 +1,67 @@
package mutations
import (
"context"
"time"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/story"
)
// AddChapterArgs is args for the addChapter mutation
type AddChapterArgs struct {
Input *struct {
StoryID string
Title string
Author *string
Source string
FictionalDate *string
}
}
// AddChapter resolves the addChapter mutation
func (r *MutationResolver) AddChapter(ctx context.Context, args *AddChapterArgs) (*types.ChapterResolver, error) {
input := args.Input
token := auth.TokenFromContext(ctx)
if !token.Permitted("member", "chapter.add") {
return nil, ErrUnauthorized
}
story, err := story.FindID(input.StoryID)
if err != nil {
return nil, err
}
author := token.UserID
if input.Author != nil {
author = *input.Author
if token.UserID != author && !token.Permitted("chapter.add") {
return nil, ErrPermissionDenied
}
}
fictionalDate := time.Time{}
if input.FictionalDate != nil {
fictionalDate, err = time.Parse(time.RFC3339Nano, *input.FictionalDate)
if err != nil {
return nil, err
}
}
chapter, err := story.AddChapter(input.Title, author, input.Source, time.Now(), fictionalDate)
if err != nil {
return nil, err
}
go change.Submit("Chapter", "add", token.UserID, chapter.ID, map[string]interface{}{
"title": input.Title,
"author": author,
"fictionalDate": fictionalDate,
})
return &types.ChapterResolver{C: chapter}, nil
}
@@ -0,0 +1,70 @@
package mutations
import (
"context"
"strings"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/character"
"git.aiterp.net/rpdata/api/model/log"
)
// AddCharacterInput is args for the addCharacter mutation
type AddCharacterInput struct {
Nick string
Name string
ShortName *string
Author *string
Description *string
}
// AddCharacter resolves the addCharacter mutation
func (r *MutationResolver) AddCharacter(ctx context.Context, args struct{ Input *AddCharacterInput }) (*types.CharacterResolver, error) {
input := args.Input
token := auth.TokenFromContext(ctx)
if !token.Permitted("member", "character.add") {
return nil, ErrUnauthorized
}
nick := input.Nick
name := input.Name
shortName := ""
if input.ShortName != nil {
shortName = *input.ShortName
} else {
shortName = strings.SplitN(input.Name, " ", 2)[0]
}
author := token.UserID
if input.Author != nil {
author = *input.Author
if author != token.UserID && !token.Permitted("character.add") {
return nil, ErrPermissionDenied
}
}
description := ""
if input.Description != nil {
description = *input.Description
}
character, err := character.New(nick, name, shortName, author, description)
if err != nil {
return nil, err
}
go change.Submit("Character", "add", token.UserID, character.ID, map[string]interface{}{
"name": character.Name,
"nick": character.Nicks[0],
"author": character.Author,
})
log.ScheduleCharacterUpdate()
return &types.CharacterResolver{C: character}, nil
}
@@ -0,0 +1,48 @@
package mutations
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/character"
"git.aiterp.net/rpdata/api/model/log"
)
// AddCharacterNickInput is args for the addCharacterNick mutation
type AddCharacterNickInput struct {
ID string
Nick string
}
// AddCharacterNick resolves the addCharacterNick mutation
func (r *MutationResolver) AddCharacterNick(ctx context.Context, args struct{ Input *AddCharacterNickInput }) (*types.CharacterResolver, error) {
input := args.Input
token := auth.TokenFromContext(ctx)
if !token.Permitted("member") {
return nil, ErrUnauthorized
}
character, err := character.FindID(input.ID)
if err != nil {
return nil, err
}
if character.Author != token.UserID && !token.Permitted("character.edit") {
return nil, ErrPermissionDenied
}
err = character.AddNick(input.Nick)
if err != nil {
return nil, err
}
go change.Submit("Character", "add.nick", token.UserID, character.ID, map[string]interface{}{
"nick": input.Nick,
})
log.ScheduleCharacterUpdate()
return &types.CharacterResolver{C: character}, nil
}
+70
View File
@@ -0,0 +1,70 @@
package mutations
import (
"context"
"time"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/log"
)
// LogAddArgs is args for the addLog mutation
type LogAddArgs struct {
Input *struct {
Date string
Channel string
Title *string
Open *bool
Event *string
Description *string
}
}
// AddLog resolves the addLog mutation
func (r *MutationResolver) AddLog(ctx context.Context, args *LogAddArgs) (*types.LogResolver, error) {
input := args.Input
token := auth.TokenFromContext(ctx)
if !token.Permitted("log.add") {
return nil, ErrUnauthorized
}
date, err := time.Parse(time.RFC3339Nano, args.Input.Date)
if err != nil {
return nil, err
}
title := ""
if input.Title != nil {
title = *input.Title
}
event := ""
if input.Event != nil {
event = *input.Event
}
description := ""
if input.Description != nil {
description = *input.Description
}
open := input.Open != nil && *input.Open == true
log, err := log.New(date, input.Channel, title, event, description, open)
if err != nil {
return nil, err
}
go change.Submit("Log", "add", token.UserID, log.ID, map[string]interface{}{
"channel": log.Channel,
"title": log.Title,
"event": log.Event,
"description": log.Description,
"open": log.Open,
})
return &types.LogResolver{L: log}, nil
}
+60
View File
@@ -0,0 +1,60 @@
package mutations
import (
"context"
"time"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/log"
)
// PostAddArgs is args for addPost mutation
type PostAddArgs struct {
Input *struct {
LogID string
Time string
Kind string
Nick string
Text string
}
}
// AddPost resolves the addPost mutation
func (r *MutationResolver) AddPost(ctx context.Context, args *PostAddArgs) (*types.PostResolver, error) {
input := args.Input
token := auth.TokenFromContext(ctx)
if !token.Permitted("post.add") {
return nil, ErrUnauthorized
}
postTime, err := time.Parse(time.RFC3339Nano, input.Time)
if err != nil {
return nil, err
}
log, err := log.FindID(input.LogID)
if err != nil {
return nil, err
}
post, err := log.NewPost(postTime, input.Kind, input.Nick, input.Text)
if err != nil {
return nil, err
}
go change.Submit("Post", "add", token.UserID, post.ID, map[string]interface{}{
"logId": post.LogID,
"time": post.Time,
"kind": post.Kind,
"nick": post.Nick,
"text": post.Text,
"position": post.Position,
})
go log.UpdateCharacters()
return &types.PostResolver{P: post}, nil
}
+83
View File
@@ -0,0 +1,83 @@
package mutations
import (
"context"
"time"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/story"
)
// StoryAddArgs is args for the addStory mutation
type StoryAddArgs struct {
Input *struct {
Name string
Category string
Author *string
Open *bool
Listed *bool
FictionalDate *string
Tags *[]struct {
Kind string
Name string
}
}
}
// AddStory resolves the addStory mutation
func (r *MutationResolver) AddStory(ctx context.Context, args *StoryAddArgs) (*types.StoryResolver, error) {
input := args.Input
token := auth.TokenFromContext(ctx)
if !token.Permitted("member", "story.add") {
return nil, ErrUnauthorized
}
author := token.UserID
if input.Author != nil {
author = *input.Author
if token.UserID != author && !token.Permitted("story.add") {
return nil, ErrPermissionDenied
}
}
listed := (input.Listed != nil && *input.Listed == true)
open := (input.Open != nil && *input.Open == true)
tags := make([]story.Tag, 0, 8)
if input.Tags != nil {
for _, tagInput := range *input.Tags {
tags = append(tags, story.Tag{
Kind: tagInput.Kind,
Name: tagInput.Name,
})
}
}
fictionalDate := time.Time{}
if input.FictionalDate != nil {
date, err := time.Parse(time.RFC3339Nano, *input.FictionalDate)
if err != nil {
return nil, err
}
fictionalDate = date
}
story, err := story.New(input.Name, author, input.Category, listed, open, tags, time.Now(), fictionalDate)
if err != nil {
return nil, err
}
go change.Submit("Story", "add", token.UserID, story.ID, map[string]interface{}{
"name": input.Name,
"category": input.Category,
"author": input.Author,
})
return &types.StoryResolver{S: story}, nil
}
+53
View File
@@ -0,0 +1,53 @@
package mutations
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/story"
)
// StoryTagAddArgs is args for the addStoryTag mutation
type StoryTagAddArgs struct {
Input *struct {
ID string
Tag struct {
Kind string
Name string
}
}
}
// AddStoryTag resolves the addStoryTag mutation
func (r *MutationResolver) AddStoryTag(ctx context.Context, args *StoryTagAddArgs) (*types.StoryResolver, error) {
input := args.Input
tag := story.Tag{Kind: input.Tag.Kind, Name: input.Tag.Name}
token := auth.TokenFromContext(ctx)
if !token.Permitted("member", "story.edit") {
return nil, ErrUnauthorized
}
story, err := story.FindID(input.ID)
if err != nil {
return nil, err
}
if story.Author != token.UserID && !token.Permitted("story.edit") {
return nil, ErrPermissionDenied
}
err = story.AddTag(tag)
if err != nil {
return nil, err
}
go change.Submit("Story", "add.tag", token.UserID, story.ID, map[string]interface{}{
"kind": tag.Kind,
"name": tag.Name,
})
return &types.StoryResolver{S: story}, nil
}
+50
View File
@@ -0,0 +1,50 @@
package mutations
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/channel"
)
// ChannelEditArgs is input for the editChannel mutation
type ChannelEditArgs struct {
Input *struct {
Name string
Logged *bool
Hub *bool
EventName *string
LocationName *string
}
}
// EditChannel resolves the editChannel mutation
func (r *MutationResolver) EditChannel(ctx context.Context, args *ChannelEditArgs) (*types.ChannelResolver, error) {
input := args.Input
token := auth.TokenFromContext(ctx)
if !token.Permitted("channel.edit") {
return nil, ErrUnauthorized
}
channel, err := channel.FindName(input.Name)
if err != nil {
return nil, err
}
err = channel.Edit(input.Logged, input.Hub, input.EventName, input.LocationName)
if err != nil {
return nil, err
}
go change.Submit("Channel", "edit", token.UserID, channel.Name, map[string]interface{}{
"logged": input.Logged,
"hub": input.Hub,
"location": input.LocationName,
"event": input.EventName,
})
return &types.ChannelResolver{C: channel}, nil
}
+63
View File
@@ -0,0 +1,63 @@
package mutations
import (
"context"
"time"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/story"
)
// EditChapterArgs is args for the editChapter mutation
type EditChapterArgs struct {
Input *struct {
ID string
Title *string
Source *string
FictionalDate *string
}
}
// EditChapter resolves the editChapter mutation
func (r *MutationResolver) EditChapter(ctx context.Context, args *EditChapterArgs) (*types.ChapterResolver, error) {
input := args.Input
token := auth.TokenFromContext(ctx)
if !token.Permitted("member", "chapter.edit") {
return nil, ErrUnauthorized
}
chapter, err := story.FindChapterID(input.ID)
if err != nil {
return nil, err
}
if chapter.Author != token.UserID && !token.Permitted("chapter.edit") {
return nil, ErrPermissionDenied
}
var fictionalDate *time.Time
if input.FictionalDate != nil {
date, err := time.Parse(time.RFC3339Nano, *input.FictionalDate)
if err != nil {
return nil, err
}
fictionalDate = &date
}
err = chapter.Edit(input.Title, input.Source, fictionalDate)
if err != nil {
return nil, err
}
go change.Submit("Chapter", "edit", token.UserID, chapter.ID, map[string]interface{}{
"title": input.Title,
"source": input.Source,
"fictionalDate": fictionalDate,
})
return &types.ChapterResolver{C: chapter}, nil
}
@@ -0,0 +1,64 @@
package mutations
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/character"
)
// CharacterEditInput is args for mutation addCharacterNick/removeCharacterNick
type CharacterEditInput struct {
ID string
Name *string
ShortName *string
Description *string
}
// EditCharacter resolves the editCharacter mutation
func (r *MutationResolver) EditCharacter(ctx context.Context, args struct{ Input *CharacterEditInput }) (*types.CharacterResolver, error) {
input := args.Input
token := auth.TokenFromContext(ctx)
if !token.Permitted("member") {
return nil, ErrUnauthorized
}
character, err := character.FindID(input.ID)
if err != nil {
return nil, err
}
if character.Author != token.UserID && !token.Permitted("character.edit") {
return nil, ErrPermissionDenied
}
name := ""
if input.Name != nil {
name = *input.Name
}
shortName := ""
if input.ShortName != nil {
shortName = *input.ShortName
}
description := ""
if input.Description != nil {
description = *input.Description
}
err = character.Edit(name, shortName, description)
if err != nil {
return nil, err
}
go change.Submit("Character", "edit", token.UserID, character.ID, map[string]interface{}{
"name": character.Name,
"shortName": character.ShortName,
"description": character.Description,
})
return &types.CharacterResolver{C: character}, nil
}
+43
View File
@@ -0,0 +1,43 @@
package mutations
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/file"
)
// FileEditArgs is args for the editFile mutation
type FileEditArgs struct {
Input *struct {
ID string
Name *string
Public *bool
}
}
// EditFile resolves the editFile mutation
func (r *MutationResolver) EditFile(ctx context.Context, args *FileEditArgs) (*types.FileResolver, error) {
input := args.Input
token := auth.TokenFromContext(ctx)
if !token.Permitted("member") {
return nil, ErrUnauthorized
}
file, err := file.FindID(input.ID)
if err != nil {
return nil, err
}
if file.Author != token.UserID && !token.Permitted("file.edit") {
return nil, ErrUnauthorized
}
err = file.Edit(input.Name, input.Public)
if err != nil {
return nil, err
}
return &types.FileResolver{F: file}, nil
}
+51
View File
@@ -0,0 +1,51 @@
package mutations
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/log"
)
// LogEditArgs is args for the editLog mutation
type LogEditArgs struct {
Input *struct {
ID string
Title *string
Event *string
Description *string
Open *bool
}
}
// EditLog resolves the editLog mutation
func (r *MutationResolver) EditLog(ctx context.Context, args *LogEditArgs) (*types.LogResolver, error) {
input := args.Input
token := auth.TokenFromContext(ctx)
if !token.Permitted("log.edit") {
return nil, ErrUnauthorized
}
log, err := log.FindID(input.ID)
if err != nil {
return nil, err
}
err = log.Edit(input.Title, input.Event, input.Description, input.Open)
if err != nil {
return nil, err
}
go change.Submit("Log", "edit", token.UserID, log.ID, map[string]interface{}{
"channel": log.Channel,
"title": input.Title,
"event": input.Event,
"description": input.Description,
"open": input.Open,
})
return &types.LogResolver{L: log}, nil
}
+63
View File
@@ -0,0 +1,63 @@
package mutations
import (
"context"
"time"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/log"
)
// PostEditArgs is args for the editPost mutation
type PostEditArgs struct {
Input *struct {
ID string
Time *string
Kind *string
Nick *string
Text *string
}
}
// EditPost resolves the editPost mutation
func (r *MutationResolver) EditPost(ctx context.Context, args *PostEditArgs) (*types.PostResolver, error) {
input := args.Input
token := auth.TokenFromContext(ctx)
if !token.Permitted("post.edit") {
return nil, ErrUnauthorized
}
postTime := (*time.Time)(nil)
if args.Input.Time != nil {
t, err := time.Parse(time.RFC3339Nano, *input.Time)
if err != nil {
return nil, err
}
postTime = &t
}
post, err := log.FindPostID(input.ID)
if err != nil {
return nil, err
}
err = post.Edit(postTime, input.Kind, input.Nick, input.Text)
if err != nil {
return nil, err
}
go change.Submit("Post", "edit", token.UserID, post.ID, map[string]interface{}{
"time": postTime,
"kind": input.Kind,
"nick": input.Nick,
"text": input.Text,
})
go log.UpdateCharacters(post.LogID)
return &types.PostResolver{P: post}, nil
}
+69
View File
@@ -0,0 +1,69 @@
package mutations
import (
"context"
"time"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/story"
)
// StoryEditArgs is args for the addStory mutation
type StoryEditArgs struct {
Input *struct {
ID string
Name *string
Category *string
Author *string
Open *bool
Listed *bool
FictionalDate *string
}
}
// EditStory resolves the editStory mutation
func (r *MutationResolver) EditStory(ctx context.Context, args *StoryEditArgs) (*types.StoryResolver, error) {
input := args.Input
token := auth.TokenFromContext(ctx)
if !token.Permitted("member", "story.edit") {
return nil, ErrUnauthorized
}
story, err := story.FindID(input.ID)
if err != nil {
return nil, err
}
if story.Author != token.UserID && !token.Permitted("story.edit") {
return nil, ErrPermissionDenied
}
var fictionalDate *time.Time
if input.FictionalDate != nil {
date, err := time.Parse(time.RFC3339Nano, *input.FictionalDate)
if err != nil {
return nil, err
}
fictionalDate = &date
}
err = story.Edit(input.Name, input.Category, input.Listed, input.Open, fictionalDate)
if err != nil {
return nil, err
}
go change.Submit("Story", "edit", token.UserID, story.ID, map[string]interface{}{
"name": input.Name,
"category": input.Category,
"author": input.Author,
"open": input.Open,
"listed": input.Listed,
"fictionalDate": input.FictionalDate,
})
return &types.StoryResolver{S: story}, nil
}
+45
View File
@@ -0,0 +1,45 @@
package mutations
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/log"
)
// PostMoveArgs is args for movePost mutation
type PostMoveArgs struct {
Input *struct {
ID string
ToPosition int32
}
}
// MovePost resolves the movePost mutation
func (r *MutationResolver) MovePost(ctx context.Context, args *PostMoveArgs) (*types.PostResolver, error) {
input := args.Input
token := auth.TokenFromContext(ctx)
if !token.Permitted("post.move") {
return nil, ErrUnauthorized
}
post, err := log.FindPostID(input.ID)
if err != nil {
return nil, err
}
err = post.Move(int(input.ToPosition))
if err != nil {
return nil, err
}
go change.Submit("Post", "move", token.UserID, post.ID, map[string]interface{}{
"logId": post.LogID,
"targetIndex": input.ToPosition,
})
return &types.PostResolver{P: post}, nil
}
@@ -0,0 +1,37 @@
package mutations
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/channel"
)
// RemoveChannelArgs is args for removeChannels mutation
type RemoveChannelArgs struct {
Name string
}
// RemoveChannel resolves the editChannel mutation
func (r *MutationResolver) RemoveChannel(ctx context.Context, args RemoveChannelArgs) (*types.ChannelResolver, error) {
token := auth.TokenFromContext(ctx)
if !token.Permitted("channel.remove") {
return nil, ErrUnauthorized
}
channel, err := channel.FindName(args.Name)
if err != nil {
return nil, err
}
err = channel.Remove()
if err != nil {
return nil, err
}
go change.Submit("Channel", "remove", token.UserID, channel.Name, nil)
return &types.ChannelResolver{C: channel}, nil
}
@@ -0,0 +1,37 @@
package mutations
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/story"
)
// RemoveChapterArgs is args for the addChapter mutation
type RemoveChapterArgs struct {
ID string
}
// RemoveChapter resolves the removeChapter mutation
func (r *MutationResolver) RemoveChapter(ctx context.Context, args *RemoveChapterArgs) (*types.ChapterResolver, error) {
token := auth.TokenFromContext(ctx)
if !token.Permitted("member", "chapter.edit") {
return nil, ErrUnauthorized
}
chapter, err := story.FindChapterID(args.ID)
if err != nil {
return nil, err
}
err = chapter.Remove()
if err != nil {
return nil, err
}
go change.Submit("Chapter", "remove", token.UserID, chapter.ID, nil)
return &types.ChapterResolver{C: chapter}, nil
}
@@ -0,0 +1,44 @@
package mutations
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/character"
)
// RemoveCharacterArgs is args for the removeCharacter mutation
type RemoveCharacterArgs struct {
ID string
}
// RemoveCharacter resolves the removeCharacter mutation
func (r *MutationResolver) RemoveCharacter(ctx context.Context, args RemoveCharacterArgs) (*types.CharacterResolver, error) {
token := auth.TokenFromContext(ctx)
if !token.Permitted("member") {
return nil, ErrUnauthorized
}
character, err := character.FindID(args.ID)
if err != nil {
return nil, err
}
if character.Author != token.UserID && !token.Permitted("character.remove") {
return nil, ErrPermissionDenied
}
err = character.Remove()
if err != nil {
return nil, err
}
go change.Submit("Character", "remove", token.UserID, character.ID, map[string]interface{}{
"name": character.Name,
"author": character.Author,
"nicks": character.Nicks,
})
return &types.CharacterResolver{C: character}, nil
}
@@ -0,0 +1,48 @@
package mutations
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/character"
"git.aiterp.net/rpdata/api/model/log"
)
// RemoveCharacterNick is args for the addCharacterNick mutation
type RemoveCharacterNick struct {
ID string
Nick string
}
// RemoveCharacterNick resolves the removeCharacterNick mutation
func (r *MutationResolver) RemoveCharacterNick(ctx context.Context, args struct{ Input *RemoveCharacterNick }) (*types.CharacterResolver, error) {
input := args.Input
token := auth.TokenFromContext(ctx)
if !token.Permitted("member") {
return nil, ErrUnauthorized
}
character, err := character.FindID(input.ID)
if err != nil {
return nil, err
}
if character.Author != token.UserID && !token.Permitted("character.edit") {
return nil, ErrPermissionDenied
}
err = character.RemoveNick(input.Nick)
if err != nil {
return nil, err
}
go change.Submit("Character", "remove.nick", token.UserID, character.ID, map[string]interface{}{
"nick": input.Nick,
})
log.ScheduleCharacterUpdate()
return &types.CharacterResolver{C: character}, nil
}
+37
View File
@@ -0,0 +1,37 @@
package mutations
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/file"
)
// RemoveFileArgs is an arg
type RemoveFileArgs struct {
ID string
}
// RemoveFile resolves the removeFIle mutation
func (r *MutationResolver) RemoveFile(ctx context.Context, args *RemoveFileArgs) (*types.FileResolver, error) {
token := auth.TokenFromContext(ctx)
if !token.Permitted("member") {
return nil, ErrUnauthorized
}
file, err := file.FindID(args.ID)
if err != nil {
return nil, err
}
if file.Author != token.UserID && !token.Permitted("file.remove") {
return nil, ErrUnauthorized
}
err = file.Delete()
if err != nil {
return nil, err
}
return &types.FileResolver{F: file}, nil
}
+37
View File
@@ -0,0 +1,37 @@
package mutations
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/log"
)
// RemoveLogArgs is args for the removeLog mutation
type RemoveLogArgs struct {
ID string
}
// RemoveLog resolves the removeLog mutation
func (r *MutationResolver) RemoveLog(ctx context.Context, args *RemoveLogArgs) (*types.LogResolver, error) {
token := auth.TokenFromContext(ctx)
if !token.Permitted("log.remove") {
return nil, ErrUnauthorized
}
log, err := log.FindID(args.ID)
if err != nil {
return nil, err
}
err = log.Remove()
if err != nil {
return nil, err
}
go change.Submit("Log", "remove", token.UserID, log.ID, nil)
return &types.LogResolver{L: log}, nil
}
+36
View File
@@ -0,0 +1,36 @@
package mutations
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/log"
)
// PostRemoveArgs is args for the removePost mutation
type PostRemoveArgs struct {
ID string
}
// RemovePost resolves the removePost mutation
func (r *MutationResolver) RemovePost(ctx context.Context, args PostRemoveArgs) (*types.PostResolver, error) {
token := auth.TokenFromContext(ctx)
if !token.Permitted("post.remove") {
return nil, ErrUnauthorized
}
post, err := log.RemovePost(args.ID)
if err != nil {
return nil, err
}
go change.Submit("Post", "remove", token.UserID, post.ID, map[string]interface{}{
"logId": post.LogID,
})
go log.UpdateCharacters(post.LogID)
return &types.PostResolver{P: post}, nil
}
+41
View File
@@ -0,0 +1,41 @@
package mutations
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/story"
)
// StoryRemoveArgs is args for the removeStory mutation
type StoryRemoveArgs struct {
ID string
}
// RemoveStory resolves the removeStory mutation
func (r *MutationResolver) RemoveStory(ctx context.Context, args *StoryRemoveArgs) (*types.StoryResolver, error) {
token := auth.TokenFromContext(ctx)
if !token.Permitted("member", "story.edit") {
return nil, ErrUnauthorized
}
story, err := story.FindID(args.ID)
if err != nil {
return nil, err
}
if story.Author != token.UserID && !token.Permitted("story.remove") {
return nil, ErrPermissionDenied
}
err = story.Remove()
if err != nil {
return nil, err
}
go change.Submit("Story", "remove", token.UserID, story.ID, nil)
return &types.StoryResolver{S: story}, nil
}
@@ -0,0 +1,53 @@
package mutations
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/story"
)
// StoryTagRemoveArgs is args for the removeStoryTag mutation
type StoryTagRemoveArgs struct {
Input *struct {
ID string
Tag struct {
Kind string
Name string
}
}
}
// RemoveStoryTag resolves the removeStoryTag mutation
func (r *MutationResolver) RemoveStoryTag(ctx context.Context, args *StoryTagRemoveArgs) (*types.StoryResolver, error) {
input := args.Input
tag := story.Tag{Kind: input.Tag.Kind, Name: input.Tag.Name}
token := auth.TokenFromContext(ctx)
if !token.Permitted("member", "story.edit") {
return nil, ErrUnauthorized
}
story, err := story.FindID(input.ID)
if err != nil {
return nil, err
}
if story.Author != token.UserID && !token.Permitted("story.edit") {
return nil, ErrPermissionDenied
}
err = story.RemoveTag(tag)
if err != nil {
return nil, err
}
go change.Submit("Story", "remove.tag", token.UserID, story.ID, map[string]interface{}{
"kind": tag.Kind,
"name": tag.Name,
})
return &types.StoryResolver{S: story}, nil
}
+14
View File
@@ -0,0 +1,14 @@
// Package mutations contains resolvers for individual GrahpQL mutations. They were previously mixed in with one another, but that caused
// difficulty seeking out specific mutations.
package mutations
import "errors"
// MutationResolver is a resolver for all mutations. The stuttery name is due to being embedded in the root resolver.
type MutationResolver struct{}
// ErrUnauthorized is when a guest acts like they own the place
var ErrUnauthorized = errors.New("Unauthorized")
// ErrPermissionDenied is returned when users act above their station
var ErrPermissionDenied = errors.New("Permission denied")
-208
View File
@@ -1,208 +0,0 @@
package resolver
import (
"context"
"time"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/session"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/log"
)
// PostArgs is an arg
type PostArgs struct {
ID string
}
// Post implements the post query
func (r *QueryResolver) Post(ctx context.Context, args *PostArgs) (*types.PostResolver, error) {
post, err := log.FindPostID(args.ID)
if err != nil {
return nil, err
}
return &types.PostResolver{P: post}, nil
}
// PostsArgs is an arg
type PostsArgs struct {
IDs []string
}
// Posts implements the posts query
func (r *QueryResolver) Posts(ctx context.Context, args *PostsArgs) ([]*types.PostResolver, error) {
posts, err := log.ListPostIDs(args.IDs...)
if err != nil {
return nil, err
}
resolvers := make([]*types.PostResolver, len(posts))
for i := range resolvers {
resolvers[i] = &types.PostResolver{P: posts[i]}
}
return resolvers, nil
}
// PostAddArgs is args for addPost mutation
type PostAddArgs struct {
Input *struct {
LogID string
Time string
Kind string
Nick string
Text string
}
}
// AddPost resolves the addPost mutation
func (r *MutationResolver) AddPost(ctx context.Context, args *PostAddArgs) (*types.PostResolver, error) {
input := args.Input
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("post.add") {
return nil, ErrUnauthorized
}
postTime, err := time.Parse(time.RFC3339Nano, input.Time)
if err != nil {
return nil, err
}
log, err := log.FindID(input.LogID)
if err != nil {
return nil, err
}
post, err := log.NewPost(postTime, input.Kind, input.Nick, input.Text)
if err != nil {
return nil, err
}
go change.Submit("Post", "add", user.ID, post.ID, map[string]interface{}{
"logId": post.LogID,
"time": post.Time,
"kind": post.Kind,
"nick": post.Nick,
"text": post.Text,
"position": post.Position,
})
go log.UpdateCharacters()
return &types.PostResolver{P: post}, nil
}
// PostEditArgs is args for the editPost mutation
type PostEditArgs struct {
Input *struct {
ID string
Time *string
Kind *string
Nick *string
Text *string
}
}
// EditPost resolves the editPost mutation
func (r *MutationResolver) EditPost(ctx context.Context, args *PostEditArgs) (*types.PostResolver, error) {
input := args.Input
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("post.edit") {
return nil, ErrUnauthorized
}
postTime := (*time.Time)(nil)
if args.Input.Time != nil {
t, err := time.Parse(time.RFC3339Nano, *input.Time)
if err != nil {
return nil, err
}
postTime = &t
}
post, err := log.FindPostID(input.ID)
if err != nil {
return nil, err
}
err = post.Edit(postTime, input.Kind, input.Nick, input.Text)
if err != nil {
return nil, err
}
go change.Submit("Post", "edit", user.ID, post.ID, map[string]interface{}{
"time": postTime,
"kind": input.Kind,
"nick": input.Nick,
"text": input.Text,
})
go log.UpdateCharacters(post.LogID)
return &types.PostResolver{P: post}, nil
}
// PostMoveArgs is args for movePost mutation
type PostMoveArgs struct {
Input *struct {
ID string
ToPosition int32
}
}
// MovePost resolves the movePost mutation
func (r *MutationResolver) MovePost(ctx context.Context, args *PostMoveArgs) (*types.PostResolver, error) {
input := args.Input
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("post.move") {
return nil, ErrUnauthorized
}
post, err := log.FindPostID(input.ID)
if err != nil {
return nil, err
}
err = post.Move(int(input.ToPosition))
if err != nil {
return nil, err
}
go change.Submit("Post", "move", user.ID, post.ID, map[string]interface{}{
"logId": post.LogID,
"targetIndex": input.ToPosition,
})
return &types.PostResolver{P: post}, nil
}
// PostRemoveArgs is an arg
type PostRemoveArgs struct {
ID string
}
// RemovePost resolves the removePost mutation
func (r *MutationResolver) RemovePost(ctx context.Context, args PostRemoveArgs) (*types.PostResolver, error) {
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("post.remove") {
return nil, ErrUnauthorized
}
post, err := log.RemovePost(args.ID)
if err != nil {
return nil, err
}
go change.Submit("Post", "remove", user.ID, post.ID, map[string]interface{}{
"logId": post.LogID,
})
go log.UpdateCharacters(post.LogID)
return &types.PostResolver{P: post}, nil
}
+23
View File
@@ -0,0 +1,23 @@
package queries
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/model/channel"
)
// ChannelArgs is args for channel query
type ChannelArgs struct {
Name string
}
// Channel resolves the channel query
func (r *QueryResolver) Channel(ctx context.Context, args *ChannelArgs) (*types.ChannelResolver, error) {
channel, err := channel.FindName(args.Name)
if err != nil {
return nil, err
}
return &types.ChannelResolver{C: channel}, nil
}
+43
View File
@@ -0,0 +1,43 @@
package queries
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/model/channel"
)
// ChannelsArgs is args for channel query
type ChannelsArgs struct {
Filter *struct {
Logged *bool
EventName *string
LocationName *string
}
}
// Channels resolves the channels query
func (r *QueryResolver) Channels(ctx context.Context, args *ChannelsArgs) ([]*types.ChannelResolver, error) {
var channels []channel.Channel
var err error
filter := args.Filter
if filter != nil {
channels, err = channel.List(filter.Logged, filter.EventName, filter.LocationName)
if err != nil {
return nil, err
}
} else {
channels, err = channel.List(nil, nil, nil)
if err != nil {
return nil, err
}
}
resolvers := make([]*types.ChannelResolver, len(channels))
for i := range channels {
resolvers[i] = &types.ChannelResolver{C: channels[i]}
}
return resolvers, nil
}
+23
View File
@@ -0,0 +1,23 @@
package queries
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/model/story"
)
// ChapterArgs is args for chapter query
type ChapterArgs struct {
ID string
}
// Chapter resolves the chapter query
func (r *QueryResolver) Chapter(ctx context.Context, args *ChapterArgs) (*types.ChapterResolver, error) {
chapter, err := story.FindChapterID(args.ID)
if err != nil {
return nil, err
}
return &types.ChapterResolver{C: chapter}, nil
}
+43
View File
@@ -0,0 +1,43 @@
package queries
import (
"context"
"errors"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/model/character"
)
// ErrIncorrectArguments is returned by character query.
var ErrIncorrectArguments = errors.New("You can only query character by ID or Nick")
// CharacterArgs is args for character query
type CharacterArgs struct {
ID *string
Nick *string
}
// Character resolves the character query
func (r *QueryResolver) Character(ctx context.Context, args *CharacterArgs) (*types.CharacterResolver, error) {
var char character.Character
var err error
if args.Nick != nil && args.ID != nil {
return nil, ErrIncorrectArguments
}
switch {
case args.ID != nil:
char, err = character.FindID(*args.ID)
case args.Nick != nil:
char, err = character.FindNick(*args.Nick)
default:
err = ErrIncorrectArguments
}
if err != nil {
return nil, err
}
return &types.CharacterResolver{C: char}, nil
}
+60
View File
@@ -0,0 +1,60 @@
package queries
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/model/character"
)
// CharactersArgs is args for characters query
type CharactersArgs struct {
Filter *struct {
IDs *[]string
Nicks *[]string
Names *[]string
Author *string
Search *string
Logged *bool
}
}
// Characters resolves the characters query
func (r *QueryResolver) Characters(ctx context.Context, args *CharactersArgs) ([]*types.CharacterResolver, error) {
var characters []character.Character
var err error
filter := args.Filter
if filter != nil {
var ids []string
var nicks []string
var names []string
if filter.IDs != nil {
ids = *filter.IDs
}
if filter.Nicks != nil {
nicks = *filter.Nicks
}
if filter.Names != nil {
names = *filter.Names
}
characters, err = character.ListFilter(ids, nicks, names, filter.Author, filter.Search, filter.Logged)
if err != nil {
return nil, err
}
} else {
characters, err = character.List()
if err != nil {
return nil, err
}
}
resolvers := make([]*types.CharacterResolver, 0, len(characters))
for _, character := range characters {
resolvers = append(resolvers, &types.CharacterResolver{C: character})
}
return resolvers, nil
}
+23
View File
@@ -0,0 +1,23 @@
package queries
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/model/file"
)
// FileArgs is args for file query
type FileArgs struct {
ID string
}
// File resolves the file query
func (r *QueryResolver) File(ctx context.Context, args *FileArgs) (*types.FileResolver, error) {
file, err := file.FindID(args.ID)
if err != nil {
return nil, err
}
return &types.FileResolver{F: file}, nil
}
+45
View File
@@ -0,0 +1,45 @@
package queries
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/file"
)
// FilesArgs is args for files query
type FilesArgs struct {
Filter *struct {
Public *bool
MimeTypes *[]string
}
}
// Files resolves the file query
func (r *QueryResolver) Files(ctx context.Context, args *FilesArgs) ([]*types.FileResolver, error) {
filter := args.Filter
token := auth.TokenFromContext(ctx)
author := ""
if token != nil {
author = token.UserID
}
mimeTypes := []string(nil)
if filter.MimeTypes != nil {
mimeTypes = *filter.MimeTypes
}
files, err := file.List(author, filter.Public, mimeTypes)
if err != nil {
return nil, err
}
resolvers := make([]*types.FileResolver, len(files))
for i := range files {
resolvers[i] = &types.FileResolver{F: files[i]}
}
return resolvers, nil
}
+23
View File
@@ -0,0 +1,23 @@
package queries
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/model/log"
)
// LogArgs is args for log query
type LogArgs struct {
ID string
}
// Log resolves the log query
func (r *QueryResolver) Log(ctx context.Context, args *LogArgs) (*types.LogResolver, error) {
log, err := log.FindID(args.ID)
if err != nil {
return nil, err
}
return &types.LogResolver{L: log}, nil
}
+49
View File
@@ -0,0 +1,49 @@
package queries
import (
"context"
"errors"
"git.aiterp.net/rpdata/api/graphql/loader"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/model/log"
)
// LogsArgs is args for the logs query
type LogsArgs struct {
Filter *log.Filter
}
// Logs resolves the logs query
func (r *QueryResolver) Logs(ctx context.Context, args *LogsArgs) ([]*types.LogResolver, error) {
var logs []log.Log
filter := args.Filter
if filter == nil {
filter = log.NewFilter().WithLimit(100)
}
logs, err := log.List(filter)
if err != nil {
return nil, err
}
if len(logs) >= 100 {
loader := loader.FromContext(ctx)
if loader == nil {
return nil, errors.New("no loader")
}
for _, log := range logs {
loader.PrimeCharacters("id", log.CharacterIDs...)
loader.PrimeChannels("name", log.Channel)
}
}
resolvers := make([]*types.LogResolver, len(logs))
for i := range logs {
resolvers[i] = &types.LogResolver{L: logs[i]}
}
return resolvers, nil
}
+23
View File
@@ -0,0 +1,23 @@
package queries
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/model/log"
)
// PostArgs is args for the post query
type PostArgs struct {
ID string
}
// Post resolves the post query
func (r *QueryResolver) Post(ctx context.Context, args *PostArgs) (*types.PostResolver, error) {
post, err := log.FindPostID(args.ID)
if err != nil {
return nil, err
}
return &types.PostResolver{P: post}, nil
}
+28
View File
@@ -0,0 +1,28 @@
package queries
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/model/log"
)
// PostsArgs is args for the posts query
type PostsArgs struct {
IDs []string
}
// Posts resolves the posts query
func (r *QueryResolver) Posts(ctx context.Context, args *PostsArgs) ([]*types.PostResolver, error) {
posts, err := log.ListPostIDs(args.IDs...)
if err != nil {
return nil, err
}
resolvers := make([]*types.PostResolver, len(posts))
for i := range resolvers {
resolvers[i] = &types.PostResolver{P: posts[i]}
}
return resolvers, nil
}
@@ -1,13 +1,15 @@
package resolver
// Package queries contains resolvers for individual GrahpQL queries. They were previously mixed in with one another, but that caused
// difficulty seeking out specific queries.
package queries
import "errors"
// QueryResolver is a resolver for all queries. The stuttery name is due to being embedded in the root resolver.
type QueryResolver struct{}
// ErrCannotResolve is returned when a resolver constructor is at its wit's end
var ErrCannotResolve = errors.New("Cannot resolve due to invalid arguments")
// ErrNotImplemented is for TODOs
var ErrNotImplemented = errors.New("Resolver not implemented")
// ErrUnauthorized is when a guest acts like they own the place
var ErrUnauthorized = errors.New("Unauthorized")
+109
View File
@@ -0,0 +1,109 @@
package queries
import (
"context"
"time"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/model/story"
)
// StoriesArg is args for stories query
type StoriesArg struct {
Filter *struct {
Author *string
Tags *[]struct {
Kind string
Name string
}
EarliestFictionalDate *string
LatestFictionalDate *string
Category *string
Limit *int32
Open *bool
Unlisted *bool
}
}
// Stories resolves the stories query
func (r *QueryResolver) Stories(ctx context.Context, args *StoriesArg) ([]*types.StoryResolver, error) {
var err error
token := auth.TokenFromContext(ctx)
filter := args.Filter
author := ""
category := ""
tags := make([]story.Tag, 0, 8)
earliest := time.Time{}
latest := time.Time{}
unlisted := false
open := (*bool)(nil)
limit := 0
if filter != nil {
if filter.Author != nil {
author = *filter.Author
}
if filter.Category != nil {
category = *filter.Category
}
if filter.Tags != nil {
for _, tagInput := range *filter.Tags {
tags = append(tags, story.Tag{
Kind: tagInput.Kind,
Name: tagInput.Name,
})
}
}
if filter.EarliestFictionalDate != nil {
earliest, err = time.Parse(time.RFC3339Nano, *filter.EarliestFictionalDate)
if err != nil {
return nil, err
}
}
if filter.LatestFictionalDate != nil {
latest, err = time.Parse(time.RFC3339Nano, *filter.LatestFictionalDate)
if err != nil {
return nil, err
}
}
if filter.Limit != nil {
limit = int(*filter.Limit)
}
unlisted = filter.Unlisted != nil && *filter.Unlisted == true
if unlisted {
if token == nil {
return nil, ErrUnauthorized
}
if author != "" && author != token.UserID && !token.Permitted("story.unlisted") {
return nil, ErrPermissionDenied
}
author = token.UserID
}
open = filter.Open
}
stories, err := story.List(author, category, tags, earliest, latest, unlisted, open, limit)
if err != nil {
return nil, err
}
resolvers := make([]*types.StoryResolver, len(stories))
for i, story := range stories {
resolvers[i] = &types.StoryResolver{S: story}
}
return resolvers, nil
}
+23
View File
@@ -0,0 +1,23 @@
package queries
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/model/story"
)
// StoryArgs is args for story query
type StoryArgs struct {
ID string
}
// Story resolves the story query
func (r *QueryResolver) Story(ctx context.Context, args *StoryArgs) (*types.StoryResolver, error) {
story, err := story.FindID(args.ID)
if err != nil {
return nil, err
}
return &types.StoryResolver{S: story}, nil
}
@@ -1,17 +1,11 @@
package resolver
package queries
import (
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/model/story"
)
// TagInput resolves the TagInput input
type TagInput struct {
Kind string
Name string
}
// Tags implements the tags query
// Tags resolves the tags query
func (r *QueryResolver) Tags() ([]*types.TagResolver, error) {
tags, err := story.ListTags()
if err != nil {
+13
View File
@@ -0,0 +1,13 @@
package resolver
import (
"git.aiterp.net/rpdata/api/graphql/resolver/mutations"
"git.aiterp.net/rpdata/api/graphql/resolver/queries"
)
// The Resolver combines the query and mutation resolvers from the subpackages.
// This is the one to pass along with the schema.
type Resolver struct {
queries.QueryResolver
mutations.MutationResolver
}
-13
View File
@@ -1,13 +0,0 @@
package resolver
// The RootResolver brings queries and mutations together. The rest is just for readability.
type RootResolver struct {
MutationResolver
QueryResolver
}
// The QueryResolver is the entry point for all top-level read operations.
type QueryResolver struct{}
// The MutationResolver is the entry point for all top-level mutation operations.
type MutationResolver struct{}
-39
View File
@@ -1,39 +0,0 @@
package resolver
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/session"
)
// LoginArgs is args
type LoginArgs struct {
Username string
Password string
}
// Session resolves query.session
func (r *QueryResolver) Session(ctx context.Context) (*types.SessionResolver, error) {
return &types.SessionResolver{S: session.FromContext(ctx)}, nil
}
// Login resolves mutation.login
func (r *MutationResolver) Login(ctx context.Context, args *LoginArgs) (*types.SessionResolver, error) {
session := session.FromContext(ctx)
err := session.Login(args.Username, args.Password)
if err != nil {
return nil, err
}
return &types.SessionResolver{S: session}, nil
}
// Logout resolves mutation.logout
func (r *MutationResolver) Logout(ctx context.Context) (*types.SessionResolver, error) {
session := session.FromContext(ctx)
session.Logout()
return &types.SessionResolver{S: session}, nil
}
-329
View File
@@ -1,329 +0,0 @@
package resolver
import (
"context"
"time"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/session"
"git.aiterp.net/rpdata/api/model/change"
"git.aiterp.net/rpdata/api/model/story"
)
// StoryArgs is args for story query
type StoryArgs struct {
ID string
}
// Story implements the story query
func (r *QueryResolver) Story(ctx context.Context, args *StoryArgs) (*types.StoryResolver, error) {
story, err := story.FindID(args.ID)
if err != nil {
return nil, err
}
return &types.StoryResolver{S: story}, nil
}
// StoriesArg is args for stories query
type StoriesArg struct {
Filter *struct {
Author *string
Tags *[]TagInput
EarliestFictionalDate *string
LatestFictionalDate *string
Limit *int32
}
}
// Stories implements the stories query
func (r *QueryResolver) Stories(ctx context.Context, args *StoriesArg) ([]*types.StoryResolver, error) {
filter := args.Filter
author := ""
if filter != nil && filter.Author != nil {
author = *filter.Author
}
tags := make([]story.Tag, 0, 8)
if filter != nil && filter.Tags != nil {
for _, tagInput := range *filter.Tags {
tags = append(tags, story.Tag{
Kind: tagInput.Kind,
Name: tagInput.Name,
})
}
}
earliest := time.Time{}
err := error(nil)
if filter != nil && filter.EarliestFictionalDate != nil {
earliest, err = time.Parse(time.RFC3339Nano, *filter.EarliestFictionalDate)
if err != nil {
return nil, err
}
}
latest := time.Time{}
if filter != nil && filter.LatestFictionalDate != nil {
latest, err = time.Parse(time.RFC3339Nano, *filter.LatestFictionalDate)
if err != nil {
return nil, err
}
}
limit := 30
if filter != nil && filter.Limit != nil {
limit = int(*filter.Limit)
}
stories, err := story.List(author, tags, earliest, latest, limit)
if err != nil {
return nil, err
}
resolvers := make([]*types.StoryResolver, len(stories))
for i, story := range stories {
resolvers[i] = &types.StoryResolver{S: story}
}
return resolvers, nil
}
// StoryAddArgs is args for the addStory mutation
type StoryAddArgs struct {
Input *struct {
Name string
Category string
Author *string
Open *bool
Listed *bool
Tags *[]TagInput
FictionalDate *string
}
}
// AddStory implements the addStory mutation
func (r *MutationResolver) AddStory(ctx context.Context, args *StoryAddArgs) (*types.StoryResolver, error) {
input := args.Input
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("member", "story.add") {
return nil, ErrUnauthorized
}
author := user.ID
if input.Author != nil {
author = *input.Author
if user.ID != author && !user.Permitted("story.add") {
return nil, ErrPermissionDenied
}
}
listed := (input.Listed != nil && *input.Listed == true)
open := (input.Open != nil && *input.Open == true)
tags := make([]story.Tag, 0, 8)
if input.Tags != nil {
for _, tagInput := range *input.Tags {
tags = append(tags, story.Tag{
Kind: tagInput.Kind,
Name: tagInput.Name,
})
}
}
fictionalDate := time.Time{}
if input.FictionalDate != nil {
date, err := time.Parse(time.RFC3339Nano, *input.FictionalDate)
if err != nil {
return nil, err
}
fictionalDate = date
}
story, err := story.New(input.Name, author, input.Category, listed, open, tags, time.Now(), fictionalDate)
if err != nil {
return nil, err
}
go change.Submit("Story", "add", user.ID, story.ID, map[string]interface{}{
"name": input.Name,
"category": input.Category,
"author": input.Author,
})
return &types.StoryResolver{S: story}, nil
}
// StoryTagAddArgs is args for the addStoryTag mutation
type StoryTagAddArgs struct {
Input *struct {
ID string
Tag TagInput
}
}
// AddStoryTag implements the addStoryTag mutation
func (r *MutationResolver) AddStoryTag(ctx context.Context, args *StoryTagAddArgs) (*types.StoryResolver, error) {
input := args.Input
tag := story.Tag{Kind: input.Tag.Kind, Name: input.Tag.Name}
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("member", "story.edit") {
return nil, ErrUnauthorized
}
story, err := story.FindID(input.ID)
if err != nil {
return nil, err
}
if story.Author != user.ID && !user.Permitted("story.edit") {
return nil, ErrPermissionDenied
}
err = story.AddTag(tag)
if err != nil {
return nil, err
}
go change.Submit("Story", "add.tag", user.ID, story.ID, map[string]interface{}{
"kind": tag.Kind,
"name": tag.Name,
})
return &types.StoryResolver{S: story}, nil
}
// StoryTagRemoveArgs is args for the removeStoryTag mutation
type StoryTagRemoveArgs struct {
Input *struct {
ID string
Tag TagInput
}
}
// RemoveStoryTag implements the removeStoryTag mutation
func (r *MutationResolver) RemoveStoryTag(ctx context.Context, args *StoryTagRemoveArgs) (*types.StoryResolver, error) {
input := args.Input
tag := story.Tag{Kind: input.Tag.Kind, Name: input.Tag.Name}
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("member", "story.edit") {
return nil, ErrUnauthorized
}
story, err := story.FindID(input.ID)
if err != nil {
return nil, err
}
if story.Author != user.ID && !user.Permitted("story.edit") {
return nil, ErrPermissionDenied
}
err = story.RemoveTag(tag)
if err != nil {
return nil, err
}
go change.Submit("Story", "remove.tag", user.ID, story.ID, map[string]interface{}{
"kind": tag.Kind,
"name": tag.Name,
})
return &types.StoryResolver{S: story}, nil
}
// StoryEditArgs is args for the addStory mutation
type StoryEditArgs struct {
Input *struct {
ID string
Name *string
Category *string
Author *string
Open *bool
Listed *bool
FictionalDate *string
}
}
// EditStory implements the editStory mutation
func (r *MutationResolver) EditStory(ctx context.Context, args *StoryEditArgs) (*types.StoryResolver, error) {
input := args.Input
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("member", "story.edit") {
return nil, ErrUnauthorized
}
story, err := story.FindID(input.ID)
if err != nil {
return nil, err
}
if story.Author != user.ID && !user.Permitted("story.edit") {
return nil, ErrPermissionDenied
}
var fictionalDate *time.Time
if input.FictionalDate != nil {
date, err := time.Parse(time.RFC3339Nano, *input.FictionalDate)
if err != nil {
return nil, err
}
fictionalDate = &date
}
err = story.Edit(input.Name, input.Category, input.Listed, input.Open, fictionalDate)
if err != nil {
return nil, err
}
go change.Submit("Story", "edit", user.ID, story.ID, map[string]interface{}{
"name": input.Name,
"category": input.Category,
"author": input.Author,
"open": input.Open,
"listed": input.Listed,
"fictionalDate": input.FictionalDate,
})
return &types.StoryResolver{S: story}, nil
}
// StoryRemoveArgs is args for the removeStory mutation
type StoryRemoveArgs struct {
ID string
}
// RemoveStory implements the removeStory mutation
func (r *MutationResolver) RemoveStory(ctx context.Context, args *StoryRemoveArgs) (*types.StoryResolver, error) {
user := session.FromContext(ctx).User()
if user == nil || !user.Permitted("member", "story.edit") {
return nil, ErrUnauthorized
}
story, err := story.FindID(args.ID)
if err != nil {
return nil, err
}
if story.Author != user.ID && !user.Permitted("story.remove") {
return nil, ErrPermissionDenied
}
err = story.Remove()
if err != nil {
return nil, err
}
go change.Submit("Story", "remove", user.ID, story.ID, nil)
return &types.StoryResolver{S: story}, nil
}
-16
View File
@@ -1,16 +0,0 @@
package types
import "git.aiterp.net/rpdata/api/internal/session"
// SessionResolver resolves Session
type SessionResolver struct{ S *session.Session }
// User resolves Session.user
func (r *SessionResolver) User() *UserResolver {
user := r.S.User()
if user == nil {
return nil
}
return &UserResolver{U: user}
}
-16
View File
@@ -1,16 +0,0 @@
package types
import "git.aiterp.net/rpdata/api/internal/session"
// UserResolver resulves the user type
type UserResolver struct{ U *session.User }
// ID resolves User.id
func (r *UserResolver) ID() string {
return r.U.ID
}
// Permissions resolves User.permissions
func (r *UserResolver) Permissions() []string {
return r.U.Permissions
}
-490
View File
@@ -1,490 +0,0 @@
// Code generated by go-bindata.
// sources:
// root.graphql
// types/change.graphql
// types/channel.graphql
// types/chapter.graphql
// types/character.graphql
// types/file.graphql
// types/log.graphql
// types/post.graphql
// types/session.graphql
// types/story.graphql
// types/tag.graphql
// types/user.graphql
// DO NOT EDIT!
package schema
import (
"bytes"
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
)
func bindataRead(data []byte, name string) ([]byte, error) {
gz, err := gzip.NewReader(bytes.NewBuffer(data))
if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err)
}
var buf bytes.Buffer
_, err = io.Copy(&buf, gz)
clErr := gz.Close()
if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err)
}
if clErr != nil {
return nil, err
}
return buf.Bytes(), nil
}
type asset struct {
bytes []byte
info os.FileInfo
}
type bindataFileInfo struct {
name string
size int64
mode os.FileMode
modTime time.Time
}
func (fi bindataFileInfo) Name() string {
return fi.name
}
func (fi bindataFileInfo) Size() int64 {
return fi.size
}
func (fi bindataFileInfo) Mode() os.FileMode {
return fi.mode
}
func (fi bindataFileInfo) ModTime() time.Time {
return fi.modTime
}
func (fi bindataFileInfo) IsDir() bool {
return false
}
func (fi bindataFileInfo) Sys() interface{} {
return nil
}
var _rootGraphql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x9c\x57\xcd\x6e\xeb\x38\x0f\xdd\xfb\x29\x18\x74\xd1\x14\x08\xf2\x00\xd9\xf5\xeb\xbd\xc5\x17\x20\x9d\xe9\x4c\x32\xab\xa2\xc0\x55\x6d\xc6\x26\xaa\x48\x1e\x51\x6e\x10\x5c\xdc\x77\x1f\x50\xf2\x8f\xec\x3a\x1d\x60\x76\xa6\x4c\x1e\x1e\x52\xf4\x61\x72\x03\x87\x0a\xe1\x8f\x06\xdd\x05\xfc\xa5\x46\x70\x58\x3b\x64\x34\x9e\xc1\x57\x62\xaa\x02\xd0\x78\x77\x81\xda\x92\x9c\x92\xf1\x36\xbc\xba\x7f\xde\xae\xb3\x10\x13\xc3\x7f\x66\x00\x37\xf0\x48\xa6\x80\xbc\x52\x4e\xe5\x1e\x1d\xbc\x5d\x00\xc9\x57\xe8\x40\x19\xd8\x7e\x03\xeb\x40\x81\xa1\xfc\x7d\x9d\xc1\xe0\xb6\xa4\x62\x03\x7b\xef\xc8\x94\xab\xf0\xb6\xb3\xee\x36\xf0\xd0\x39\x65\x33\xf8\x9c\x26\x00\x4d\xec\xc1\x1e\x81\x0a\x8e\x30\x1c\xf2\x19\x50\x8d\xaf\xac\x5b\xc3\xef\x46\x5f\xc0\x1a\x84\x5a\x39\x75\x42\x21\xa8\x3c\x28\xf0\x74\xc2\x94\x0f\x2f\xa9\xe0\x0d\xbc\x44\x12\x8b\xd7\x16\x6d\x74\x12\x31\x13\xa2\x2f\x3d\xd3\xc5\xeb\x22\x03\x18\xf3\x35\x06\xb5\x90\x35\xaa\x4b\x25\x27\x4b\x31\x3b\x8c\x45\xac\x56\xce\x93\x58\xa5\x75\xe7\xcd\x6b\xd8\xd7\x98\xd3\xf1\x42\xa6\x84\x1f\xda\x96\x25\x16\x1b\xf0\xae\xc1\x1f\xe0\x90\xbd\xa3\xbc\xbd\x37\x46\xe5\xf2\x0a\xbc\x05\x2b\x35\x47\xd7\xf5\x90\x98\x97\x5d\xf4\xff\xac\xd5\xa8\x4c\x5b\x80\xbc\x5b\xbc\x2e\x00\xc6\x05\x68\x5b\x0a\xf9\xed\xb7\x0c\xe4\x39\xb9\xaf\xbb\x0d\xec\x6c\x39\x76\xe5\xe8\xc5\xcb\x23\x69\x8f\x2e\x78\xf0\x63\x78\x96\x34\x3b\x5b\xfe\x1f\x55\x11\xfb\x94\x44\xd6\x96\x7d\xcc\x22\x44\xc5\x4a\xf2\x48\x73\x9e\x2d\xfb\x89\x3f\xc7\x00\x5e\xc3\xd6\xdf\x32\x9c\x50\x19\x2f\x55\x2b\xad\xed\x19\x6c\x18\x8c\x5a\x39\xcf\x32\x17\xd2\x98\xbf\xb6\xf2\x5a\x93\x79\x0f\x6e\x90\xeb\x86\x65\x0e\xec\x31\xe2\xad\x00\xd7\xe5\x1a\x8e\x61\x50\x9d\xb5\x27\x28\x90\x73\x47\xb5\x27\x6b\xc2\xb1\xaf\x30\x70\x78\x52\x75\x0d\x5c\xd9\x46\x17\x40\x1e\xf0\x43\xc6\x1d\x73\x7b\x42\x99\xa8\x8a\x4c\xb9\x86\x43\x45\x0c\x85\x45\x06\x63\x3d\x54\xea\x43\xde\xc5\xae\xac\x80\xc9\xe4\x08\x34\xe2\xfd\x86\xf0\x77\x83\x8e\xb0\x00\x32\x81\xb0\x34\xf2\x56\x6e\xb7\xb6\x86\xf1\x96\x81\x51\x63\x1e\xd8\x30\xfa\xae\x53\xd3\x89\x95\x76\xbd\x48\xbf\x26\x3d\x3e\x92\xc6\xfe\x26\xc5\x98\xb4\xf8\x91\x34\x76\xee\x1a\x19\x4a\xf4\x61\x02\xd5\x87\x22\xad\xde\x34\x86\x20\x69\xf7\x11\xc8\xe4\xba\x29\xf0\xb9\x79\xd3\x94\x03\x09\x33\xbf\x92\x56\x9c\x49\x6b\x70\xe8\x1b\x67\xa0\x8e\x6f\x43\x14\x28\x86\x33\x6a\xdd\xa6\xe6\xe5\x08\xa1\x1f\xc5\x15\x9c\xe8\x84\x87\x4b\x8d\x69\x45\x52\x90\x70\x9a\x14\xc4\xde\xba\x4b\x5f\x51\xb0\x26\x25\xed\xe5\x6c\x12\x41\xc8\xad\x37\xe1\x30\xa6\xfb\x68\x0f\x93\x1a\x42\x67\x13\xe6\x95\xaa\x5b\x7d\x0b\x89\x5b\x7b\x92\xfa\x21\x9e\x66\x93\xcf\xb9\x20\xf6\x64\x72\x0f\x5e\x95\x0c\x0d\xc7\xcb\x1e\x78\xc9\xf1\x06\x5e\x0e\xaa\x9c\xe4\xce\x1b\xe7\xd0\x78\x60\x64\x26\x6b\xa4\x84\xf8\xb4\x81\x7d\x7c\x58\x64\xbf\xb2\x2c\x8a\xfa\x53\xe3\x55\x18\x93\xa9\xae\x9f\x1d\x79\xfc\x57\x51\xef\xc3\xa3\xae\xdf\x17\x85\x28\x37\x9e\x07\x95\xcc\x00\x54\x51\x3c\x0c\x22\x6e\xea\xc6\x27\x82\x7d\x5f\x14\x5b\x39\x5a\xa4\x2a\xbe\xc8\x7a\x38\xd1\x54\x99\xf9\x6b\x80\xbf\x51\xfe\xfe\x09\x54\x0e\xe7\x50\x83\x62\xdd\xc0\x9f\x78\xb2\x1f\x18\xa1\x8f\xce\x9e\x46\xe0\x2e\xbc\xfc\x6f\xf8\x01\xfd\x7b\x41\x7e\x84\x88\x05\xf9\xeb\x0d\x10\xf7\xab\x58\x2d\x53\xf5\x15\xc3\xcf\xe3\xd4\x43\x24\x97\xd2\x4a\x7a\xdf\xbd\xb0\x58\x06\x2e\x62\x4e\xae\x22\xa8\x7c\x52\x52\x8a\xd1\x96\x34\x03\x32\x2d\x27\x41\x49\x8b\x69\x71\xfa\x52\xbe\xda\x73\xe3\x42\x64\xba\xb4\x2d\x63\x21\x3b\x59\x33\x31\xff\xce\x96\x69\x01\x3b\x5b\x8e\xc9\xc7\x18\x21\x3e\x0e\x1a\x11\x1e\xa2\x7a\xb2\x31\x2e\x12\xdd\x8d\xb6\xda\x10\x90\xd0\x13\x9d\x8d\xdc\x9e\xc3\x6e\x8a\x79\xee\xa3\xd9\xe7\x09\xa2\x3b\xa2\xd7\xc6\x09\xbf\x34\xf0\x7b\x6b\xcf\x45\x3e\x45\x82\x6d\xa4\xf0\x4b\x23\x9f\x5a\x7b\x2e\xb2\x2f\xae\x8d\x8d\xd5\x3d\xcf\x2f\xd3\xae\xbe\x96\xa8\x88\x72\x4b\xf4\x31\xac\x86\x81\xa8\xd8\x7d\xba\x20\xc3\x93\x74\x6d\x6c\x4c\xf7\x38\xbf\x58\xc6\xed\x0c\x42\x1a\xfb\xb9\x8f\xb2\x1d\xd3\x05\x23\xbd\xef\xa8\xc2\xdd\x17\x9e\x04\x8b\x50\x26\x00\x07\x55\x8e\x30\x0e\xaa\xbc\x0e\xd3\x13\x4f\x91\x22\xfb\x6b\x60\x31\xe4\x0a\x5e\xdb\xc2\xae\x28\xe9\xe1\xe7\xaa\x46\x03\xf9\x15\x9f\x31\x97\xb9\x85\xf6\x49\x02\xc2\x3a\x0a\x3f\x69\x92\xc6\x3e\x74\x6b\xa9\xff\x90\xeb\xcf\xc2\x5c\x4f\x04\xae\x87\x1b\xd4\x60\x06\x64\xaa\x06\xf5\xbc\xb4\xd5\x13\x61\xbb\xbe\x25\xbb\x8a\x76\xb6\x04\x32\xf1\x27\x24\x99\x65\xc3\xe8\x46\xea\xb1\x82\x5a\x31\x9f\xad\x1b\x37\xa5\xdb\x7f\x6d\x3f\x05\xc5\x36\x3e\xc2\xd8\xc6\x2f\xef\xc6\x3b\x92\xf3\x0a\x4f\x0a\x7e\x8a\xb7\xfc\xe2\xba\x6c\xe2\xdf\x18\xf9\xdc\xda\xcd\xb7\xe9\x77\x60\xf6\xeb\x9f\x00\x00\x00\xff\xff\x7a\x45\x45\x35\x28\x0d\x00\x00")
func rootGraphqlBytes() ([]byte, error) {
return bindataRead(
_rootGraphql,
"root.graphql",
)
}
func rootGraphql() (*asset, error) {
bytes, err := rootGraphqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "root.graphql", size: 3368, mode: os.FileMode(420), modTime: time.Unix(1531585622, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _typesChangeGraphql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x34\xcc\x21\xae\x02\x31\x18\x85\x51\xdf\x55\x7c\x2f\xcf\xb3\x00\x1c\x41\xb1\x8c\x9f\xe9\x85\x36\x90\xb6\x99\xde\x11\x13\xc2\xde\x09\x02\x7b\xc4\xf9\xe7\xc4\xb9\x44\xbb\x8b\x3a\x09\x46\xac\xa6\xdf\x70\x11\xa5\x4e\xf7\x75\xc7\x25\xcc\x12\x8d\xab\xd8\xa6\x32\xee\x3c\xa4\xc1\xf2\xac\x6a\x9e\x6c\xe3\x4b\x39\xac\x43\xf2\x3e\xf4\x1b\x5f\x09\x6a\x3e\x72\x69\xfe\x4b\xef\x4f\x00\x00\x00\xff\xff\xac\xbf\xc1\xe6\x6b\x00\x00\x00")
func typesChangeGraphqlBytes() ([]byte, error) {
return bindataRead(
_typesChangeGraphql,
"types/change.graphql",
)
}
func typesChangeGraphql() (*asset, error) {
bytes, err := typesChangeGraphqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "types/change.graphql", size: 107, mode: os.FileMode(420), modTime: time.Unix(1525610729, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _typesChannelGraphql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x90\x31\x4b\xc4\x40\x10\x85\xfb\xfd\x15\x4f\xae\xb0\xf1\x17\x5c\xa7\x87\x45\x1a\x0b\x15\x6c\x6f\x93\x9d\x64\x07\xf6\x66\x42\x76\x56\x10\xf1\xbf\x4b\xf6\x8c\xa7\x16\x72\x78\x95\x70\x55\xf2\xd8\x37\x8f\x8f\x6f\x85\x46\x7a\x9d\x76\xde\x58\x05\xbe\xd5\x62\xf0\x82\xe6\x7e\x83\x2e\x7a\x11\x4a\xce\x5e\x46\xc2\x66\x1f\xf0\xea\x80\x15\x1e\x23\x2d\xcf\x97\x19\xe2\x77\xe4\x50\x3f\x6b\x3c\xd8\xc4\x32\x5c\xb8\xda\x7b\x8a\x64\x91\x26\xd8\xa1\x8f\x1c\xb5\xa4\x80\x96\x90\x74\x18\x28\x38\x7c\xfc\xac\x71\xa3\x9a\xc8\xcb\x2f\xc7\x9c\xe1\x11\x4b\xfb\x09\x87\x39\xfd\xbc\x9c\xf1\xe8\x99\xc4\x2a\xd3\x15\x74\xc2\x56\x4a\x4a\x5b\x70\x0f\x51\xa1\x79\x27\x8f\xd4\x71\xcf\x15\xa0\x96\xef\xbe\xf0\x1f\x76\x92\x76\x7b\x37\x47\x4e\x2d\xfd\x6f\x6b\x6f\xce\xb1\x8c\xc5\x16\x8d\xd7\x21\x34\x35\x1f\xad\x13\x38\x41\xe8\x9f\x7d\xfe\x13\x9d\xb7\x81\xed\xec\xf3\x04\x9f\xef\x01\x00\x00\xff\xff\xed\x4a\x80\xd4\x06\x04\x00\x00")
func typesChannelGraphqlBytes() ([]byte, error) {
return bindataRead(
_typesChannelGraphql,
"types/channel.graphql",
)
}
func typesChannelGraphql() (*asset, error) {
bytes, err := typesChannelGraphqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "types/channel.graphql", size: 1030, mode: os.FileMode(420), modTime: time.Unix(1529172367, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _typesChapterGraphql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x54\xb1\x6e\xdb\x30\x10\xdd\xf9\x15\xaf\xf0\x90\x25\x75\xf7\x6e\x41\x92\xc1\x53\x87\x04\xe8\x7c\x31\xcf\xd2\xa1\xd2\x51\x20\x4f\x36\x84\xa2\xff\x5e\x90\x94\xa3\x54\xb1\xdd\x49\xc2\xe9\xdd\xe3\xdd\x7b\x4f\xdc\xe0\x01\x8f\x2d\x0d\xc6\x11\x92\x60\x2d\xa3\x27\x51\xec\x83\x1a\xab\xe1\x2d\xf8\x09\xe1\x00\x42\xb2\x10\xa7\xad\xb3\x69\xe0\xf7\x8e\xdf\x0e\xd8\xe0\xb5\x65\xec\x6b\xe5\x2e\x61\xf7\xe4\x00\xf1\xdf\xf1\x62\x51\xb4\xf9\xe2\x2e\x60\x4c\xac\x63\x87\xfa\xbc\x89\xa4\xd1\xda\x10\x1d\xe6\x97\x9b\xd8\x14\xc6\xb8\xcf\xb4\xf5\x65\x85\xfd\xd9\xb2\x96\xfd\xe6\x06\x9c\x28\x61\x08\xc9\xd8\x43\x54\x4c\xa8\x9b\x1c\xb0\x8f\x4c\xc6\xfe\x89\xec\xd2\x60\xa2\x5f\x47\x95\x23\xc7\xc4\xf0\x64\x0c\x52\x0f\x93\x9e\xb3\x46\x1f\xc8\x1d\x70\x90\xbd\x49\x50\xea\xae\x50\x95\xf6\x70\x00\x7b\xb1\xad\x43\x79\xae\xcf\xfd\xe3\xdc\x06\x3b\x1d\x46\xc3\x21\x44\x90\xf7\x67\xe5\xfb\xd1\x28\xd3\x3b\x29\x5f\xe7\xf2\x83\xf7\x15\xbd\x18\x53\x6c\x83\x85\xdc\x0c\x7a\x5f\xde\x42\xd6\x29\x7f\xdb\x5d\xb2\xaa\x18\x93\xbb\x1a\x39\xf2\xc7\xc5\xb6\xd8\x19\xf6\xa4\xd0\x60\x78\x63\x74\x7c\x30\x70\x3f\xd8\x74\xc3\xce\xea\x5d\xa6\x4b\x5c\x37\x59\x33\x9e\xa4\xeb\x30\xa6\x7a\x54\x17\x9a\xa6\x98\x92\x2b\x11\xbb\xa7\xfb\x22\x73\xd0\x62\xd0\xa6\x54\x13\x4e\x62\x2d\x68\x18\x62\x18\xa2\x64\x2d\x07\x8e\xbd\xa4\x24\x41\x53\x99\x30\x7b\x0b\x4a\x20\x0d\xd6\x72\x9c\xa7\xd8\xe2\xf9\x58\x83\xa0\xf7\x10\xbb\x4b\x0b\x6f\x1e\x4c\xfa\x21\x44\x13\x6d\xbe\xf5\xe1\x28\xda\x9c\x7f\x84\x2d\x5e\x5b\x39\xc7\x11\x4a\x3d\xd7\x99\x67\x19\xc6\xc4\x1e\xa7\x9c\xb0\x3c\x7c\xee\xcb\x8b\x14\xd6\x34\xbe\xcd\x63\x7d\x4a\xf1\xa2\x50\x4f\xf1\x97\x0f\x27\x9d\xa3\xbb\x68\x54\x4f\xbf\x96\xe9\x1f\x43\x8d\x58\x37\xdd\x83\x52\x92\x46\x41\x4b\xf0\x6a\xc4\x56\x72\x5f\x4b\xe6\x3a\x6b\x39\x8e\xb7\xc3\xf6\xec\xc5\xd6\x69\x5b\xe2\x55\x08\x2e\xdd\x04\x8f\x2d\x69\xf3\x4f\xa6\xae\x5d\x08\x9f\xf0\x57\x7e\xf0\x8a\x7b\x61\x2b\xa0\x0b\xeb\xd3\xff\x97\xff\x1b\x00\x00\xff\xff\x48\xf2\x61\xf4\x08\x05\x00\x00")
func typesChapterGraphqlBytes() ([]byte, error) {
return bindataRead(
_typesChapterGraphql,
"types/chapter.graphql",
)
}
func typesChapterGraphql() (*asset, error) {
bytes, err := typesChapterGraphqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "types/chapter.graphql", size: 1288, mode: os.FileMode(420), modTime: time.Unix(1530984073, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _typesCharacterGraphql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\x54\xbd\x72\xdc\x3c\x0c\xec\xf5\x14\xeb\x71\xf1\x35\xb6\xbf\xde\xdd\xc5\x4e\xe1\xc6\xc9\x38\xee\x32\x29\x60\x11\x3a\x61\x4c\x91\x0a\x01\xda\xa3\x78\xee\xdd\x33\x94\xee\xcf\x3a\x5d\x91\xee\x8e\x5c\x2c\x80\xdd\xa5\x2e\xb1\xc2\x5d\x4b\x89\x6a\xe3\x84\xc4\x7d\x62\xe5\x60\x0a\x0a\x78\xfa\x8e\x7a\x77\x55\xd9\xd0\xf3\x11\xf2\xa3\x02\x4a\x6d\x0e\xf2\x3b\x33\xc4\x71\x30\x69\x84\x13\x9a\x98\x60\x2d\x1f\x95\x02\xe2\x6e\xf1\xc3\x92\x84\xf5\x45\x35\x16\x3e\xb7\x8c\x3e\x49\x47\x69\xc0\xc3\xd3\x1d\x82\xd4\xaf\x78\x61\x1f\xc3\x5a\xc2\x1a\x16\x4f\x28\x0a\x62\x47\x32\x71\xac\xbc\xdf\xd7\x2a\x48\x35\xd6\x42\xc6\x0e\xef\x62\x2d\xac\x15\x3d\x21\xd0\x5b\xfc\xdc\xce\xf1\xeb\x68\x92\x3d\xec\x3f\x05\x65\x6b\x63\xc1\x4f\x3f\x16\xe6\x3e\x46\x07\xea\xb8\x70\x53\xc7\x0b\xc8\x72\x5c\x76\x71\xa2\xbd\xa7\x01\xef\x2d\x07\x68\x4f\x35\x43\x14\x5a\x53\xaa\xf9\x0a\x59\x33\x79\x3f\x8c\x1b\x37\x92\xd4\xfe\x5f\xcb\x1b\x87\x1d\xb7\xb6\x31\xd9\xe3\x69\x83\xd5\x74\x03\xc7\x5a\x27\xe9\x4d\x62\x40\x6c\x4e\x74\x3b\xba\x3e\x10\x6c\xaa\xea\x12\x0f\xa1\xcf\x36\xda\x45\xce\x15\xd5\xf7\x65\x5a\xc9\x78\xb7\xb7\x7b\xe5\xdc\x84\xfe\x38\xef\xde\x6e\xd9\xc4\x75\x5c\x07\xf9\xc3\x33\x0b\xf0\x32\xcc\x6c\xfc\x67\x4d\xbf\x95\x35\x28\xf8\x69\x73\x0e\xec\x46\xd4\x0d\xbe\x0c\x70\xdc\x50\xf6\x76\x05\x31\x64\x65\x3d\xc8\x09\x8b\xaf\x1c\x20\x61\x3c\x3a\xa7\xea\xd4\xe1\xfe\x48\xcc\x51\x99\xc3\x64\x37\xcb\x62\x7e\x9e\xcc\x0f\x57\xd0\x9e\x6b\x69\x06\x50\x88\xd6\x72\xda\x06\xe9\x06\xcf\x45\x8e\xc0\xec\x74\x82\x90\x47\xcf\xa9\x13\x55\x89\x41\x21\x0d\xc4\xca\xfa\xd1\x46\xca\x21\xe6\x84\xf8\x1e\xca\x36\x69\x3b\xf5\xe7\x4c\x2e\xd8\xf8\x58\x9c\xa0\xe0\x90\xb8\x8b\x6f\x3c\xfe\xed\xb2\x51\x99\x78\x6e\x6a\xb9\x9c\xbb\xfa\x70\xbf\x94\xa1\xc5\xe7\x3b\x9a\x6e\xb1\x74\x45\x4c\xdb\x86\x27\x0e\x6f\xaa\x79\xdb\xaf\x4e\x6c\xde\x56\xdc\xe9\x67\xa3\x70\xb3\x13\x3b\xd7\xbf\xc9\xde\x4f\xa1\x9b\x4f\x8c\xeb\xeb\xa2\xe2\x78\xa8\xe4\x29\x09\x85\x03\xfc\x62\x96\xad\xe5\x10\x4e\x6f\x6b\xca\x74\x4b\x56\xde\x6b\x56\x76\x25\x45\x75\xec\x7a\xaa\x0d\x5e\xd4\xf4\x7c\x94\x96\xde\xe7\xd2\xb7\x71\x29\x53\x9b\xbf\x01\x00\x00\xff\xff\x54\xc3\xda\x87\x97\x05\x00\x00")
func typesCharacterGraphqlBytes() ([]byte, error) {
return bindataRead(
_typesCharacterGraphql,
"types/character.graphql",
)
}
func typesCharacterGraphql() (*asset, error) {
bytes, err := typesCharacterGraphqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "types/character.graphql", size: 1431, mode: os.FileMode(420), modTime: time.Unix(1526204279, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _typesFileGraphql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\x52\xb1\x8e\x13\x31\x10\xed\xfd\x15\x8f\x4b\x41\x73\xca\x07\xa4\x03\x71\x48\x91\x48\xc3\x1d\xa2\xf6\x9e\x67\xe3\x51\x6c\xcf\x62\x8f\xb3\x0a\xa7\xfb\x77\x64\xef\x1e\x8a\x50\x0a\xe8\xa8\x56\x3b\x7e\xef\xcd\x9b\x37\xb3\xc1\x07\x7c\xe6\x40\x78\x96\xa4\x96\x53\x01\xa7\x51\x72\xb4\xca\x92\x60\x07\xa9\x0a\x8b\xb1\x21\x6c\x72\x98\x3d\x65\x82\x0a\x9c\xcc\x29\x88\x75\x60\xdd\x1a\xbd\x4c\xb4\xa8\xbc\x18\x60\x83\x27\x4f\x9d\xf2\xbe\xa0\x26\xfe\x51\x09\xfb\x4f\x06\x60\xb7\xc3\xa3\x66\x4e\xc7\x77\xe6\x37\xee\xc4\xc9\x41\xc6\x8e\xdf\xe2\x20\x45\x31\x73\x08\x18\x08\x77\x75\x6a\x2d\xee\xee\x31\x54\x45\x91\x48\x50\x6f\x15\x36\x13\x26\xc9\x4a\x0e\x63\x96\x08\xf5\x84\x99\x4f\xbc\x10\xbd\x3d\x13\x44\x3d\x65\x9c\x6d\xa8\x54\x30\x4a\x86\x7a\x2e\x5b\x83\xde\xee\x86\x0b\xe5\x48\xcd\xc5\xd2\x91\xd3\xd1\xa0\xd7\xfe\x80\x7e\xf7\xd4\x85\x75\x1d\x10\x5c\x30\xd5\x21\xf0\x73\xb8\x20\x70\x51\x3b\xb4\x29\x1e\x25\x92\xa4\x66\x4a\x3d\x4e\x49\xe6\x40\xee\xd8\xf5\x1b\xb1\x67\xb1\x59\xcc\x16\x5d\x67\x6d\xc4\x96\xeb\x99\x69\x06\xeb\x3d\xbc\xcc\x74\xa6\xdc\x3c\x2f\x1d\x76\xf8\x28\x12\xc8\xa6\x2b\xdb\x6b\xc8\xc9\x46\x32\xe8\x9f\x1b\xb3\x1d\xf6\x87\x07\xf4\x15\xad\x06\x1a\xcb\x00\x91\x23\x3d\x5d\xa6\x5b\x94\x55\xb7\xf0\x4f\x02\x27\x0c\x17\xa5\x62\xd0\xff\x77\xd8\x27\xbd\x82\x2e\x89\x51\x36\x80\xad\xea\x25\xdf\x90\xfb\xf6\xf5\xcb\xdb\xe5\x5c\x05\xe7\xa5\x28\x39\x03\xd4\x1c\xde\x48\xe6\xd5\x98\x0d\xf6\x69\xaa\xda\xd7\x46\x8e\xb5\x1f\x56\xac\xda\x4f\xd2\x70\x7f\x7b\x58\xeb\x0b\xf2\xc5\xfc\xc3\xd1\xfd\x17\x3b\xfc\x9b\x15\x9a\xd7\x5f\x01\x00\x00\xff\xff\xe4\xc2\x5b\xa8\x9d\x03\x00\x00")
func typesFileGraphqlBytes() ([]byte, error) {
return bindataRead(
_typesFileGraphql,
"types/file.graphql",
)
}
func typesFileGraphql() (*asset, error) {
bytes, err := typesFileGraphqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "types/file.graphql", size: 925, mode: os.FileMode(420), modTime: time.Unix(1528836115, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _typesLogGraphql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x56\xc1\x6e\xdc\x46\x0c\xbd\xeb\x2b\x98\xf8\xe0\x04\x48\xd3\x83\x4f\xdd\x9b\x6b\x24\xa8\x81\xa0\x0d\x92\x00\x3d\x18\x39\xcc\x6a\xb8\x2b\x22\x23\x52\x99\xa1\x76\xb3\x08\xf2\xef\x05\x29\xc9\x5e\x29\x5b\x64\xd1\x43\x91\x83\x4f\x9e\x1d\x93\x8f\xe4\x23\xf9\x46\x17\x70\x0d\x6f\x64\x0b\x54\x40\x1b\x84\xa7\x1b\x4a\xf8\x14\x64\x03\x81\xe1\xdd\x5b\x28\x58\x0a\x09\x57\x7a\xe8\xd0\xed\xbe\x56\x00\xe6\xd3\x33\x7d\xee\x11\x28\x22\x2b\x6d\x08\x33\x6c\x24\x3b\x44\x92\xed\xcb\x0a\x80\xe2\x0a\xde\x6b\x26\xde\x3e\xa9\x46\x9f\x82\xb5\x70\x0c\xf9\xf0\x03\x6f\xf8\xd0\x50\xb1\x8c\x02\x24\x51\x28\x8d\x64\xc5\x0c\x81\x23\xb4\x92\x11\x4a\x4f\x1a\xd6\x09\xdd\xa9\xa8\xe4\xb0\x45\xd8\x37\xc8\x1e\xc7\x51\x88\x3f\x41\x14\x2c\x7c\xa9\xc0\x88\x11\x54\x60\x8d\x10\x0a\xe0\x97\x2e\x5b\x51\x3b\xb4\x2c\x1d\xfb\x76\x99\xea\x87\x06\x21\x06\x1d\x02\x84\xa9\x22\xbb\x39\x61\x58\x37\x81\x19\x93\x73\x36\x99\x8e\x77\x7f\x86\x76\xe9\x71\xcb\x1b\xc9\x6d\x50\x12\x86\xb0\x96\x5e\x3d\xdf\x09\x43\xad\xf0\x34\xb4\x83\xf8\x08\x69\x05\x37\xc3\xe1\x28\xf0\xd8\x9b\xcb\x02\x4a\x9a\xbc\x1c\x3f\x9c\xc8\x31\xc9\xf6\xb2\x00\xee\x90\xf5\x05\xec\x1b\xaa\x9b\xa9\xe1\x25\xb4\x4e\x8b\x9d\x35\x6c\x2d\xac\x9f\xbb\x8c\x3b\x92\xde\xb3\x59\x5b\x13\x48\x3d\xc2\x05\xbc\x6a\x3b\x3d\x40\xf1\x10\xd0\x62\x60\x73\x0e\x0a\xa4\x97\x05\x58\x86\x28\x66\xea\x87\x53\xcc\x62\xa9\x33\x75\x4e\x81\x93\x36\x16\x72\x94\x19\x7a\x0c\xda\x40\xcf\x05\x1d\xec\xc8\x69\x01\xf9\x77\x83\xda\xe0\xfd\xf4\x4c\x68\x06\x23\x1d\x3a\x89\xf6\x77\x05\xbf\x8b\x24\x0c\x3c\x6f\x5d\x0e\xb5\x62\xb6\xb2\x77\x92\x76\x18\xa7\xfa\x0d\xc9\x76\x61\xec\xc1\x68\xb6\x82\xbb\x9b\xe9\xc7\x93\x8f\x47\x48\x9d\x14\x2d\x56\xcd\xe8\x6b\xae\x53\x3d\x75\x60\x9b\xbd\x0d\x25\xc5\x8c\x11\xd6\x07\xf8\x44\x1c\x8b\x41\xbb\xdf\x33\xff\xb9\xba\x1b\xcb\xfa\xf8\x7c\x05\x77\x6f\xa5\xa8\x45\xf8\x56\x55\x17\xf0\xda\x5d\x7d\x1a\x93\x6c\x0b\x7c\xee\x31\x1f\x2a\xe2\xae\x57\x5b\xca\x32\xfe\x7f\xd8\xcd\x71\x50\x8a\x8d\x7c\xa2\x96\x14\x32\x96\x3e\xa9\x5f\x3c\x23\xae\x53\x6f\xd3\xff\xfc\x61\xb8\xac\xac\x29\x76\x05\x30\x74\xd9\x9a\x77\x0e\x86\x77\x79\x86\x30\x65\x31\x51\x7b\x12\x03\xbf\xcc\xf3\x78\x20\x78\x8e\xf3\x1e\x43\xae\x1b\xe7\x09\x6a\x61\x45\x56\x5b\x5b\xbf\x9d\x06\x61\xb0\x7c\xe3\x31\x54\x40\x38\x1d\xbc\xe5\x4e\xd6\xb2\xfd\x33\xe3\x06\x21\xb4\xd2\xb3\x5a\xeb\xbc\x17\x15\x0c\xc9\xae\xe0\x96\x75\x60\xff\xd6\x79\x76\x29\x88\xd1\x34\xb0\xed\xd5\x17\xf8\xa1\x03\xd7\x31\x0e\x56\x5f\xe7\xfa\xf1\x30\x10\x2f\x6c\xb0\xde\xbd\xbe\xb9\xba\xba\xfa\x0d\x06\x09\x80\x3d\x69\x03\x7d\x67\x39\x73\x60\x19\xe4\xd1\x36\xaf\x26\x17\xdd\x1f\x4a\xce\xbe\xc1\x8c\xf7\xe3\x4a\x05\x0a\xea\xb1\x66\xcc\x1c\xff\xf2\xe5\x09\x69\x35\x29\xc2\xa0\x16\x4b\xd5\x58\x1a\x2f\xd7\x6b\x5c\xab\xd3\xb4\x7e\x1f\xc3\xc7\x03\x38\xb4\xb8\x54\x84\xa5\xc7\xf5\x20\xc6\x4b\x79\x18\xe3\x9e\x96\x80\xb3\x1b\xf4\x2a\x92\x2e\x3b\x44\x71\x8e\xff\xdd\x8b\x75\x06\x4f\xe7\x96\xf9\x9f\x8a\x1b\x09\x42\xfe\xb5\x4e\x52\xf0\xc8\x7a\x4e\xbd\x93\xe0\xcf\xf8\x1f\x18\x22\xe6\xe1\xe9\xac\x7b\xfd\x25\xca\x9e\x61\x87\xb9\x8c\xf1\x8c\x1c\x17\x6b\x7b\x1b\x81\x45\x21\xa4\x24\x7b\xd8\xa2\xaa\xc9\xb9\x2f\x44\xad\x7d\x48\xc3\x36\xbc\xbc\x7f\xf8\x47\xe4\xc7\xe7\xff\xdf\x76\xd1\x7a\xff\xbf\x7d\x03\x8c\x22\xfd\xf8\x15\xf0\x53\x7c\x05\x7c\xab\xfe\x09\x00\x00\xff\xff\xd3\x31\x19\xf7\x46\x0b\x00\x00")
func typesLogGraphqlBytes() ([]byte, error) {
return bindataRead(
_typesLogGraphql,
"types/log.graphql",
)
}
func typesLogGraphql() (*asset, error) {
bytes, err := typesLogGraphqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "types/log.graphql", size: 2886, mode: os.FileMode(420), modTime: time.Unix(1531585338, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _typesPostGraphql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xd4\x54\xc1\x6a\xdc\x40\x0c\xbd\xfb\x2b\x5e\x36\x87\xbd\x6c\x73\xc9\xa9\xb9\x85\xa6\x05\x43\x4b\x97\x34\x3f\x30\x99\xd1\x7a\x86\xb5\x25\x33\x92\x93\x2c\xa5\xff\x5e\x66\xbc\x21\x10\xbc\x87\x96\x04\xda\x93\x41\x7a\xa3\xf7\xa4\xf7\xf0\x39\xae\xb1\x15\x35\x24\x85\xc3\xe8\xb2\x41\x76\x70\xe8\xa5\x6b\xec\x30\xd2\xdc\xfc\xd9\x00\xe7\xb8\x8b\x84\x51\xd4\xd6\x8a\xf6\xa6\x01\x52\xb8\xc2\x0f\xcb\x89\xbb\xb3\xe6\x35\xe0\xab\x74\x68\x6f\x2e\x70\x17\x93\x96\xd9\x16\x09\xbe\x17\x25\x35\x58\x4c\xdc\xc1\xa4\xd0\x24\xde\xe3\xde\xf9\x3d\x34\xb1\xa7\xd2\x52\x5c\x6f\x5b\x74\xd9\x8d\x11\x41\x48\x79\x6d\x88\xee\x81\xe0\xf8\x00\x7f\xf0\x3d\xe9\x45\x83\x22\xb0\x5d\xe2\x0f\xce\x0a\x34\xc0\xd2\x40\x65\x97\x22\x28\x71\xd7\xa0\x56\x16\x5e\xec\x13\x87\x67\xe0\x2c\x20\xe9\x05\xbe\x73\x7f\xc0\xca\xe8\xc9\x56\x1b\xac\xd4\x13\xd3\xaa\xce\x5d\x39\x6f\x49\x78\x05\x97\x09\xb7\xdb\x0d\x1e\x63\xea\x09\x62\x91\xb2\xd6\xa2\x63\x16\x73\x05\xa4\xf5\xc5\xda\xcb\x30\x38\x0e\xba\x2e\xc2\x0b\xdd\x82\x0a\x1f\x5d\x76\xde\x28\x83\x93\xdf\x37\xa8\x9f\xd3\xf7\x2d\xc2\x2a\xb5\x8f\x18\xa7\x3c\x8a\x12\x02\x8d\xc4\x41\x21\x5c\xcf\x5d\x88\xca\xd6\xf4\x64\xa7\xe7\x8c\xa2\xa9\x28\xdd\x60\x52\x0a\xd8\x49\x46\x26\xc9\x81\xf2\x7c\xb3\xe7\xfe\x15\x5a\xb6\xb3\xe6\x57\xd3\x9c\xa3\xe5\x71\xb2\x0a\x2d\x34\x2e\x84\x9a\x91\x61\x9a\x77\x6e\x52\x6d\x5f\xcf\xe5\x19\xfb\x92\x9f\x5e\xba\x1a\x1f\x58\x74\xc7\x6b\xd7\xbb\x6b\x94\xa9\x0f\xb8\xa7\x97\x14\xfe\x85\xc9\x1b\x24\x86\xc3\xed\x97\x4f\x97\x97\x97\x1f\x8b\xc4\xc1\x19\x1e\x93\x45\x4c\xe3\x9c\x38\x76\x2c\x4a\x5e\x38\x94\xed\x33\xf9\xa4\x45\xf4\x7f\x95\x8e\x0d\x24\xe3\xf8\x0c\x89\x1f\x64\x4f\xb9\xfa\xc1\xc2\x1f\x6e\xb7\x50\x9b\x76\xbb\x77\x8b\xd0\x42\x06\x28\x24\x5b\x0a\xc1\xe7\x63\xfd\x75\x0a\x2a\xf6\xf4\x3f\xe4\xbd\xed\xfd\x27\xdc\x7d\x4b\x73\xdf\xc8\xdb\x25\x6b\x07\x79\xa0\x25\x6b\xbf\x1d\xeb\x7f\x62\xad\xcb\x1d\x19\x12\x07\x7a\x2a\xcc\xb2\x7d\xf5\x73\xf9\x1d\x00\x00\xff\xff\x60\x35\xc5\x5a\x8d\x06\x00\x00")
func typesPostGraphqlBytes() ([]byte, error) {
return bindataRead(
_typesPostGraphql,
"types/post.graphql",
)
}
func typesPostGraphql() (*asset, error) {
bytes, err := typesPostGraphqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "types/post.graphql", size: 1677, mode: os.FileMode(420), modTime: time.Unix(1530961143, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _typesSessionGraphql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x44\xcc\x4b\x0a\x02\x31\x10\x06\xe1\x7d\x4e\x51\xe0\xd6\x13\x78\x0d\xf5\x00\x83\xfe\x4e\x02\xa1\x33\x74\x77\x16\x22\xde\x5d\x7c\x80\xdb\xe2\xa3\x76\x9c\xaa\x08\x45\xb4\x61\xb8\x36\x57\xc8\x32\xc8\x2a\x2e\xd3\x5d\x96\xf4\xb1\x36\x23\x72\x49\x95\xbc\x6f\xe2\xf8\xf3\x8f\x02\xdf\xc3\x0c\x39\x59\x97\xa4\xc5\xdb\xaf\xba\xd2\x6c\x8f\xcd\xde\x69\x37\x6c\xe4\x3f\x17\x3e\xfe\xc0\x39\xe4\xe5\xf9\x0a\x00\x00\xff\xff\x3a\x24\x24\xaf\x84\x00\x00\x00")
func typesSessionGraphqlBytes() ([]byte, error) {
return bindataRead(
_typesSessionGraphql,
"types/session.graphql",
)
}
func typesSessionGraphql() (*asset, error) {
bytes, err := typesSessionGraphqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "types/session.graphql", size: 132, mode: os.FileMode(420), modTime: time.Unix(1525515204, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _typesStoryGraphql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xd4\x57\x4f\x6f\xe3\xb6\x13\xbd\xeb\x53\x4c\x90\xc3\xee\x02\x81\x7f\x3f\x20\xa7\xfa\x96\x4d\x9a\xc2\x97\x62\x91\x04\xe8\x61\xb1\x87\xb1\x38\x96\xd8\x50\xa4\x96\x1c\xc6\x35\x82\xfd\xee\xc5\x90\x94\x64\x3b\xb2\xd3\x43\x0b\xb4\x17\xc3\x12\x87\xf3\xf7\xbd\x47\xea\x12\x6e\xe0\x91\x9d\xdf\x81\x0e\x80\xd0\x6b\xaa\x09\xdc\x06\x6a\x67\x99\x2c\x03\xb7\xc8\x57\x10\xad\xd1\xcf\x04\x5b\xfd\xac\x01\x3d\xeb\xda\x50\xb8\x02\xf4\x04\xd6\x31\x04\xd7\x11\xb7\xda\x36\xc9\x1a\x42\xeb\xa2\x51\xb0\x26\x70\x5e\x91\x27\x05\xeb\x1d\xb0\xee\x08\xb4\x0d\x4c\xa8\xc4\x3f\x6b\x36\xb4\x80\x15\x43\x8d\xb6\xba\x4c\xf1\x50\x5b\xe8\xa2\x61\xdd\x1b\x82\xba\xc5\x9e\xc9\x07\xd9\xec\xac\xf8\x82\xce\x79\x02\x8c\xec\x5a\x1f\x2a\xde\xf5\x54\x32\x7f\xad\x00\x2e\xe1\xa9\x25\x08\xf2\xfc\x21\xc0\xea\xae\x02\xd0\x6a\x09\x8f\xec\xb5\x6d\x2e\xaa\x37\x16\x16\x3b\xba\x82\x6d\xab\xeb\x16\xb6\xda\x18\xc9\x7a\x0b\xb1\x07\x6d\x81\x5b\x02\xa3\x03\x87\x45\x05\xc9\xf0\x8c\x1f\x8c\xdc\x3a\x5f\x41\xf9\x73\x64\xf9\x5b\x4b\xdc\x92\x07\x97\x7e\x63\x90\x7a\x3a\xdc\x01\x2a\xf5\x3f\x52\x9a\xa7\x2a\xd9\xa5\xb0\xc9\xad\x84\x75\x3d\xd9\x25\x7c\x76\xce\x10\xda\x23\x6f\xd9\xcf\x56\x73\xeb\x22\x03\x82\xd2\x9e\x6a\x06\xa3\xed\xb3\x74\x13\x36\xda\x2a\xe0\x56\x87\xc9\x9d\x94\x43\xea\xd8\xa1\x14\x52\x23\x53\x23\x5d\x94\xa1\x0c\x19\x54\x30\xbe\x5f\xe6\x26\xdf\x96\xc7\xbd\x9d\x8c\x4d\x80\x8d\xf3\x39\x54\xd9\x26\x2f\x97\xf0\xf5\x09\x9b\x8b\x6f\xfb\x51\x86\x3a\x53\x94\x21\x33\x09\x53\x16\x96\xf0\xf5\x36\xff\x3d\xd8\xa7\x90\x69\x4a\x0b\xb6\x18\xa0\xf6\x84\x4c\x0a\x3e\x3e\xdc\xdf\x5e\x5f\x5f\xff\x24\x29\x74\xc8\x9f\xa4\xcc\xb2\x76\x87\x3c\x37\xb3\x23\x67\x92\x05\xb1\x0c\x7c\xce\xd5\x46\xd7\xac\x9d\x45\x73\xce\x59\xe9\x99\xc1\xc0\xd0\xe1\xef\xce\x43\xec\x73\x94\xbd\x71\xbe\x75\x0f\x4f\xd2\x82\x35\x09\x65\xd6\xb1\xeb\x49\x41\x47\x68\x85\x80\x96\xb6\x43\x4f\xa0\x45\x31\x22\x2b\x70\x21\x25\x49\x65\xef\x47\xf5\xfd\xa8\xaa\x4b\xb8\xd7\x46\xb6\xc8\x38\x24\xa8\xa6\x00\xdf\x23\xc9\xf0\xb5\xed\x23\xa7\x21\x6a\x0a\xc5\xec\xb5\xe0\x09\xb9\xe0\x56\x12\x4e\xf6\xe2\xe1\x0d\x9a\xab\xc9\x3c\x0d\xfd\xc8\x78\x9a\xf9\x4a\x62\x5d\x7c\x9b\xba\x44\xe8\x8d\xa6\xc0\x87\xed\xac\x60\x5c\xb8\x9f\x6b\xf3\xb4\xdf\x20\xcf\xed\xce\xaf\xdf\xd9\xdb\xe1\x1f\x80\x9d\x8b\x96\x65\x4e\x43\x57\xd8\x41\x43\x0c\x1f\x15\x6d\x30\x1a\x5e\xc2\xf5\xff\x3f\x25\x82\x74\x9a\x97\xb0\xb2\x9c\xfb\x99\x2a\x29\xe8\x26\x19\x40\x96\x9a\x2e\x32\x4a\xcc\xbd\xae\xee\x6e\x94\xca\xd6\xaf\x90\x62\x3f\x12\xa7\x4d\xa2\x1c\x07\xac\x9a\xd5\x1b\x0c\xc9\x20\xe9\xe1\x15\xa0\x55\xf2\x06\x21\xc4\xa6\xa1\x20\xa1\x4a\x93\x2f\x93\xd9\x46\xfb\x30\xaa\x46\x41\xd0\x88\x8e\x19\xa9\x1a\x72\x19\x59\x3e\x54\x24\x38\x1b\xd5\xe1\x3c\xd7\x6f\x94\xda\x03\x73\xb4\x8a\x3c\xa0\xcd\x92\x26\x21\x0b\x9c\x3d\x7d\x8f\xda\x53\xd8\xd3\x31\x54\x0a\x7a\xf2\x9d\x0e\x41\x3b\x9b\xab\x2b\xa7\x83\x9c\x1b\x6b\x4a\xfe\x71\x1d\x03\xa9\x45\x9a\x19\xa6\x89\xe6\x0e\xad\x09\x8c\x6b\x1a\x52\x49\xea\xf2\x1c\x6a\x8e\x68\x32\xcd\x9d\xff\x10\x92\x14\xc2\xea\x6e\x71\x02\xb2\x37\xc6\xb8\xed\x81\xfa\xb2\x83\xde\x85\xbf\x2a\xbc\x67\xbc\x04\xa2\xac\x64\x3d\x36\x72\xb2\x01\xda\x5d\x3e\x35\xe4\xa8\x0a\x84\xbe\x6e\xc1\x53\x88\x26\x9f\x23\x47\x0a\x3c\x8d\x27\x63\x22\xf3\x6a\xec\x72\xe9\x52\x8b\x2f\xb4\x38\xcd\xb0\x61\xba\x23\x3d\x0e\x54\xe9\x84\xf8\x9c\xd2\xb6\x39\xdc\xcb\x19\x75\x1a\xf8\x3f\x2b\xcd\x05\xf9\x93\x40\xe4\xa8\xec\xd2\xde\xb9\x53\xf8\x5f\x41\x8f\xbf\x81\x1d\xff\x79\x72\xdc\xb6\x68\x1b\x82\x6d\xb9\x55\xb0\x03\x9c\x80\x9e\x30\x2e\x29\x0e\x44\x99\xa7\xc6\x5b\x1f\x69\x70\xd3\x11\xbf\x7f\x9f\xca\x25\x1e\x30\xe3\x1c\x31\xfe\x51\x64\x0f\x8a\xfe\x84\xcd\x2c\xb6\x9f\xb0\x99\x74\x7d\x06\xdd\x58\xe6\xce\xd8\x00\xbb\xc5\xa9\xeb\x66\x5e\x16\xeb\x42\xe3\x25\x8c\x24\x9e\x4b\xcb\x53\xe7\x5e\xe8\xbd\xcc\x1e\x92\xd5\xe9\xe4\xb2\x97\x31\xbf\x8d\x77\xdd\x7b\x19\xe6\x2d\xa7\x92\xfc\xe2\x42\xd0\x6b\x43\xf0\x82\x26\x52\xbe\xf5\xa5\x64\x16\x03\x39\x2a\xb2\xb1\x3b\x24\x48\x49\xed\x8e\x42\xed\x75\x9f\xa0\x2b\x00\x18\xbe\x2c\xdc\x46\xae\xae\xae\x8e\x5d\x7a\xf2\xa0\x99\xba\x0a\xe0\xae\xbc\xca\x29\xfe\x4a\xdb\x30\x9c\xdd\x15\xa4\xc7\xbc\xb0\xb2\x79\xec\xc9\xed\x5a\x2e\xc2\xd3\x67\x48\xe3\xe4\xd7\xd9\x01\x7c\x6b\xac\x9f\x1b\xef\x62\xba\x16\x23\xa7\x2b\xb8\x7c\x4f\xe0\x2e\x51\x4d\x27\x57\xf0\xf0\xa5\x02\xf8\x3c\x9a\xe6\x30\xbf\x90\x25\x8f\xa6\xd8\xe4\x61\x40\x0a\x5e\xf8\x2f\x80\xf7\x3c\xde\x67\x53\x07\xaa\x1f\x7f\x06\x00\x00\xff\xff\x3b\xb1\x09\x16\x57\x0d\x00\x00")
func typesStoryGraphqlBytes() ([]byte, error) {
return bindataRead(
_typesStoryGraphql,
"types/story.graphql",
)
}
func typesStoryGraphql() (*asset, error) {
bytes, err := typesStoryGraphqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "types/story.graphql", size: 3415, mode: os.FileMode(420), modTime: time.Unix(1531585348, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _typesTagGraphql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x52\x41\x73\xd5\x3c\x0c\xbc\xe7\x57\x6c\xa7\x87\xef\x92\xef\x71\xef\xad\xc3\x70\x60\x60\xe0\xd0\xfe\x01\x3d\x47\x89\x3d\xd8\x52\xc6\x52\x52\x1e\x4c\xff\x3b\x63\xa7\x8f\x02\x67\x38\xd9\x71\xa4\xd5\xee\x6a\x6f\x71\x8f\x47\x5a\x90\x0c\x84\xc2\x24\x06\x9d\x41\x66\x1a\x12\x79\x92\x05\xe6\x5a\x13\x1b\x3c\x92\x23\xd2\xce\x98\xd8\x29\x65\x43\x12\x04\x2d\x45\x05\x4f\xc9\x23\x54\x18\x24\xea\x91\xeb\x69\xf0\xcb\xca\x1d\xf8\xfb\x00\xdc\xe2\x31\x32\x9c\x96\xff\x0c\x5f\x92\x4c\x03\xfa\x71\xd7\x0a\x3e\x24\x99\x6e\x86\x3f\x8a\x84\x0a\x0f\xe8\xc7\x1d\x1e\xbc\x26\x59\x6e\x86\xe7\x61\xf8\x57\x6c\x93\xac\x9b\x37\xe4\xf7\xfd\xf2\xb7\x39\xe7\xac\x4f\x3c\x61\xa7\xbc\xb1\x61\xd6\xda\x40\x4e\x1d\x95\x65\x2b\x57\xc8\x97\xb9\xf7\x02\xad\x0b\x49\xfa\x46\x9e\x54\x0e\xb1\x81\x3c\x44\x50\xce\x70\xae\xa5\x63\x24\xf9\x7f\x93\xb4\x73\x35\x46\xd0\xba\x6a\xed\xf5\x36\xc2\x99\x8a\x8d\x58\xaa\x6e\xab\x8d\x08\x5b\x76\x1b\x5b\x4f\x60\x1b\xc1\x1e\x4e\xa7\xd3\x00\x7c\xfe\x65\xca\xa1\xe6\x1e\x21\x52\xa5\xe0\x5c\x9b\x2a\x58\xd4\x2d\x4f\x87\x8f\x1e\x19\xfc\x95\x82\x63\xde\x72\xee\x3a\x9b\xf9\xed\xf9\x67\x53\x43\x7d\x7b\xfd\xb8\x42\x66\x0d\xaf\x42\xe4\xe2\xb1\xed\x69\xae\x5a\x40\x58\x33\x09\x3b\x5c\x41\x02\x36\xa7\x73\x4e\x16\x0b\x8b\x9f\xf0\x18\x93\xa1\xd0\x05\xba\x73\xcd\xb4\x1e\x7b\xa3\xdf\xdd\x19\x41\x32\x21\xcd\x30\x1d\x71\x56\x8f\x7d\x68\xb3\xb6\x47\xc3\x69\xb1\xab\x8a\x33\x63\x33\x9e\x1a\xc7\x8f\x2f\x94\x86\xab\xe1\xbc\xb3\xf8\xe1\xf4\x9a\xd5\xa1\xb5\xdd\xa8\x7a\xcf\x57\x7f\x6b\x7d\xef\x5a\xd9\xd1\xf4\xa9\x05\xe8\x45\x3f\x9d\x75\xe7\x11\xe7\xcd\x91\x1c\x93\xb2\xc1\x3c\xb5\x5d\x25\x46\xd9\xb2\xa7\x35\xf3\x6b\x34\x75\xe1\x1e\xbb\x1e\x1e\xe1\xa7\xfe\xe7\xf2\x26\x44\x5a\x9b\xf1\xb3\xd6\x42\x7e\x28\x3f\x9b\x66\xf6\x66\x7e\xb2\xbe\x91\xa6\xac\x31\x79\xe0\x86\x35\x3c\xff\x08\x00\x00\xff\xff\x1f\xbf\x7a\x64\xc0\x03\x00\x00")
func typesTagGraphqlBytes() ([]byte, error) {
return bindataRead(
_typesTagGraphql,
"types/tag.graphql",
)
}
func typesTagGraphql() (*asset, error) {
bytes, err := typesTagGraphqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "types/tag.graphql", size: 960, mode: os.FileMode(420), modTime: time.Unix(1530963938, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _typesUserGraphql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4c\xcc\x41\x0a\xc2\x40\x0c\x85\xe1\x7d\x4e\xf1\x8a\xb7\xe8\x35\xd4\x95\xb8\x18\x6c\x6c\xdf\xa2\x99\x21\x89\x88\x88\x77\x97\x0e\x5d\x74\x19\xf2\xbd\xff\x84\xcb\xa2\xb8\x86\x3a\xf2\xd3\x14\x0c\x3c\xab\x83\x96\xea\xe5\x91\xb4\x19\x6f\xe6\x82\xd7\x26\x6a\x4b\x56\x0b\x14\x9b\x10\x9a\xdb\x37\xa4\xcf\x7a\xe0\x2b\x40\xef\xd1\xbb\xb7\xb2\xaa\x00\x9c\x46\x9c\xd3\x69\xf3\x20\x07\xd1\xd4\x57\x46\xb0\x9a\xe0\x70\xc4\x88\xdb\xae\xef\x83\xfc\xfe\x01\x00\x00\xff\xff\x74\x0a\x0a\x1c\xa0\x00\x00\x00")
func typesUserGraphqlBytes() ([]byte, error) {
return bindataRead(
_typesUserGraphql,
"types/user.graphql",
)
}
func typesUserGraphql() (*asset, error) {
bytes, err := typesUserGraphqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "types/user.graphql", size: 160, mode: os.FileMode(420), modTime: time.Unix(1525514069, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
// Asset loads and returns the asset for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func Asset(name string) ([]byte, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
}
return a.bytes, nil
}
return nil, fmt.Errorf("Asset %s not found", name)
}
// MustAsset is like Asset but panics when Asset would return an error.
// It simplifies safe initialization of global variables.
func MustAsset(name string) []byte {
a, err := Asset(name)
if err != nil {
panic("asset: Asset(" + name + "): " + err.Error())
}
return a
}
// AssetInfo loads and returns the asset info for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func AssetInfo(name string) (os.FileInfo, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
}
return a.info, nil
}
return nil, fmt.Errorf("AssetInfo %s not found", name)
}
// AssetNames returns the names of the assets.
func AssetNames() []string {
names := make([]string, 0, len(_bindata))
for name := range _bindata {
names = append(names, name)
}
return names
}
// _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){
"root.graphql": rootGraphql,
"types/change.graphql": typesChangeGraphql,
"types/channel.graphql": typesChannelGraphql,
"types/chapter.graphql": typesChapterGraphql,
"types/character.graphql": typesCharacterGraphql,
"types/file.graphql": typesFileGraphql,
"types/log.graphql": typesLogGraphql,
"types/post.graphql": typesPostGraphql,
"types/session.graphql": typesSessionGraphql,
"types/story.graphql": typesStoryGraphql,
"types/tag.graphql": typesTagGraphql,
"types/user.graphql": typesUserGraphql,
}
// AssetDir returns the file names below a certain
// directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the
// following hierarchy:
// data/
// foo.txt
// img/
// a.png
// b.png
// then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
// AssetDir("") will return []string{"data"}.
func AssetDir(name string) ([]string, error) {
node := _bintree
if len(name) != 0 {
cannonicalName := strings.Replace(name, "\\", "/", -1)
pathList := strings.Split(cannonicalName, "/")
for _, p := range pathList {
node = node.Children[p]
if node == nil {
return nil, fmt.Errorf("Asset %s not found", name)
}
}
}
if node.Func != nil {
return nil, fmt.Errorf("Asset %s not found", name)
}
rv := make([]string, 0, len(node.Children))
for childName := range node.Children {
rv = append(rv, childName)
}
return rv, nil
}
type bintree struct {
Func func() (*asset, error)
Children map[string]*bintree
}
var _bintree = &bintree{nil, map[string]*bintree{
"root.graphql": &bintree{rootGraphql, map[string]*bintree{}},
"types": &bintree{nil, map[string]*bintree{
"change.graphql": &bintree{typesChangeGraphql, map[string]*bintree{}},
"channel.graphql": &bintree{typesChannelGraphql, map[string]*bintree{}},
"chapter.graphql": &bintree{typesChapterGraphql, map[string]*bintree{}},
"character.graphql": &bintree{typesCharacterGraphql, map[string]*bintree{}},
"file.graphql": &bintree{typesFileGraphql, map[string]*bintree{}},
"log.graphql": &bintree{typesLogGraphql, map[string]*bintree{}},
"post.graphql": &bintree{typesPostGraphql, map[string]*bintree{}},
"session.graphql": &bintree{typesSessionGraphql, map[string]*bintree{}},
"story.graphql": &bintree{typesStoryGraphql, map[string]*bintree{}},
"tag.graphql": &bintree{typesTagGraphql, map[string]*bintree{}},
"user.graphql": &bintree{typesUserGraphql, map[string]*bintree{}},
}},
}}
// RestoreAsset restores an asset under the given directory
func RestoreAsset(dir, name string) error {
data, err := Asset(name)
if err != nil {
return err
}
info, err := AssetInfo(name)
if err != nil {
return err
}
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
if err != nil {
return err
}
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil {
return err
}
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
if err != nil {
return err
}
return nil
}
// RestoreAssets restores an asset under the given directory recursively
func RestoreAssets(dir, name string) error {
children, err := AssetDir(name)
// File
if err != nil {
return RestoreAsset(dir, name)
}
// Dir
for _, child := range children {
err = RestoreAssets(dir, filepath.Join(name, child))
if err != nil {
return err
}
}
return nil
}
func _filePath(dir, name string) string {
cannonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
}
+7 -18
View File
@@ -3,19 +3,19 @@ type Query {
# Find character by either an ID or a nick.
character(id: String, nick: String): Character
# Find characters by either a list of ids, nicks or an author. Only one parameter at a time
characters(ids: [String!], nicks: [String!], author: String): [Character!]!
# Find characters
characters(filter: CharactersFilter): [Character!]!
# Find channel by name
channel(name: String!): Channel
# Find all channels. Specifying `logged: true` restricts the search to only logged.
channels(logged: Boolean): [Channel!]!
# Find channels
channels(filter: ChannelsFilter): [Channel!]!
# Find log by ID
log(id: String): Log
log(id: String!): Log
# Find logs
logs(filter: LogsFilter): [LogHeader!]!
@@ -32,8 +32,8 @@ type Query {
# Find file by ID
file(id: String!): File
# Files get all available files. If includePublic is set, it will return public files as well
files(includePublic: Boolean, mimeTypes: [String!]): [File!]!
# Find files
files(filter: FilesFilter): [File!]!
# Find story by ID
@@ -49,10 +49,6 @@ type Query {
# Find all distinct tags used in stories
tags: [Tag!]!
# Find current session
session: Session!
}
# The Mutation type represents write entry points into the API.
@@ -137,13 +133,6 @@ type Mutation {
# Remove a chapter
removeChapter(id: String!): Chapter!
# Log in
login(username: String!, password: String!): Session!
# Log out
logout(): Session!
}
schema {
+12
View File
@@ -16,6 +16,18 @@ type Channel {
locationName: String
}
# Filters for the channels query
input ChannelsFilter {
# Filter to either logged or unlogged channels
logged: Boolean
# Filter by event name
eventName: String
# Filter by location name
locationName: String
}
input ChannelAddInput {
# The channel's name
name: String!
+21
View File
@@ -22,6 +22,27 @@ type Character {
description: String!
}
# Filter for characters query
input CharactersFilter {
# Filter by character IDs
ids: [String!]
# Filter by nicks
nicks: [String!]
# Filter by names
names: [String!]
# Filter by author
author: String
# Filter by text search matching against the character's description
search: String
# Filter by whether they've been part of a log.
logged: Boolean
}
# Input for adding characters
input CharacterAddInput {
# The primary IRC nick name to recognize this character by
+9
View File
@@ -29,6 +29,15 @@ type File {
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.
mimeTypes: [String!]
}
# Input for editFile mutation
input EditFileInput {
# The file's unique ID

Some files were not shown because too many files have changed in this diff Show More