Compare commits

...
90 Commits
Author SHA1 Message Date
gisle 947765884e models: Fixed earliestDate being exclusive of the earliest date in chnages.List.
the build was successful
2018-11-01 19:38:01 +01:00
gisle ffec008820 graph2: Added change type and query.
the build was successful
2018-10-31 21:38:45 +01:00
gisle 138b3e3eab graph2: Fixed post change on move not including all posts as objects. 2018-10-31 21:38:32 +01:00
gisle 929abfdbe4 package: Removed rpdata-ui. 2018-10-31 20:28:09 +01:00
gisle a8cd595876 graph2: Added change logging for story and chapter, fixed channel ops not being logged.
the build was successful
2018-10-29 19:12:23 +01:00
gisle 9ed3b32b42 models: Added support for multiple on changekeys.All, though only first is used for * key. 2018-10-29 17:51:39 +01:00
gisle 6bae52d652 models: Added benchmarks to changekeys, added shortcuts for common types there could be many of. 2018-10-29 17:44:48 +01:00
gisle cee370c2ac models: Changed change logging system to be allow for better querying in the future.
the build was successful
2018-10-28 12:18:17 +01:00
gisle 9affdbd20e graph2: Added move mutation to chapter.
the build was successful
2018-10-27 14:59:47 +02:00
gisle cf545cde3b graphql: Removed old graphql setup.
the build was successful
2018-10-27 10:16:25 +02:00
gisle b414cf118f rpdata-wikifileimport: Fixed it to use new models module.
the build was successful
2018-10-27 10:15:23 +02:00
gisle 4979b02104 models: Migrated last from model 2018-10-27 10:14:58 +02:00
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
228 changed files with 4981 additions and 17874 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/*
+7 -1
View File
@@ -4,4 +4,10 @@ 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.Resolver{}, 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)
}
}
+3 -3
View File
@@ -9,7 +9,7 @@ import (
"git.aiterp.net/rpdata/api/internal/config"
"git.aiterp.net/rpdata/api/internal/store"
"git.aiterp.net/rpdata/api/model/file"
"git.aiterp.net/rpdata/api/models/files"
)
func main() {
@@ -33,7 +33,7 @@ func main() {
store.Init()
for _, info := range data.Query.Allimages {
existing, err := file.FindName("wiki", info.Name, info.User)
existing, err := files.FindName("wiki", info.Name, info.User)
if err == nil && existing.ID != "" {
log.Println("Skipping", info.Name, "because it already exists")
continue
@@ -45,7 +45,7 @@ func main() {
continue
}
file, err := file.Insert(info.Name, "wiki", info.Mime, info.User, ts, info.Size, info.URL)
file, err := files.Insert(info.Name, "wiki", info.Mime, info.User, ts, info.Size, info.URL)
if err != nil {
log.Println("Skipping", info.Name, "error:", err)
continue
+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
+67
View File
@@ -0,0 +1,67 @@
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
Change:
model: git.aiterp.net/rpdata/api/models.Change
ChangeModel:
model: git.aiterp.net/rpdata/api/models.ChangeModel
ChangeKey:
model: git.aiterp.net/rpdata/api/models.ChangeKey
ChangeKeyInput: # It's the same as ChangeKey
model: git.aiterp.net/rpdata/api/models.ChangeKey
ChangesFilter:
model: git.aiterp.net/rpdata/api/models/changes.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
+51
View File
@@ -0,0 +1,51 @@
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) Change() ChangeResolver {
return &types.ChangeResolver
}
func (r *rootResolver) Token() TokenResolver {
return &types.TokenResolver
}
+14
View File
@@ -0,0 +1,14 @@
package queries
import (
"context"
"git.aiterp.net/rpdata/api/models"
"git.aiterp.net/rpdata/api/models/changes"
)
/// Queries
func (r *resolver) Changes(ctx context.Context, filter *changes.Filter) ([]models.Change, error) {
return changes.List(filter)
}
+79
View File
@@ -0,0 +1,79 @@
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/changekeys"
"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, true, changekeys.Listed(channel), 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, true, changekeys.Listed(channel), channel)
return channel, nil
}
+163
View File
@@ -0,0 +1,163 @@
package queries
import (
"context"
"errors"
"time"
"git.aiterp.net/rpdata/api/models/changekeys"
"git.aiterp.net/rpdata/api/models/changes"
"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")
}
chapter, err := chapters.Add(story, input.Title, author, input.Source, time.Now(), input.FictionalDate)
if err != nil {
return models.Chapter{}, errors.New("Failed to create chapter: " + err.Error())
}
go changes.Submit("Chapter", "add", token.UserID, story.Listed, changekeys.Listed(story, chapter), story, chapter)
return chapter, nil
}
func (r *mutationResolver) MoveChapter(ctx context.Context, input input.ChapterMoveInput) (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.move") {
return models.Chapter{}, errors.New("You are not allowed to move this chapter")
}
target, err := stories.FindID(input.StoryID)
if err != nil {
return models.Chapter{}, errors.New("Target story not found")
}
if !target.Open && !token.PermittedUser(target.Author, "member", "chapter.move") {
return models.Chapter{}, errors.New("You are not permitted to move chapters to this story")
}
oldStoryID := chapter.StoryID
chapter, err = chapters.Move(chapter, target)
if err != nil {
return models.Chapter{}, errors.New("Failed to move chapter: " + err.Error())
}
go func() {
story, err := stories.FindID(chapter.StoryID)
if err != nil {
story.ID = chapter.StoryID
}
oldStory, err := stories.FindID(oldStoryID)
if err != nil {
oldStory.ID = oldStoryID
}
changes.Submit("Chapter", "move-out", chapter.Author, oldStory.Listed, changekeys.Many(oldStory, chapter), chapter)
changes.Submit("Chapter", "move-in", token.UserID, story.Listed, changekeys.Listed(story, chapter), story, chapter)
}()
return chapter, nil
}
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{}
}
chapter, err = chapters.Edit(chapter, input.Title, input.Source, input.FictionalDate)
if err != nil {
return models.Chapter{}, errors.New("Failed to edit chapter: " + err.Error())
}
go func() {
story, err := stories.FindID(chapter.StoryID)
if err != nil {
story.ID = chapter.StoryID
}
changes.Submit("Chapter", "edit", token.UserID, story.Listed, changekeys.Many(story, chapter), chapter)
}()
return chapter, nil
}
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")
}
chapter, err = chapters.Remove(chapter)
if err != nil {
return models.Chapter{}, errors.New("Failed to remove chapter: " + err.Error())
}
go func() {
story, err := stories.FindID(chapter.StoryID)
if err != nil {
story.ID = chapter.StoryID
}
changes.Submit("Chapter", "remove", token.UserID, story.Listed, changekeys.Many(story, chapter), chapter)
}()
return chapter, nil
}
+174
View File
@@ -0,0 +1,174 @@
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/changekeys"
"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, true, changekeys.Listed(character), 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, true, changekeys.Listed(character), 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, true, changekeys.Listed(character), 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, true, changekeys.Listed(character), 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, true, changekeys.Listed(character), 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
+139
View File
@@ -0,0 +1,139 @@
package queries
import (
"context"
"errors"
"strings"
"git.aiterp.net/rpdata/api/models/channels"
"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/changekeys"
"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
}
existingChannel, _ := channels.FindName(input.Channel)
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 func() {
if existingChannel.Name == "" {
channel, err := channels.FindName(input.Channel)
if err == nil {
changes.Submit("Channel", "add", token.UserID, true, changekeys.Listed(channel), channel)
}
}
changes.Submit("Log", "add", token.UserID, true, changekeys.Listed(log), 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, true, changekeys.Listed(log), 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, true, changekeys.Listed(log), log)
return log, nil
}
+162
View File
@@ -0,0 +1,162 @@
package queries
import (
"context"
"errors"
"git.aiterp.net/rpdata/api/models/changekeys"
"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, true, changekeys.Many(log, post), 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 func() {
log, err := logs.FindID(post.LogID)
if err != nil {
log = models.Log{ID: post.LogID}
}
changes.Submit("Post", "edit", token.UserID, true, changekeys.Many(log, post), 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())
}
go func() {
log, err := logs.FindID(post.LogID)
if err != nil {
log = models.Log{ID: post.LogID}
}
changes.Submit("Post", "move", token.UserID, true, changekeys.Many(log, posts), posts)
}()
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)
changes.Submit("Post", "remove", token.UserID, true, changekeys.Many(log, post), 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
+169
View File
@@ -0,0 +1,169 @@
package queries
import (
"context"
"errors"
"time"
"git.aiterp.net/rpdata/api/models/changekeys"
"git.aiterp.net/rpdata/api/models/changes"
"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
story, err := stories.Add(input.Name, author, input.Category, listed, open, input.Tags, time.Now(), fictionalDate)
if err != nil {
return models.Story{}, errors.New("Failed to add story: " + err.Error())
}
go changes.Submit("Story", "add", token.UserID, story.Listed, changekeys.Listed(story), story)
return story, nil
}
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")
}
story, err = stories.AddTag(story, input.Tag)
if err != nil {
return models.Story{}, errors.New("Failed to add story: " + err.Error())
}
go changes.Submit("Story", "tag", token.UserID, story.Listed, changekeys.Listed(story), story, input.Tag)
return story, nil
}
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")
}
story, err = stories.RemoveTag(story, input.Tag)
if err != nil {
return models.Story{}, errors.New("Failed to add story: " + err.Error())
}
go changes.Submit("Story", "untag", token.UserID, story.Listed, changekeys.Listed(story), story, input.Tag)
return story, nil
}
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{}
}
story, err = stories.Edit(story, input.Name, input.Category, input.Listed, input.Open, input.FictionalDate)
if err != nil {
return models.Story{}, errors.New("Failed to add story: " + err.Error())
}
go changes.Submit("Story", "edit", token.UserID, story.Listed, changekeys.Listed(story), story)
return story, nil
}
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())
}
go changes.Submit("Story", "remove", token.UserID, story.Listed, changekeys.Listed(story), story)
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
}
@@ -1,118 +1,68 @@
# The Query type represents the read entry points into the API.
schema {
query: Query
mutation: Mutation
}
type Query {
# Find character by either an ID or a nick.
character(id: String, nick: String): Character
character(id: String, nick: String): Character!
# Find characters
characters(filter: CharactersFilter): [Character!]!
# Find channel by name
channel(name: String!): Channel
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
log(id: String!): Log!
# Find logs
logs(filter: LogsFilter): [LogHeader!]!
# Find post by ID.
post(id: String!): Post
# Find posts by IDs. It's meant to allow other parts of the UI to link to a cluster of posts, e.g. for a room description for the
# Mapp should it ever become a thing. This does not have a filter, since it's meant to be queried in the logs' response's selection set.
posts(ids: [String!]!): [Post!]!
# Find file by ID
file(id: String!): File
# Find files
files(filter: FilesFilter): [File!]!
# Find story by ID
story(id: String!): Story
# Find stories
stories(filter: StoriesFilter): [Story!]!
logs(filter: LogsFilter): [Log!]!
# Find story chapter by ID
chapter(id: String!): Chapter
chapter(id: String!): Chapter!
# Find all distinct tags used in stories
tags: [Tag!]!
# Find current session
session: Session!
# 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!]!
# Find changes
changes(filter: ChangesFilter): [Change!]!
# Get information about the token, useful for debugging.
token: Token!
}
# The Mutation type represents write entry points into the API.
type Mutation {
# 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(id: String!): Character!
# Add a channel
addChannel(input: ChannelAddInput!): Channel!
# Edit a channel
editChannel(input: ChannelEditInput!): Channel!
# Remove a channel
removeChannel(name: String!): Channel!
# Add a new log
addLog(input: LogAddInput!): Log!
# Edit a log
editLog(input: LogEditInput!): Log!
# Remove a log
removeLog(id: String!): Log!
# Add a post
addPost(input: AddPostInput!): Post!
# Edit a post
editPost(input: EditPostInput!): Post!
# Move a post
movePost(input: MovePostInput!): Post!
# Remove a post
removePost(id: String!): Post!
# Edit a file
editFile(input: EditFileInput!): File!
# Remove a file
removeFile(id: String!): File!
# Add a story
addStory(input: StoryAddInput!): Story!
@@ -126,7 +76,7 @@ type Mutation {
editStory(input: StoryEditInput!): Story!
# Remove a story
removeStory(id: String!): Story!
removeStory(input: StoryRemoveInput!): Story!
# Add a chapter to a story
@@ -135,18 +85,59 @@ type Mutation {
# Edit a chapter
editChapter(input: ChapterEditInput!): Chapter!
# Move a chapter
moveChapter(input: ChapterMoveInput!): Chapter!
# Remove a chapter
removeChapter(id: String!): Chapter!
removeChapter(input: ChapterRemoveInput!): Chapter!
# Log in
login(username: String!, password: String!): Session!
# 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!
# Log out
logout(): Session!
# 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!
}
schema {
query: Query
mutation: Mutation
}
# A Date represents a RFC3339 encoded date with up to millisecond precision.
scalar Date
+81
View File
@@ -0,0 +1,81 @@
"""
A change represents a a change in the history.
"""
type Change {
"A unique ID of the change."
id: String!
"The model of the changed object."
model: ChangeModel!
"The operation performed."
op: String!
"The author that performed the change."
author: String!
"Whether the change will be listed without a matching key."
listed: Boolean!
"The keys indexing this change."
keys: [ChangeKey!]!
"The date when the change happened."
date: Date
"The changed objects."
objects: [ChangeObject!]!
}
union ChangeObject = Character | Channel | Log | Post | Story | Tag | Chapter
"""
A change key is a key associated with a change, used for filtering
and subscription. A log post edit has keys for both the log and post,
making it suitable to use it to patch an active log file.
"""
type ChangeKey {
"The model the key is for."
model: ChangeModel!
"The model's id, or * if it's a key indicating a model list should be updated."
id: String!
}
"""
See ChangeKey
"""
input ChangeKeyInput {
"The model the key is for."
model: ChangeModel!
"The model's id, or * if it's a key indicating a model list should be updated."
id: String!
}
"""
A model related to the change.
"""
enum ChangeModel {
Character
Channel
Log
Post
Story
Tag
Chapter
}
"""
Optional filter for the changes query.
"""
input ChangesFilter {
"The keys to query for."
keys: [ChangeKeyInput!]
"Only show changes more recent than this date."
earliestDate: Date
"Limit the amount of results."
limit: Int
}
@@ -13,13 +13,13 @@ type Chapter {
source: String!
# When the chapter was posted initialy
createdDate: String!
createdDate: Date!
# The in-universe date and time of the chapter
fictionalDate: String!
fictionalDate: Date
# The date of edit.
editedDate: String!
editedDate: Date!
}
# Input for addChapter mutation
@@ -40,7 +40,7 @@ input ChapterAddInput {
source: String!
# Optionally, assign a fictional date for the chapter
fictionalDate: String
fictionalDate: Date
}
# Input for editChapter mutation
@@ -55,5 +55,23 @@ input ChapterEditInput {
source: String
# Set the fictional date for a chapter
fictionalDate: String
fictionalDate: Date
# Remove the fictional date for a chapter
clearFictionalDate: Boolean
}
# Input for moveChapter mutation
input ChapterMoveInput {
# The chapter to move
id: String!
# The story to move it to
storyId: String!
}
# Input for removeChapter mutation
input ChapterRemoveInput {
# The chapter to remove
id: String!
}
@@ -68,6 +68,7 @@ input CharacterNickInput {
nick: String!
}
# Inpuit for editCharacter mutation
input CharacterEditInput {
# The id for the character to edit
id: String!
@@ -80,4 +81,11 @@ input CharacterEditInput {
# A short description for the character
description: String
}
}
# Input for removeCharacter mutation
input CharacterRemoveInput {
# The id for the character to edit
id: String!
}
@@ -7,7 +7,7 @@ type File {
kind: String!
# The time of uploading
time: String!
time: Date!
# Whether the file is publicly listable. Someone with knowledge of the ID
# will still be able to view it, however.
@@ -35,7 +35,7 @@ input FilesFilter {
public: Boolean
# Limit the MIME types of the files.
mimeTypes: [String!]
mimeType: [String!]
}
# Input for editFile mutation
@@ -8,7 +8,7 @@ type Log {
shortId: String!
# The date for a log.
date: String!
date: Date!
# The channel of a log.
channelName: String!
@@ -21,7 +21,7 @@ type Log {
# The log's event, which is the same as the tags in the previous logbot site.
# Empty string means that it's no event.
event: String!
eventName: String!
# The description of a session, which is empty if unset.
description: String!
@@ -50,17 +50,17 @@ input LogsFilter {
# Search post content
search: String
# Limit to only open logs
# Limit by whether it's open or not
open: Boolean
# Limit the amount of posts
# Limit the amount of results you get back
limit: Int
}
# Input for addLog mutation
input LogAddInput {
# The date of the log, in RFC3339 format with up to nanosecond precision
date: String!
# The date of the log.
date: Date!
# The channel where the log is set
channel: String!
@@ -96,37 +96,8 @@ input LogEditInput {
open: Boolean
}
# A LogHeader is a cut-down version of Log that does not allow getting the actual posts.
type LogHeader {
# A unique identifier for the log.
# Input for removeLog mutation
input LogRemoveInput {
# The id of the log to remove
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: String!
# The channel name 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.
event: 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!]!
}
}
@@ -7,7 +7,7 @@ type Post {
logId: String!
# The date and time of posting
time: String!
time: Date!
# The kind of post this is. Only "text", "scene" and "action" are RP, while others are annotations and 'commands'.
kind: String!
@@ -23,12 +23,12 @@ type Post {
}
# Input for the addPost mutation
input AddPostInput {
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: String!
time: Date!
# The kind of post this is. Only "text", "scene" and "action" are RP, while others are annotations and 'commands'.
kind: String!
@@ -41,12 +41,12 @@ input AddPostInput {
}
# Input for the editPost mutation
input EditPostInput {
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: String
time: Date
# The kind of post this is. Only "text", "scene" and "action" are RP, while others are annotations and 'commands'.
kind: String
@@ -59,10 +59,24 @@ input EditPostInput {
}
# Input for the movePost mutation
input MovePostInput {
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
}
@@ -25,14 +25,14 @@ type Story {
# The chapters of this story
chapters: [Chapter!]!
# The date the story was created (RFC3339 format).
createdDate: String!
# The date the story was created.
createdDate: Date!
# The date the story is set in (RFC3339 format).
fictionalDate: String!
# The date the story is set in.
fictionalDate: Date
# The date of the last major update to the story (RFC3339 format). This being bumped means a new chapter has been added.
updatedDate: String!
# 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.
@@ -40,14 +40,23 @@ 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: String
earliestFictionalDate: Date
# The latest fictionalDate
latestFictionalDate: String
latestFictionalDate: Date
# The max amount of stories to get (default: 30)
limit: Int
@@ -75,8 +84,8 @@ input StoryAddInput {
# Set which tags the story should have.
tags: [TagInput!]
# Set the fictional date of the story (RFC3339 format).
fictionalDate: String
# Set the fictional date of the story.
fictionalDate: Date
}
# Input for the editStory mutation
@@ -101,8 +110,11 @@ input StoryEditInput {
# Change whether to show this story in the list and search results
listed: Boolean
# Set the fictional date of the story (RFC3339 format).
fictionalDate: String
# Set the fictional date of the story.
fictionalDate: Date
# Clear the fictional date of the story.
clearFictionalDate: Boolean
}
# Input for the addStoryTag mutation
@@ -123,20 +135,26 @@ input StoryTagRemoveInput {
tag: TagInput!
}
# Input for the removeStory mutation
input StoryRemoveInput {
# What story to remove.
id: String!
}
# Possible values for Story.category
enum StoryCategory {
# Description and content of a document or item
Document
# 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
# General information
Info
# A short story
Story
}
@@ -33,4 +33,5 @@ enum TagKind {
# 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!]!
}
+24
View File
@@ -0,0 +1,24 @@
package types
import (
"context"
"git.aiterp.net/rpdata/api/graph2/input"
"git.aiterp.net/rpdata/api/models"
)
type changeResolver struct{}
func (r *changeResolver) Objects(ctx context.Context, obj *models.Change) ([]input.ChangeObject, error) {
objects := obj.Objects()
results := make([]input.ChangeObject, len(objects))
for i := range objects {
results[i] = objects[i]
}
return results, nil
}
// ChangeResolver is a resolver
var ChangeResolver changeResolver
+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
-59
View File
@@ -1,59 +0,0 @@
package mutations
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"
)
// 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
}
-67
View File
@@ -1,67 +0,0 @@
package mutations
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"
)
// 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
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
}
@@ -1,70 +0,0 @@
package mutations
import (
"context"
"strings"
"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"
)
// 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
}
@@ -1,48 +0,0 @@
package mutations
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/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
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
}
-70
View File
@@ -1,70 +0,0 @@
package mutations
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"
)
// 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
}
-60
View File
@@ -1,60 +0,0 @@
package mutations
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"
)
// 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
}
-83
View File
@@ -1,83 +0,0 @@
package mutations
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"
)
// 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
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
}
-53
View File
@@ -1,53 +0,0 @@
package mutations
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/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}
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
}
-50
View File
@@ -1,50 +0,0 @@
package mutations
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"
)
// 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
}
-63
View File
@@ -1,63 +0,0 @@
package mutations
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"
)
// 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
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
}
@@ -1,64 +0,0 @@
package mutations
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/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
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
}
-43
View File
@@ -1,43 +0,0 @@
package mutations
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/session"
"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
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
}
-51
View File
@@ -1,51 +0,0 @@
package mutations
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/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
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
}
-63
View File
@@ -1,63 +0,0 @@
package mutations
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"
)
// 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
}
-69
View File
@@ -1,69 +0,0 @@
package mutations
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"
)
// 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
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
}
-26
View File
@@ -1,26 +0,0 @@
package mutations
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/session"
)
// LoginArgs is args for the login mutation
type LoginArgs struct {
Username string
Password string
}
// Login resolves the login mutation
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
}
-16
View File
@@ -1,16 +0,0 @@
package mutations
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/session"
)
// 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
}
-45
View File
@@ -1,45 +0,0 @@
package mutations
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/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
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
}
@@ -1,37 +0,0 @@
package mutations
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"
)
// 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) {
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
}
@@ -1,37 +0,0 @@
package mutations
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/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) {
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
}
@@ -1,44 +0,0 @@
package mutations
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/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) {
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
}
@@ -1,48 +0,0 @@
package mutations
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/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
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
}
-37
View File
@@ -1,37 +0,0 @@
package mutations
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/session"
"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) {
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
}
-37
View File
@@ -1,37 +0,0 @@
package mutations
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/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) {
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
}
-36
View File
@@ -1,36 +0,0 @@
package mutations
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/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) {
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
}
-41
View File
@@ -1,41 +0,0 @@
package mutations
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/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) {
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
}
@@ -1,53 +0,0 @@
package mutations
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/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}
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
}
-14
View File
@@ -1,14 +0,0 @@
// 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")
-23
View File
@@ -1,23 +0,0 @@
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
@@ -1,43 +0,0 @@
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
@@ -1,23 +0,0 @@
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
@@ -1,43 +0,0 @@
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
}
-59
View File
@@ -1,59 +0,0 @@
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
}
}
// 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)
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
@@ -1,23 +0,0 @@
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
@@ -1,45 +0,0 @@
package queries
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/session"
"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
user := session.FromContext(ctx).User()
author := ""
if user != nil {
author = user.ID
}
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
@@ -1,23 +0,0 @@
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
}
-79
View File
@@ -1,79 +0,0 @@
package queries
import (
"context"
"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 *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 *LogsArgs) ([]*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
}
-23
View File
@@ -1,23 +0,0 @@
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
@@ -1,28 +0,0 @@
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
}
-11
View File
@@ -1,11 +0,0 @@
// 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")
-13
View File
@@ -1,13 +0,0 @@
package queries
import (
"context"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/internal/session"
)
// Session resolves query.session
func (r *QueryResolver) Session(ctx context.Context) (*types.SessionResolver, error) {
return &types.SessionResolver{S: session.FromContext(ctx)}, nil
}
-77
View File
@@ -1,77 +0,0 @@
package queries
import (
"context"
"time"
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"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
Limit *int32
}
}
// Stories resolves 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
}
-23
View File
@@ -1,23 +0,0 @@
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
}
-21
View File
@@ -1,21 +0,0 @@
package queries
import (
"git.aiterp.net/rpdata/api/graphql/resolver/types"
"git.aiterp.net/rpdata/api/model/story"
)
// Tags resolves the tags query
func (r *QueryResolver) Tags() ([]*types.TagResolver, error) {
tags, err := story.ListTags()
if err != nil {
return nil, err
}
resolvers := make([]*types.TagResolver, len(tags))
for i, tag := range tags {
resolvers[i] = &types.TagResolver{T: tag}
}
return resolvers, nil
}
-13
View File
@@ -1,13 +0,0 @@
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
}
-39
View File
@@ -1,39 +0,0 @@
package types
import "git.aiterp.net/rpdata/api/model/channel"
// ChannelResolver for the Channel graphql type
type ChannelResolver struct{ C channel.Channel }
// Name resolves channel.name
func (r *ChannelResolver) Name() string {
return r.C.Name
}
// Logged resolves channel.logged
func (r *ChannelResolver) Logged() bool {
return r.C.Logged
}
// Hub resolves channel.hub
func (r *ChannelResolver) Hub() bool {
return r.C.Hub
}
// EventName resolves channel.eventName
func (r *ChannelResolver) EventName() *string {
if r.C.Event == "" {
return nil
}
return &r.C.Event
}
// LocationName resolves channel.locationName
func (r *ChannelResolver) LocationName() *string {
if r.C.Location == "" {
return nil
}
return &r.C.Location
}
-50
View File
@@ -1,50 +0,0 @@
package types
import (
"time"
"git.aiterp.net/rpdata/api/model/story"
)
// ChapterResolver for the Chapter graphql type
type ChapterResolver struct{ C story.Chapter }
// ID resolves Chapter.id
func (r *ChapterResolver) ID() string {
return r.C.ID
}
// StoryID resolves Chapter.storyId
func (r *ChapterResolver) StoryID() string {
return r.C.StoryID
}
// Title resolves Chapter.title
func (r *ChapterResolver) Title() string {
return r.C.Title
}
// Author resolves Chapter.author
func (r *ChapterResolver) Author() string {
return r.C.Author
}
// Source resolves Chapter.source
func (r *ChapterResolver) Source() string {
return r.C.Source
}
// CreatedDate resolves Chapter.createdDate
func (r *ChapterResolver) CreatedDate() string {
return r.C.CreatedDate.Format(time.RFC3339Nano)
}
// FictionalDate resolves Chapter.fictionalDate
func (r *ChapterResolver) FictionalDate() string {
return r.C.FictionalDate.Format(time.RFC3339Nano)
}
// EditedDate resolves Chapter.editedDate
func (r *ChapterResolver) EditedDate() string {
return r.C.EditedDate.Format(time.RFC3339Nano)
}
-45
View File
@@ -1,45 +0,0 @@
package types
import "git.aiterp.net/rpdata/api/model/character"
// CharacterResolver for the Character graphql type
type CharacterResolver struct{ C character.Character }
// ID is a property resolver
func (r *CharacterResolver) ID() string {
return r.C.ID
}
// Nick is a property resolver
func (r *CharacterResolver) Nick() *string {
if len(r.C.Nicks) == 0 {
return nil
}
return &r.C.Nicks[0]
}
// Nicks is a property resolver
func (r *CharacterResolver) Nicks() []string {
return r.C.Nicks
}
// Name is a property resolver
func (r *CharacterResolver) Name() string {
return r.C.Name
}
// ShortName is a property resolver
func (r *CharacterResolver) ShortName() string {
return r.C.ShortName
}
// Author is a property resolver
func (r *CharacterResolver) Author() string {
return r.C.Author
}
// Description is a property resolver
func (r *CharacterResolver) Description() string {
return r.C.Description
}
-59
View File
@@ -1,59 +0,0 @@
package types
import (
"time"
"git.aiterp.net/rpdata/api/model/file"
)
// FileResolver for the File graphql type
type FileResolver struct{ F file.File }
// ID resolves File.id
func (f *FileResolver) ID() string {
return f.F.ID
}
// Author resolves File.author
func (f *FileResolver) Author() string {
return f.F.Author
}
// Kind resolves File.kind
func (f *FileResolver) Kind() string {
return f.F.Kind
}
// Time resolves File.time
func (f *FileResolver) Time() string {
return f.F.Time.Format(time.RFC3339Nano)
}
// Public resolves File.public
func (f *FileResolver) Public() bool {
return f.F.Public
}
// Name resolves File.name
func (f *FileResolver) Name() string {
return f.F.Name
}
// MimeType resolves File.mimeType
func (f *FileResolver) MimeType() string {
return f.F.MimeType
}
// Size resolves File.size
func (f *FileResolver) Size() int32 {
return int32(f.F.Size)
}
// URL resolves File.url
func (f *FileResolver) URL() *string {
if f.F.URL == "" {
return nil
}
return &f.F.URL
}
-117
View File
@@ -1,117 +0,0 @@
package types
import (
"context"
"errors"
"time"
"git.aiterp.net/rpdata/api/graphql/loader"
"git.aiterp.net/rpdata/api/model/log"
)
// LogResolver for the Log graphql type
type LogResolver struct{ L log.Log }
// ID resolves Log.id
func (r *LogResolver) ID() string {
return r.L.ID
}
// ShortID resolves Log.shortId
func (r *LogResolver) ShortID() string {
return r.L.ShortID
}
// Date resolves Log.date
func (r *LogResolver) Date() string {
return r.L.Date.Format(time.RFC3339Nano)
}
// ChannelName resolves Log.channelName
func (r *LogResolver) ChannelName() string {
return r.L.Channel
}
// Channel resolves Log.channe
func (r *LogResolver) Channel(ctx context.Context) (*ChannelResolver, error) {
loader := loader.FromContext(ctx)
if loader == nil {
return nil, errors.New("no loader")
}
channel, err := loader.Channel("name", r.L.Channel)
if err != nil {
return nil, err
}
return &ChannelResolver{C: channel}, nil
}
// Title resolves Log.title
func (r *LogResolver) Title() string {
return r.L.Title
}
// Event resolves Log.event
func (r *LogResolver) Event() string {
return r.L.Event
}
// Description resolves Log.description
func (r *LogResolver) Description() string {
return r.L.Description
}
// Open resolves Log.open
func (r *LogResolver) Open() bool {
return r.L.Open
}
// Characters resolves Log.characters
func (r *LogResolver) Characters(ctx context.Context) ([]*CharacterResolver, error) {
loader := loader.FromContext(ctx)
if loader == nil {
return nil, errors.New("no loader")
}
chars, err := loader.Characters("id", r.L.CharacterIDs...)
if err != nil {
return nil, err
}
resolvers := make([]*CharacterResolver, 0, len(chars))
for i := range chars {
if chars[i].ID == "" {
continue
}
resolvers = append(resolvers, &CharacterResolver{C: chars[i]})
}
return resolvers, nil
}
// Posts resolves Log.posts
func (r *LogResolver) Posts(ctx context.Context, args *LogPostArgs) ([]*PostResolver, error) {
var kinds []string
if args.Kinds != nil {
kinds = *args.Kinds
}
posts, err := r.L.Posts(kinds...)
if err != nil {
return nil, err
}
resolvers := make([]*PostResolver, len(posts))
for i := range posts {
resolvers[i] = &PostResolver{posts[i]}
}
return resolvers, nil
}
// LogPostArgs is an arg
type LogPostArgs struct {
Kinds *[]string
}
-45
View File
@@ -1,45 +0,0 @@
package types
import (
"time"
"git.aiterp.net/rpdata/api/model/log"
)
// PostResolver for the Post graphql type
type PostResolver struct{ P log.Post }
// ID resolves Post.id
func (r *PostResolver) ID() string {
return r.P.ID
}
// LogID resolves Post.logId
func (r *PostResolver) LogID() string {
return r.P.LogID
}
// Time resolves Post.time
func (r *PostResolver) Time() string {
return r.P.Time.Format(time.RFC3339Nano)
}
// Kind resolves Post.logId
func (r *PostResolver) Kind() string {
return r.P.Kind
}
// Nick resolves Post.nick
func (r *PostResolver) Nick() string {
return r.P.Nick
}
// Text resolves Post.text
func (r *PostResolver) Text() string {
return r.P.Text
}
// Position resolves Post.text
func (r *PostResolver) Position() int32 {
return int32(r.P.Position)
}
-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}
}
-80
View File
@@ -1,80 +0,0 @@
package types
import (
"time"
"git.aiterp.net/rpdata/api/model/story"
)
// StoryResolver for the Story graphql type
type StoryResolver struct{ S story.Story }
// ID resolves Story.id
func (r *StoryResolver) ID() string {
return r.S.ID
}
// Author resolves Story.author
func (r *StoryResolver) Author() string {
return r.S.Author
}
// Name resolves Story.name
func (r *StoryResolver) Name() string {
return r.S.Name
}
// Category resolves Story.category
func (r *StoryResolver) Category() string {
return r.S.Category
}
// Open resolves Story.open
func (r *StoryResolver) Open() bool {
return r.S.Open
}
// Listed resolves Story.listed
func (r *StoryResolver) Listed() bool {
return r.S.Listed
}
// Tags resolves Story.tags
func (r *StoryResolver) Tags() []*TagResolver {
resolvers := make([]*TagResolver, len(r.S.Tags))
for i, tag := range r.S.Tags {
resolvers[i] = &TagResolver{T: tag}
}
return resolvers
}
// Chapters resolves Story.chapters
func (r *StoryResolver) Chapters() ([]*ChapterResolver, error) {
chapters, err := r.S.Chapters()
if err != nil {
return nil, err
}
resolvers := make([]*ChapterResolver, len(chapters))
for i, chapter := range chapters {
resolvers[i] = &ChapterResolver{C: chapter}
}
return resolvers, nil
}
// CreatedDate resolves Story.createdDate
func (r *StoryResolver) CreatedDate() string {
return r.S.CreatedDate.Format(time.RFC3339Nano)
}
// FictionalDate resolves Story.fictionalDate
func (r *StoryResolver) FictionalDate() string {
return r.S.FictionalDate.Format(time.RFC3339Nano)
}
// UpdatedDate resolves Story.updatedDate
func (r *StoryResolver) UpdatedDate() string {
return r.S.UpdatedDate.Format(time.RFC3339Nano)
}
-16
View File
@@ -1,16 +0,0 @@
package types
import "git.aiterp.net/rpdata/api/model/story"
// TagResolver for the Tag graphql type
type TagResolver struct{ T story.Tag }
// Kind resolves Tag.kind
func (r *TagResolver) Kind() string {
return r.T.Kind
}
// Name resolves Tag.name
func (r *TagResolver) Name() string {
return r.T.Name
}

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