Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b117aea7f0 | ||
|
|
33ac47a12c | ||
|
|
18a83073e8 | ||
|
|
f14386eceb | ||
|
|
6748ff8bc2 | ||
|
|
e4761ad4a0 | ||
|
|
21b7b44159 | ||
|
|
663a95c70d | ||
|
|
525191a0f9 | ||
|
|
410e07a6a2 | ||
|
|
9aa7304982 | ||
|
|
0f06a3750f | ||
|
|
0bc86ced98 | ||
|
|
c5ea598ea1 | ||
|
|
b6b7b8f1b6 | ||
|
|
e9b00b61fd | ||
|
|
c9084287a4 |
+15
-1
@@ -10,6 +10,20 @@ steps:
|
|||||||
- go generate ./...
|
- go generate ./...
|
||||||
- go test -v ./...
|
- go test -v ./...
|
||||||
|
|
||||||
|
# - name: postgres-migrate
|
||||||
|
# image: golang:1.15
|
||||||
|
# depends_on: []
|
||||||
|
# environment:
|
||||||
|
# DB_CONNECT:
|
||||||
|
# from_secret: db_connect
|
||||||
|
# commands:
|
||||||
|
# - go get -u github.com/pressly/goose/...
|
||||||
|
# - cd migrations/postgres
|
||||||
|
# - goose postgres "$DB_CONNECT" up
|
||||||
|
# when:
|
||||||
|
# event:
|
||||||
|
# - tag
|
||||||
|
|
||||||
- name: docker-release-tag
|
- name: docker-release-tag
|
||||||
image: plugins/docker
|
image: plugins/docker
|
||||||
settings:
|
settings:
|
||||||
@@ -36,4 +50,4 @@ steps:
|
|||||||
registry: r.vmaple.dev
|
registry: r.vmaple.dev
|
||||||
when:
|
when:
|
||||||
event:
|
event:
|
||||||
- tag
|
- tag
|
||||||
|
|||||||
@@ -14,5 +14,9 @@ rpdata-*
|
|||||||
/main.exe
|
/main.exe
|
||||||
|
|
||||||
/graph2/graphcore/*_gen.go
|
/graph2/graphcore/*_gen.go
|
||||||
|
/database/postgres/psqlcore/*.go
|
||||||
|
!/database/postgres/psqlcore/package.go
|
||||||
generated.gql
|
generated.gql
|
||||||
generated.go
|
generated.go
|
||||||
|
/dump.zip
|
||||||
|
/rpdata-dump-v1/
|
||||||
|
|||||||
+4
-2
@@ -1,6 +1,6 @@
|
|||||||
## 1. Build Server
|
## 1. Build Server
|
||||||
# Use Go 1.14
|
# Use Go 1.14
|
||||||
FROM golang:1.14 AS build-server
|
FROM golang:1.16 AS build-server
|
||||||
|
|
||||||
# Load repository into docker
|
# Load repository into docker
|
||||||
WORKDIR /project/
|
WORKDIR /project/
|
||||||
@@ -12,10 +12,12 @@ RUN go generate ./...
|
|||||||
# Build server and tools (CGO disabled to make them 100% static)
|
# Build server and tools (CGO disabled to make them 100% static)
|
||||||
ENV CGO_ENABLED 0
|
ENV CGO_ENABLED 0
|
||||||
RUN go build -installsuffix cgo -ldflags="-s -w" -o /binaries/rpdata-server ./cmd/rpdata-server
|
RUN go build -installsuffix cgo -ldflags="-s -w" -o /binaries/rpdata-server ./cmd/rpdata-server
|
||||||
|
RUN go build -installsuffix cgo -ldflags="-s -w" -o /binaries/rpdata-dump ./cmd/rpdata-dump
|
||||||
|
RUN go build -installsuffix cgo -ldflags="-s -w" -o /binaries/rpdata-restore ./cmd/rpdata-restore
|
||||||
|
|
||||||
## 2. Distribute
|
## 2. Distribute
|
||||||
# Use alpine linux
|
# Use alpine linux
|
||||||
FROM alpine:3.12
|
FROM alpine:3.13
|
||||||
|
|
||||||
# Install certificates to enable HTTPS GET requests, and tzdata for log imports.
|
# Install certificates to enable HTTPS GET requests, and tzdata for log imports.
|
||||||
RUN apk add --no-cache ca-certificates tzdata
|
RUN apk add --no-cache ca-certificates tzdata
|
||||||
|
|||||||
@@ -0,0 +1,270 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"git.aiterp.net/rpdata/api/database"
|
||||||
|
"git.aiterp.net/rpdata/api/database/mongodb"
|
||||||
|
"git.aiterp.net/rpdata/api/internal/config"
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var flagDriver = flag.String("driver", "mongodb", "The database driver to use.")
|
||||||
|
var flagHost = flag.String("host", "127.0.0.1", "The host to connect to.")
|
||||||
|
var flagPort = flag.Int("port", 27017, "The port to connect on.")
|
||||||
|
var flagDb = flag.String("db", "rpdata", "The database name")
|
||||||
|
var flagUsername = flag.String("username", "", "")
|
||||||
|
var flagPassword = flag.String("password", "", "")
|
||||||
|
var flagMechanism = flag.String("mechanism", "", "")
|
||||||
|
var flagOutputFile = flag.String("outfile", "dump.zip", "The file to write to.")
|
||||||
|
var flagIncludeKeys = flag.Bool("include-keys", false, "Whether to include the keys.")
|
||||||
|
var flagIncludeUsers = flag.Bool("include-users", false, "Whether to include the keys.")
|
||||||
|
var flagIncludeUnlisted = flag.Bool("include-unlisted", false, "Whether to include unlisted stuff.")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
trueValue := true
|
||||||
|
truePtr := &trueValue
|
||||||
|
|
||||||
|
cfg := config.Database{
|
||||||
|
Driver: *flagDriver,
|
||||||
|
Host: *flagHost,
|
||||||
|
Port: *flagPort,
|
||||||
|
Db: *flagDb,
|
||||||
|
Username: *flagUsername,
|
||||||
|
Password: *flagPassword,
|
||||||
|
Mechanism: *flagMechanism,
|
||||||
|
}
|
||||||
|
|
||||||
|
blacklist := make(map[string]bool)
|
||||||
|
|
||||||
|
mongodb.DisableFixes = true
|
||||||
|
|
||||||
|
db, err := database.Init(cfg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to open database:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.OpenFile(*flagOutputFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to open output file:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
zipWriter := zip.NewWriter(file)
|
||||||
|
defer func() {
|
||||||
|
err = zipWriter.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to close output file:", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*30)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
log.Println("Dumping characters...")
|
||||||
|
characters, err := db.Characters().List(ctx, models.CharacterFilter{})
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to get characters:", err)
|
||||||
|
}
|
||||||
|
if len(characters) == 0 {
|
||||||
|
log.Println("No characters to dump.")
|
||||||
|
}
|
||||||
|
for i, character := range characters {
|
||||||
|
err := writeJsonFile(zipWriter, "character", i, character.ID, time.Now(), character)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to write character", character.ID, ":", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Dumping channels...")
|
||||||
|
channels, err := db.Channels().List(ctx, models.ChannelFilter{})
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to get channels:", err)
|
||||||
|
}
|
||||||
|
if len(channels) == 0 {
|
||||||
|
log.Println("No channels to dump.")
|
||||||
|
}
|
||||||
|
for i, channel := range channels {
|
||||||
|
err := writeJsonFile(zipWriter, "channel", i, channel.Name, time.Now(), channel)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to write channel", channel.Name, ":", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Dumping changes...")
|
||||||
|
changes, err := db.Changes().List(ctx, models.ChangeFilter{})
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to get changes:", err)
|
||||||
|
}
|
||||||
|
if len(changes) == 0 {
|
||||||
|
log.Println("No changes to dump.")
|
||||||
|
}
|
||||||
|
for i, change := range changes {
|
||||||
|
if !*flagIncludeUnlisted && !change.Listed {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err := writeJsonFile(zipWriter, "change", i, change.ID, change.Date, change)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to write change", change.ID, ":", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Dumping stories...")
|
||||||
|
stories, err := db.Stories().List(ctx, models.StoryFilter{})
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to get stories:", err)
|
||||||
|
}
|
||||||
|
unlistedStories, err := db.Stories().List(ctx, models.StoryFilter{Unlisted: truePtr})
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to get unlisted stories:", err)
|
||||||
|
} else {
|
||||||
|
stories = append(stories, unlistedStories...)
|
||||||
|
}
|
||||||
|
if len(stories) == 0 {
|
||||||
|
log.Println("No stories to dump.")
|
||||||
|
}
|
||||||
|
for i, story := range stories {
|
||||||
|
if !*flagIncludeUnlisted && !story.Listed {
|
||||||
|
blacklist[story.ID] = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err := writeJsonFile(zipWriter, "story", i, story.ID, story.CreatedDate, story)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to write story", story.ID, ":", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Dumping chapters...")
|
||||||
|
chapters, err := db.Chapters().List(ctx, models.ChapterFilter{})
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to get chapters:", err)
|
||||||
|
}
|
||||||
|
if len(chapters) == 0 {
|
||||||
|
log.Println("No chapters to dump.")
|
||||||
|
}
|
||||||
|
for i, chapter := range chapters {
|
||||||
|
if !*flagIncludeUnlisted && blacklist[chapter.StoryID] {
|
||||||
|
blacklist[chapter.ID] = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err := writeJsonFile(zipWriter, "chapter", i, chapter.ID, chapter.CreatedDate, chapter)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to write chapter", chapter.ID, ":", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Dumping comments...")
|
||||||
|
comments, err := db.Comments().List(ctx, models.CommentFilter{})
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to get comments:", err)
|
||||||
|
}
|
||||||
|
if len(comments) == 0 {
|
||||||
|
log.Println("No comments to dump.")
|
||||||
|
}
|
||||||
|
for i, comment := range comments {
|
||||||
|
if !*flagIncludeUnlisted && blacklist[comment.ChapterID] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err := writeJsonFile(zipWriter, "comment", i, comment.ID, comment.CreatedDate, comment)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to write comment", comment.ID, ":", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Dumping posts...")
|
||||||
|
posts, err := db.Posts().List(ctx, models.PostFilter{})
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to get posts:", err)
|
||||||
|
}
|
||||||
|
if len(posts) == 0 {
|
||||||
|
log.Println("No posts to dump.")
|
||||||
|
}
|
||||||
|
for i, post := range posts {
|
||||||
|
err := writeJsonFile(zipWriter, "post", i, post.ID, post.Time, post)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to write post", post.ID, ":", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Dumping logs...")
|
||||||
|
logs, err := db.Logs().List(ctx, models.LogFilter{})
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to get logs:", err)
|
||||||
|
}
|
||||||
|
if len(logs) == 0 {
|
||||||
|
log.Println("No logs to dump.")
|
||||||
|
}
|
||||||
|
for i, logEntry := range logs {
|
||||||
|
err := writeJsonFile(zipWriter, "log", i, logEntry.ID, logEntry.Date, logEntry)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to write post", logEntry.ID, ":", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if *flagIncludeUsers {
|
||||||
|
log.Println("Dumping users...")
|
||||||
|
users, err := db.Users().List(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to get users:", err)
|
||||||
|
}
|
||||||
|
if len(users) == 0 {
|
||||||
|
log.Println("No users to dump.")
|
||||||
|
}
|
||||||
|
for i, userEntry := range users {
|
||||||
|
err := writeJsonFile(zipWriter, "user", i, userEntry.ID, time.Now(), userEntry)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to write user", userEntry.ID, ":", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if *flagIncludeKeys {
|
||||||
|
log.Println("Dumping keys...")
|
||||||
|
keys, err := db.Keys().List(ctx, models.KeyFilter{})
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to get users:", err)
|
||||||
|
}
|
||||||
|
if len(keys) == 0 {
|
||||||
|
log.Println("No users to dump.")
|
||||||
|
}
|
||||||
|
for i, keyEntry := range keys {
|
||||||
|
err := writeJsonFile(zipWriter, "key", i, keyEntry.ID, time.Now(), keyEntry)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to write key", keyEntry.ID, ":", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeJsonFile(zw *zip.Writer, model string, idx int, id string, t time.Time, data interface{}) error {
|
||||||
|
w, err := zw.CreateHeader(&zip.FileHeader{
|
||||||
|
Name: fmt.Sprintf("rpdata_dump_v1/%s/%06d_%s.json", model, idx, id),
|
||||||
|
Modified: t,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.NewEncoder(w).Encode(data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,318 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"git.aiterp.net/rpdata/api/database"
|
||||||
|
"git.aiterp.net/rpdata/api/database/mongodb"
|
||||||
|
"git.aiterp.net/rpdata/api/internal/config"
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var flagDriver = flag.String("driver", "postgres", "The database driver to use.")
|
||||||
|
var flagHost = flag.String("host", "127.0.0.1", "The host to connect to.")
|
||||||
|
var flagPort = flag.Int("port", 5432, "The port to connect on.")
|
||||||
|
var flagDb = flag.String("db", "rpdata", "The database name")
|
||||||
|
var flagUsername = flag.String("username", "", "")
|
||||||
|
var flagPassword = flag.String("password", "", "")
|
||||||
|
var flagMechanism = flag.String("mechanism", "", "")
|
||||||
|
var flagInputFile = flag.String("infile", "dump.zip", "The file to read from.")
|
||||||
|
var flagIncludeKeys = flag.Bool("include-keys", false, "Whether to include the keys.")
|
||||||
|
var flagReplace = flag.Bool("replace", false, "Replace existing content")
|
||||||
|
var flagOnly = flag.String("only", "", "Only restore these models (e.g. \"log,post\")")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
cfg := config.Database{
|
||||||
|
Driver: *flagDriver,
|
||||||
|
Host: *flagHost,
|
||||||
|
Port: *flagPort,
|
||||||
|
Db: *flagDb,
|
||||||
|
Username: *flagUsername,
|
||||||
|
Password: *flagPassword,
|
||||||
|
Mechanism: *flagMechanism,
|
||||||
|
RestoreIDs: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var only map[string]bool
|
||||||
|
if *flagOnly != "" {
|
||||||
|
only = make(map[string]bool)
|
||||||
|
|
||||||
|
for _, part := range strings.Split(*flagOnly, ",") {
|
||||||
|
only[strings.Trim(part, " \t\n\r")] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mongodb.DisableFixes = true
|
||||||
|
|
||||||
|
db, err := database.Init(cfg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to open database:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg2 := cfg
|
||||||
|
cfg2.RestoreIDs = false
|
||||||
|
|
||||||
|
zipReader, err := zip.OpenReader(*flagInputFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to open input file:", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
err = zipReader.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to close input file:", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*30)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
postMap := make(map[string][]*models.Post)
|
||||||
|
|
||||||
|
for _, file := range zipReader.File {
|
||||||
|
if strings.HasSuffix(file.Name, "/") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(file.Name, "/")
|
||||||
|
if len(parts) < 3 || parts[0] != "rpdata_dump_v1" {
|
||||||
|
log.Fatalln("Unrecognized file path:", file.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if only != nil && !only[parts[1]] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
reader, err := file.Open()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Unrecognized file:", file.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hideList := make(map[string]bool)
|
||||||
|
|
||||||
|
log.Println("Loaded", parts[1], parts[2], "from archive.")
|
||||||
|
|
||||||
|
switch parts[1] {
|
||||||
|
case "character":
|
||||||
|
{
|
||||||
|
character := models.Character{}
|
||||||
|
err := json.NewDecoder(reader).Decode(&character)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Could not parse character:", parts[2], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *flagReplace {
|
||||||
|
_ = db.Characters().Delete(ctx, character)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Characters().Insert(ctx, character)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Could not insert character:", parts[2], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Character", character.Name, "inserted.")
|
||||||
|
}
|
||||||
|
case "channel":
|
||||||
|
{
|
||||||
|
channel := models.Channel{}
|
||||||
|
err := json.NewDecoder(reader).Decode(&channel)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Could not parse channel:", parts[2], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *flagReplace {
|
||||||
|
_ = db.Channels().Remove(ctx, channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Channels().Insert(ctx, channel)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Could not insert channel:", parts[2], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Channel", channel.Name, "inserted.")
|
||||||
|
}
|
||||||
|
case "change":
|
||||||
|
{
|
||||||
|
change := models.Change{}
|
||||||
|
err := json.NewDecoder(reader).Decode(&change)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Could not parse character:", parts[2], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *flagReplace {
|
||||||
|
_ = db.Changes().Remove(ctx, change)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Changes().Insert(ctx, change)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Could not insert character:", parts[2], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if change.Listed {
|
||||||
|
log.Println("Change", change.ID, "inserted.")
|
||||||
|
} else {
|
||||||
|
log.Println("Unlisted change inserted.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "story":
|
||||||
|
{
|
||||||
|
story := models.Story{}
|
||||||
|
err := json.NewDecoder(reader).Decode(&story)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Could not parse story:", parts[2], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *flagReplace {
|
||||||
|
_ = db.Stories().Delete(ctx, story)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Stories().Insert(ctx, story)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Could not insert story:", parts[2], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if story.Listed {
|
||||||
|
log.Println("Story", story.Name, "inserted.")
|
||||||
|
} else {
|
||||||
|
log.Println("Unlisted story inserted.")
|
||||||
|
hideList[story.ID] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "chapter":
|
||||||
|
{
|
||||||
|
chapter := models.Chapter{}
|
||||||
|
err := json.NewDecoder(reader).Decode(&chapter)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Could not parse story:", parts[2], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *flagReplace {
|
||||||
|
_ = db.Chapters().Delete(ctx, chapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Chapters().Insert(ctx, chapter)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Could not insert story:", parts[2], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hideList[chapter.StoryID] {
|
||||||
|
log.Println("Chapter", fmt.Sprintf("%s (id: %s)", chapter.Title, chapter.ID), "inserted.")
|
||||||
|
} else {
|
||||||
|
log.Println("Unlisted chapter inserted.")
|
||||||
|
hideList[chapter.ID] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "comment":
|
||||||
|
{
|
||||||
|
comment := models.Comment{}
|
||||||
|
err := json.NewDecoder(reader).Decode(&comment)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Could not parse story:", parts[2], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *flagReplace {
|
||||||
|
_ = db.Comments().Delete(ctx, comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Comments().Insert(ctx, comment)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Could not insert story:", parts[2], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hideList[comment.ChapterID] {
|
||||||
|
log.Println("Comment", comment.Subject, "inserted.")
|
||||||
|
} else {
|
||||||
|
log.Println("Unlisted comment inserted.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case "log":
|
||||||
|
{
|
||||||
|
logg := models.Log{}
|
||||||
|
err := json.NewDecoder(reader).Decode(&logg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Could not parse log:", parts[2], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *flagReplace {
|
||||||
|
_ = db.Logs().Delete(ctx, logg)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Logs().Insert(ctx, logg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Could not insert log:", parts[2], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Log", logg.Date.Format(time.RFC3339)[:16], logg.ChannelName, "inserted.")
|
||||||
|
}
|
||||||
|
case "post":
|
||||||
|
{
|
||||||
|
post := models.Post{}
|
||||||
|
err := json.NewDecoder(reader).Decode(&post)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Could not parse post:", parts[2], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
postMap[post.LogID] = append(postMap[post.LogID], &post)
|
||||||
|
}
|
||||||
|
|
||||||
|
case "user":
|
||||||
|
{
|
||||||
|
user := models.User{}
|
||||||
|
err := json.NewDecoder(reader).Decode(&user)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Could not parse post:", parts[2], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Users().Insert(ctx, user)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Could not insert user:", parts[2], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("User", user.ID, "inserted.")
|
||||||
|
}
|
||||||
|
case "key":
|
||||||
|
{
|
||||||
|
if !*flagIncludeKeys {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
key := models.Key{}
|
||||||
|
err := json.NewDecoder(reader).Decode(&key)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Could not parse key:", parts[2], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Keys().Insert(ctx, key)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Could not insert key:", parts[2], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Key", key.ID, "inserted.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = reader.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if only == nil || only["post"] {
|
||||||
|
for _, posts := range postMap {
|
||||||
|
log.Printf("Inserting %d posts for log %s...", len(posts), posts[0].LogID)
|
||||||
|
|
||||||
|
_, err = db.Posts().InsertMany(ctx, posts...)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Could not insert post for logId:", posts[0].LogID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("\tDone!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,20 +3,18 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.aiterp.net/rpdata/api/space"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"runtime/debug"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.aiterp.net/rpdata/api/database"
|
"git.aiterp.net/rpdata/api/database"
|
||||||
"git.aiterp.net/rpdata/api/graph2"
|
"git.aiterp.net/rpdata/api/graph2"
|
||||||
"git.aiterp.net/rpdata/api/internal/config"
|
"git.aiterp.net/rpdata/api/internal/config"
|
||||||
"git.aiterp.net/rpdata/api/internal/instrumentation"
|
"git.aiterp.net/rpdata/api/internal/instrumentation"
|
||||||
"git.aiterp.net/rpdata/api/models"
|
"git.aiterp.net/rpdata/api/models"
|
||||||
"git.aiterp.net/rpdata/api/services"
|
"git.aiterp.net/rpdata/api/services"
|
||||||
|
"git.aiterp.net/rpdata/api/space"
|
||||||
"github.com/99designs/gqlgen/handler"
|
"github.com/99designs/gqlgen/handler"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"runtime/debug"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -54,11 +52,6 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Refresh Characters:", err)
|
log.Println("Refresh Characters:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = serviceBundle.Logs.FixImportDateBug(context.Background())
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Fix date bug:", err)
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go logListedChanges(serviceBundle.Changes)
|
go logListedChanges(serviceBundle.Changes)
|
||||||
@@ -112,13 +105,6 @@ func queryHandler(services *services.Bundle) http.HandlerFunc {
|
|||||||
handler.ResolverMiddleware(instrumentation.ResolverMiddleware()),
|
handler.ResolverMiddleware(instrumentation.ResolverMiddleware()),
|
||||||
)
|
)
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
// >_>
|
|
||||||
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 = services.Auth.RequestWithToken(r)
|
r = services.Auth.RequestWithToken(r)
|
||||||
|
|
||||||
handler.ServeHTTP(w, r)
|
handler.ServeHTTP(w, r)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package database
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"git.aiterp.net/rpdata/api/database/postgres"
|
||||||
|
|
||||||
"git.aiterp.net/rpdata/api/database/mongodb"
|
"git.aiterp.net/rpdata/api/database/mongodb"
|
||||||
"git.aiterp.net/rpdata/api/internal/config"
|
"git.aiterp.net/rpdata/api/internal/config"
|
||||||
@@ -34,6 +35,8 @@ func Init(config config.Database) (Database, error) {
|
|||||||
switch config.Driver {
|
switch config.Driver {
|
||||||
case "mongo", "mgo", "mongodb":
|
case "mongo", "mgo", "mongodb":
|
||||||
return mongodb.Init(config)
|
return mongodb.Init(config)
|
||||||
|
case "postgres", "psql", "pq", "postgressql":
|
||||||
|
return postgres.Connect(config)
|
||||||
default:
|
default:
|
||||||
return nil, ErrDriverUnrecognized
|
return nil, ErrDriverUnrecognized
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-12
@@ -2,17 +2,20 @@ package mongodb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"git.aiterp.net/rpdata/api/models"
|
"git.aiterp.net/rpdata/api/models"
|
||||||
"git.aiterp.net/rpdata/api/repositories"
|
"git.aiterp.net/rpdata/api/repositories"
|
||||||
"github.com/globalsign/mgo"
|
"github.com/globalsign/mgo"
|
||||||
"github.com/globalsign/mgo/bson"
|
"github.com/globalsign/mgo/bson"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type changeRepository struct {
|
type changeRepository struct {
|
||||||
changes *mgo.Collection
|
restoreIDs bool
|
||||||
idCounter *counter
|
changes *mgo.Collection
|
||||||
|
idCounter *counter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *changeRepository) Find(ctx context.Context, id string) (*models.Change, error) {
|
func (r *changeRepository) Find(ctx context.Context, id string) (*models.Change, error) {
|
||||||
@@ -31,9 +34,17 @@ func (r *changeRepository) List(ctx context.Context, filter models.ChangeFilter)
|
|||||||
if filter.EarliestDate != nil && !filter.EarliestDate.IsZero() {
|
if filter.EarliestDate != nil && !filter.EarliestDate.IsZero() {
|
||||||
query["date"] = bson.M{"$gte": *filter.EarliestDate}
|
query["date"] = bson.M{"$gte": *filter.EarliestDate}
|
||||||
}
|
}
|
||||||
|
if filter.LatestDate != nil && !filter.LatestDate.IsZero() {
|
||||||
|
dateBson, ok := query["date"].(bson.M)
|
||||||
|
if !ok {
|
||||||
|
dateBson = bson.M{}
|
||||||
|
}
|
||||||
|
|
||||||
|
dateBson["$gte"] = *filter.EarliestDate
|
||||||
|
}
|
||||||
if len(filter.Keys) > 0 {
|
if len(filter.Keys) > 0 {
|
||||||
query["keys"] = bson.M{"$in": filter.Keys}
|
query["keys"] = bson.M{"$in": filter.Keys}
|
||||||
} else {
|
} else if !r.restoreIDs {
|
||||||
query["listed"] = true
|
query["listed"] = true
|
||||||
}
|
}
|
||||||
if filter.Author != nil && *filter.Author != "" {
|
if filter.Author != nil && *filter.Author != "" {
|
||||||
@@ -58,14 +69,28 @@ func (r *changeRepository) List(ctx context.Context, filter models.ChangeFilter)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *changeRepository) Insert(ctx context.Context, change models.Change) (*models.Change, error) {
|
func (r *changeRepository) Insert(ctx context.Context, change models.Change) (*models.Change, error) {
|
||||||
next, err := r.idCounter.Increment(1)
|
if !r.restoreIDs || change.ID == "" {
|
||||||
if err != nil {
|
next, err := r.idCounter.Increment(1)
|
||||||
return nil, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
change.ID = "Change_" + strconv.Itoa(next)
|
||||||
|
} else {
|
||||||
|
tokens := strings.Split(change.ID, "_")
|
||||||
|
if len(tokens) != 2 || tokens[0] != "Change" {
|
||||||
|
return nil, errors.New("Invalid change ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := strconv.Atoi(tokens[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = r.idCounter.Bump(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
change.ID = "Change_" + strconv.Itoa(next)
|
err := r.changes.Insert(&change)
|
||||||
|
|
||||||
err = r.changes.Insert(&change)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -77,7 +102,7 @@ func (r *changeRepository) Remove(ctx context.Context, change models.Change) err
|
|||||||
return r.changes.RemoveId(change.ID)
|
return r.changes.RemoveId(change.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newChangeRepository(db *mgo.Database) (repositories.ChangeRepository, error) {
|
func newChangeRepository(db *mgo.Database, restoreIDs bool) (repositories.ChangeRepository, error) {
|
||||||
collection := db.C("common.changes")
|
collection := db.C("common.changes")
|
||||||
|
|
||||||
// Delete the old index if it exists.
|
// Delete the old index if it exists.
|
||||||
@@ -103,7 +128,8 @@ func newChangeRepository(db *mgo.Database) (repositories.ChangeRepository, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &changeRepository{
|
return &changeRepository{
|
||||||
changes: collection,
|
restoreIDs: restoreIDs,
|
||||||
idCounter: newCounter(db, "auto_increment", "Change"),
|
changes: collection,
|
||||||
|
idCounter: newCounter(db, "auto_increment", "Change"),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,20 +2,23 @@ package mongodb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"git.aiterp.net/rpdata/api/internal/generate"
|
"git.aiterp.net/rpdata/api/internal/generate"
|
||||||
"git.aiterp.net/rpdata/api/models"
|
"git.aiterp.net/rpdata/api/models"
|
||||||
"git.aiterp.net/rpdata/api/repositories"
|
"git.aiterp.net/rpdata/api/repositories"
|
||||||
"github.com/globalsign/mgo"
|
"github.com/globalsign/mgo"
|
||||||
"github.com/globalsign/mgo/bson"
|
"github.com/globalsign/mgo/bson"
|
||||||
"log"
|
"log"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type chapterRepository struct {
|
type chapterRepository struct {
|
||||||
chapters *mgo.Collection
|
restoreIDs bool
|
||||||
comments *mgo.Collection
|
chapters *mgo.Collection
|
||||||
|
comments *mgo.Collection
|
||||||
}
|
}
|
||||||
|
|
||||||
func newChapterRepository(db *mgo.Database) (repositories.ChapterRepository, error) {
|
func newChapterRepository(db *mgo.Database, restoreIDs bool) (repositories.ChapterRepository, error) {
|
||||||
collection := db.C("story.chapters")
|
collection := db.C("story.chapters")
|
||||||
|
|
||||||
err := collection.EnsureIndexKey("storyId")
|
err := collection.EnsureIndexKey("storyId")
|
||||||
@@ -32,8 +35,9 @@ func newChapterRepository(db *mgo.Database) (repositories.ChapterRepository, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &chapterRepository{
|
return &chapterRepository{
|
||||||
chapters: collection,
|
restoreIDs: restoreIDs,
|
||||||
comments: db.C("story.comments"),
|
chapters: collection,
|
||||||
|
comments: db.C("story.comments"),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +71,13 @@ func (r *chapterRepository) List(ctx context.Context, filter models.ChapterFilte
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *chapterRepository) Insert(ctx context.Context, chapter models.Chapter) (*models.Chapter, error) {
|
func (r *chapterRepository) Insert(ctx context.Context, chapter models.Chapter) (*models.Chapter, error) {
|
||||||
chapter.ID = generate.StoryID()
|
if !r.restoreIDs {
|
||||||
|
chapter.ID = generate.ChapterID()
|
||||||
|
} else {
|
||||||
|
if len(chapter.ID) != len(generate.ChapterID()) && strings.HasPrefix(chapter.ID, "S") {
|
||||||
|
return nil, errors.New("invalid story id")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err := r.chapters.Insert(chapter)
|
err := r.chapters.Insert(chapter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -15,9 +15,10 @@ import (
|
|||||||
type characterRepository struct {
|
type characterRepository struct {
|
||||||
characters *mgo.Collection
|
characters *mgo.Collection
|
||||||
cidCounter *counter
|
cidCounter *counter
|
||||||
|
restoreIDs bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCharacterRepository(db *mgo.Database) (repositories.CharacterRepository, error) {
|
func newCharacterRepository(db *mgo.Database, restoreIDs bool) (repositories.CharacterRepository, error) {
|
||||||
collection := db.C("common.characters")
|
collection := db.C("common.characters")
|
||||||
|
|
||||||
err := collection.EnsureIndexKey("name")
|
err := collection.EnsureIndexKey("name")
|
||||||
@@ -52,6 +53,7 @@ func newCharacterRepository(db *mgo.Database) (repositories.CharacterRepository,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &characterRepository{
|
return &characterRepository{
|
||||||
|
restoreIDs: restoreIDs,
|
||||||
characters: collection,
|
characters: collection,
|
||||||
cidCounter: newCounter(db, "auto_increment", "Character"),
|
cidCounter: newCounter(db, "auto_increment", "Character"),
|
||||||
}, nil
|
}, nil
|
||||||
@@ -150,13 +152,25 @@ func (r *characterRepository) List(ctx context.Context, filter models.CharacterF
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *characterRepository) Insert(ctx context.Context, character models.Character) (*models.Character, error) {
|
func (r *characterRepository) Insert(ctx context.Context, character models.Character) (*models.Character, error) {
|
||||||
nextId, err := r.cidCounter.Increment(1)
|
if !r.restoreIDs {
|
||||||
if err != nil {
|
nextId, err := r.cidCounter.Increment(1)
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
character.ID = "C" + strconv.Itoa(nextId)
|
}
|
||||||
|
character.ID = "C" + strconv.Itoa(nextId)
|
||||||
|
} else {
|
||||||
|
n, err := strconv.Atoi(character.ID[1:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
err = r.characters.Insert(&character)
|
err = r.cidCounter.Bump(n)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := r.characters.Insert(&character)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,19 +2,22 @@ package mongodb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"git.aiterp.net/rpdata/api/internal/generate"
|
"git.aiterp.net/rpdata/api/internal/generate"
|
||||||
"git.aiterp.net/rpdata/api/models"
|
"git.aiterp.net/rpdata/api/models"
|
||||||
"git.aiterp.net/rpdata/api/repositories"
|
"git.aiterp.net/rpdata/api/repositories"
|
||||||
"github.com/globalsign/mgo"
|
"github.com/globalsign/mgo"
|
||||||
"github.com/globalsign/mgo/bson"
|
"github.com/globalsign/mgo/bson"
|
||||||
"log"
|
"log"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type commentRepository struct {
|
type commentRepository struct {
|
||||||
comments *mgo.Collection
|
restoreIDs bool
|
||||||
|
comments *mgo.Collection
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCommentRepository(db *mgo.Database) (repositories.CommentRepository, error) {
|
func newCommentRepository(db *mgo.Database, restoreIDs bool) (repositories.CommentRepository, error) {
|
||||||
collection := db.C("story.comments")
|
collection := db.C("story.comments")
|
||||||
|
|
||||||
err := collection.EnsureIndexKey("chapterId")
|
err := collection.EnsureIndexKey("chapterId")
|
||||||
@@ -31,7 +34,8 @@ func newCommentRepository(db *mgo.Database) (repositories.CommentRepository, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
r := &commentRepository{
|
r := &commentRepository{
|
||||||
comments: collection,
|
restoreIDs: restoreIDs,
|
||||||
|
comments: collection,
|
||||||
}
|
}
|
||||||
|
|
||||||
go r.fixFieldTypo()
|
go r.fixFieldTypo()
|
||||||
@@ -69,7 +73,13 @@ func (r *commentRepository) List(ctx context.Context, filter models.CommentFilte
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *commentRepository) Insert(ctx context.Context, comment models.Comment) (*models.Comment, error) {
|
func (r *commentRepository) Insert(ctx context.Context, comment models.Comment) (*models.Comment, error) {
|
||||||
comment.ID = generate.CommentID()
|
if !r.restoreIDs {
|
||||||
|
comment.ID = generate.CommentID()
|
||||||
|
} else {
|
||||||
|
if len(comment.ID) != len(generate.CommentID()) && strings.HasPrefix(comment.ID, "SSC") {
|
||||||
|
return nil, errors.New("invalid story id")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err := r.comments.Insert(comment)
|
err := r.comments.Insert(comment)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+36
-9
@@ -11,6 +11,8 @@ import (
|
|||||||
"github.com/globalsign/mgo"
|
"github.com/globalsign/mgo"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var DisableFixes bool
|
||||||
|
|
||||||
type MongoDB struct {
|
type MongoDB struct {
|
||||||
session *mgo.Session
|
session *mgo.Session
|
||||||
|
|
||||||
@@ -103,7 +105,7 @@ func Init(cfg config.Database) (*MongoDB, error) {
|
|||||||
|
|
||||||
db := session.DB(cfg.Db)
|
db := session.DB(cfg.Db)
|
||||||
|
|
||||||
characters, err := newCharacterRepository(db)
|
characters, err := newCharacterRepository(db, cfg.RestoreIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
session.Close()
|
session.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -115,19 +117,19 @@ func Init(cfg config.Database) (*MongoDB, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
changes, err := newChangeRepository(db)
|
changes, err := newChangeRepository(db, cfg.RestoreIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
session.Close()
|
session.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
logs, err := newLogRepository(db)
|
logs, err := newLogRepository(db, cfg.RestoreIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
session.Close()
|
session.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
posts, err := newPostRepository(db)
|
posts, err := newPostRepository(db, cfg.RestoreIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
session.Close()
|
session.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -139,25 +141,25 @@ func Init(cfg config.Database) (*MongoDB, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
stories, err := newStoryRepository(db)
|
stories, err := newStoryRepository(db, cfg.RestoreIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
session.Close()
|
session.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
chapters, err := newChapterRepository(db)
|
chapters, err := newChapterRepository(db, cfg.RestoreIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
session.Close()
|
session.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
comments, err := newCommentRepository(db)
|
comments, err := newCommentRepository(db, cfg.RestoreIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
session.Close()
|
session.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
keys, err := newKeyRepository(db)
|
keys, err := newKeyRepository(db, cfg.RestoreIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
session.Close()
|
session.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -169,7 +171,9 @@ func Init(cfg config.Database) (*MongoDB, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
go posts.fixPositions(logs)
|
if !DisableFixes {
|
||||||
|
go posts.fixPositions(logs)
|
||||||
|
}
|
||||||
|
|
||||||
return &MongoDB{
|
return &MongoDB{
|
||||||
session: session,
|
session: session,
|
||||||
@@ -227,6 +231,29 @@ func (c *counter) With(category, name string) *counter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *counter) Bump(amount int) error {
|
||||||
|
id := c.category + "." + c.name
|
||||||
|
|
||||||
|
err := c.coll.Update(bson.M{
|
||||||
|
"_id": id,
|
||||||
|
"value": bson.M{"$lt": amount},
|
||||||
|
}, bson.M{
|
||||||
|
"value": amount,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if err == mgo.ErrNotFound {
|
||||||
|
return c.coll.Insert(bson.M{
|
||||||
|
"_id": id,
|
||||||
|
"value": amount,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *counter) Increment(amount int) (int, error) {
|
func (c *counter) Increment(amount int) (int, error) {
|
||||||
type counterDoc struct {
|
type counterDoc struct {
|
||||||
ID string `bson:"_id"`
|
ID string `bson:"_id"`
|
||||||
|
|||||||
@@ -10,10 +10,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type keyRepository struct {
|
type keyRepository struct {
|
||||||
keys *mgo.Collection
|
restoreIDs bool
|
||||||
|
keys *mgo.Collection
|
||||||
}
|
}
|
||||||
|
|
||||||
func newKeyRepository(db *mgo.Database) (repositories.KeyRepository, error) {
|
func newKeyRepository(db *mgo.Database, restoreIDs bool) (repositories.KeyRepository, error) {
|
||||||
collection := db.C("auth.keys")
|
collection := db.C("auth.keys")
|
||||||
|
|
||||||
err := collection.EnsureIndexKey("user")
|
err := collection.EnsureIndexKey("user")
|
||||||
@@ -21,7 +22,10 @@ func newKeyRepository(db *mgo.Database) (repositories.KeyRepository, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &keyRepository{keys: collection}, nil
|
return &keyRepository{
|
||||||
|
keys: collection,
|
||||||
|
restoreIDs: restoreIDs,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *keyRepository) Find(ctx context.Context, id string) (*models.Key, error) {
|
func (r *keyRepository) Find(ctx context.Context, id string) (*models.Key, error) {
|
||||||
@@ -54,7 +58,9 @@ func (r *keyRepository) List(ctx context.Context, filter models.KeyFilter) ([]*m
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *keyRepository) Insert(ctx context.Context, key models.Key) (*models.Key, error) {
|
func (r *keyRepository) Insert(ctx context.Context, key models.Key) (*models.Key, error) {
|
||||||
key.ID = generate.KeyID()
|
if !r.restoreIDs {
|
||||||
|
key.ID = generate.KeyID()
|
||||||
|
}
|
||||||
|
|
||||||
err := r.keys.Insert(&key)
|
err := r.keys.Insert(&key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+21
-10
@@ -12,14 +12,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type logRepository struct {
|
type logRepository struct {
|
||||||
openMutex sync.Mutex
|
openMutex sync.Mutex
|
||||||
|
restoreIds bool
|
||||||
|
|
||||||
logs *mgo.Collection
|
logs *mgo.Collection
|
||||||
posts *mgo.Collection
|
posts *mgo.Collection
|
||||||
shortIdCounter *counter
|
shortIdCounter *counter
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLogRepository(db *mgo.Database) (*logRepository, error) {
|
func newLogRepository(db *mgo.Database, restoreIds bool) (*logRepository, error) {
|
||||||
logs := db.C("logbot3.logs")
|
logs := db.C("logbot3.logs")
|
||||||
posts := db.C("logbot3.posts")
|
posts := db.C("logbot3.posts")
|
||||||
|
|
||||||
@@ -54,6 +55,7 @@ func newLogRepository(db *mgo.Database) (*logRepository, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &logRepository{
|
return &logRepository{
|
||||||
|
restoreIds: restoreIds,
|
||||||
logs: logs,
|
logs: logs,
|
||||||
posts: posts,
|
posts: posts,
|
||||||
shortIdCounter: newCounter(db, "auto_increment", "Log"),
|
shortIdCounter: newCounter(db, "auto_increment", "Log"),
|
||||||
@@ -123,26 +125,35 @@ func (r *logRepository) List(ctx context.Context, filter models.LogFilter) ([]*m
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *logRepository) Insert(ctx context.Context, log models.Log) (*models.Log, error) {
|
func (r *logRepository) Insert(ctx context.Context, log models.Log) (*models.Log, error) {
|
||||||
nextShortId, err := r.shortIdCounter.Increment(1)
|
if !r.restoreIds || log.ID == "" || log.ShortID == "" {
|
||||||
if err != nil {
|
nextShortId, err := r.shortIdCounter.Increment(1)
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
log.ID = generate.LogID(log)
|
log.ID = generate.LogID(log)
|
||||||
log.ShortID = "L" + strconv.Itoa(nextShortId)
|
log.ShortID = "L" + strconv.Itoa(nextShortId)
|
||||||
|
} else {
|
||||||
|
n, err := strconv.Atoi(log.ShortID[1:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = r.shortIdCounter.Bump(n)
|
||||||
|
}
|
||||||
|
|
||||||
if log.Open {
|
if log.Open {
|
||||||
// There can be only one open log in the same channel.
|
// There can be only one open log in the same channel.
|
||||||
r.openMutex.Lock()
|
r.openMutex.Lock()
|
||||||
defer r.openMutex.Unlock()
|
defer r.openMutex.Unlock()
|
||||||
|
|
||||||
_, err = r.logs.UpdateAll(bson.M{"channel": log.ChannelName, "open": true}, bson.M{"$set": bson.M{"open": false}})
|
_, err := r.logs.UpdateAll(bson.M{"channel": log.ChannelName, "open": true}, bson.M{"$set": bson.M{"open": false}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("Cannot close other logs: " + err.Error())
|
return nil, errors.New("Cannot close other logs: " + err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = r.logs.Insert(&log)
|
err := r.logs.Insert(&log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package mongodb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"git.aiterp.net/rpdata/api/internal/generate"
|
"git.aiterp.net/rpdata/api/internal/generate"
|
||||||
"git.aiterp.net/rpdata/api/models"
|
"git.aiterp.net/rpdata/api/models"
|
||||||
"git.aiterp.net/rpdata/api/repositories"
|
"git.aiterp.net/rpdata/api/repositories"
|
||||||
@@ -14,13 +15,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type postRepository struct {
|
type postRepository struct {
|
||||||
logs *mgo.Collection
|
restoreIDs bool
|
||||||
posts *mgo.Collection
|
logs *mgo.Collection
|
||||||
|
posts *mgo.Collection
|
||||||
|
|
||||||
orderMutex sync.Mutex
|
orderMutex sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPostRepository(db *mgo.Database) (*postRepository, error) {
|
func newPostRepository(db *mgo.Database, restoreIDs bool) (*postRepository, error) {
|
||||||
posts := db.C("logbot3.posts")
|
posts := db.C("logbot3.posts")
|
||||||
|
|
||||||
err := posts.EnsureIndexKey("logId")
|
err := posts.EnsureIndexKey("logId")
|
||||||
@@ -48,8 +50,9 @@ func newPostRepository(db *mgo.Database) (*postRepository, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &postRepository{
|
return &postRepository{
|
||||||
posts: posts,
|
restoreIDs: restoreIDs,
|
||||||
logs: db.C("logbot3.logs"),
|
posts: posts,
|
||||||
|
logs: db.C("logbot3.logs"),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +94,12 @@ func (r *postRepository) List(ctx context.Context, filter models.PostFilter) ([]
|
|||||||
}
|
}
|
||||||
|
|
||||||
posts := make([]*models.Post, 0, 32)
|
posts := make([]*models.Post, 0, 32)
|
||||||
err := r.posts.Find(query).Sort("-logId", "position").Limit(filter.Limit).All(&posts)
|
var err error
|
||||||
|
if filter.LogID != nil {
|
||||||
|
err = r.posts.Find(query).Sort("position").Limit(filter.Limit).All(&posts)
|
||||||
|
} else {
|
||||||
|
err = r.posts.Find(query).Limit(filter.Limit).All(&posts)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == mgo.ErrNotFound {
|
if err == mgo.ErrNotFound {
|
||||||
return []*models.Post{}, nil
|
return []*models.Post{}, nil
|
||||||
@@ -113,7 +121,14 @@ func (r *postRepository) Insert(ctx context.Context, post models.Post) (*models.
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
post.ID = generate.PostID()
|
if !r.restoreIDs {
|
||||||
|
post.ID = generate.PostID()
|
||||||
|
} else {
|
||||||
|
if len(post.ID) != len(generate.PostID()) && strings.HasPrefix(post.ID, "P") {
|
||||||
|
return nil, errors.New("invalid story id")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
post.Position = lastPost.Position + 1 // Position 1 is first position, so this is safe.
|
post.Position = lastPost.Position + 1 // Position 1 is first position, so this is safe.
|
||||||
|
|
||||||
err = r.posts.Insert(post)
|
err = r.posts.Insert(post)
|
||||||
@@ -135,7 +150,9 @@ func (r *postRepository) InsertMany(ctx context.Context, posts ...*models.Post)
|
|||||||
return nil, repositories.ErrParentMismatch
|
return nil, repositories.ErrParentMismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
post.ID = generate.PostID()
|
if !r.restoreIDs || post.ID == "" {
|
||||||
|
post.ID = generate.PostID()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r.orderMutex.Lock()
|
r.orderMutex.Lock()
|
||||||
|
|||||||
@@ -2,21 +2,24 @@ package mongodb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"git.aiterp.net/rpdata/api/internal/generate"
|
"git.aiterp.net/rpdata/api/internal/generate"
|
||||||
"git.aiterp.net/rpdata/api/models"
|
"git.aiterp.net/rpdata/api/models"
|
||||||
"git.aiterp.net/rpdata/api/repositories"
|
"git.aiterp.net/rpdata/api/repositories"
|
||||||
"github.com/globalsign/mgo"
|
"github.com/globalsign/mgo"
|
||||||
"github.com/globalsign/mgo/bson"
|
"github.com/globalsign/mgo/bson"
|
||||||
"log"
|
"log"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type storyRepository struct {
|
type storyRepository struct {
|
||||||
stories *mgo.Collection
|
stories *mgo.Collection
|
||||||
chapters *mgo.Collection
|
chapters *mgo.Collection
|
||||||
comments *mgo.Collection
|
comments *mgo.Collection
|
||||||
|
restoreIDs bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newStoryRepository(db *mgo.Database) (repositories.StoryRepository, error) {
|
func newStoryRepository(db *mgo.Database, restoreIDs bool) (repositories.StoryRepository, error) {
|
||||||
collection := db.C("story.stories")
|
collection := db.C("story.stories")
|
||||||
|
|
||||||
err := collection.EnsureIndexKey("tags")
|
err := collection.EnsureIndexKey("tags")
|
||||||
@@ -41,9 +44,10 @@ func newStoryRepository(db *mgo.Database) (repositories.StoryRepository, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &storyRepository{
|
return &storyRepository{
|
||||||
stories: collection,
|
stories: collection,
|
||||||
chapters: db.C("story.chapters"),
|
restoreIDs: restoreIDs,
|
||||||
comments: db.C("story.comments"),
|
chapters: db.C("story.chapters"),
|
||||||
|
comments: db.C("story.comments"),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +109,13 @@ func (r *storyRepository) List(ctx context.Context, filter models.StoryFilter) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *storyRepository) Insert(ctx context.Context, story models.Story) (*models.Story, error) {
|
func (r *storyRepository) Insert(ctx context.Context, story models.Story) (*models.Story, error) {
|
||||||
story.ID = generate.StoryID()
|
if !r.restoreIDs {
|
||||||
|
story.ID = generate.StoryID()
|
||||||
|
} else {
|
||||||
|
if len(story.ID) != len(generate.StoryID()) && strings.HasPrefix(story.ID, "S") {
|
||||||
|
return nil, errors.New("invalid story id")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err := r.stories.Insert(story)
|
err := r.stories.Insert(story)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"git.aiterp.net/rpdata/api/models"
|
"git.aiterp.net/rpdata/api/models"
|
||||||
"git.aiterp.net/rpdata/api/repositories"
|
"git.aiterp.net/rpdata/api/repositories"
|
||||||
"github.com/globalsign/mgo"
|
"github.com/globalsign/mgo"
|
||||||
|
"github.com/globalsign/mgo/bson"
|
||||||
)
|
)
|
||||||
|
|
||||||
type userRepository struct {
|
type userRepository struct {
|
||||||
@@ -31,6 +32,16 @@ func (r *userRepository) Find(ctx context.Context, id string) (*models.User, err
|
|||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *userRepository) List(ctx context.Context) ([]*models.User, error) {
|
||||||
|
users := make([]*models.User, 0, 8)
|
||||||
|
err := r.users.Find(bson.M{}).All(&users)
|
||||||
|
if err != nil && err != mgo.ErrNotFound {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *userRepository) Insert(ctx context.Context, user models.User) (*models.User, error) {
|
func (r *userRepository) Insert(ctx context.Context, user models.User) (*models.User, error) {
|
||||||
err := r.users.Insert(user)
|
err := r.users.Insert(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,180 @@
|
|||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"git.aiterp.net/rpdata/api/database/postgres/psqlcore"
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type changeRepository struct {
|
||||||
|
insertWithIDs bool
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *changeRepository) Find(ctx context.Context, id string) (*models.Change, error) {
|
||||||
|
row, err := psqlcore.New(r.db).SelectChangeByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.change(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *changeRepository) List(ctx context.Context, filter models.ChangeFilter) ([]*models.Change, error) {
|
||||||
|
params := psqlcore.SelectChangesParams{LimitSize: 0}
|
||||||
|
if filter.Keys != nil {
|
||||||
|
params.FilterKeys = true
|
||||||
|
keys := make([]string, len(filter.Keys))
|
||||||
|
for i, ck := range filter.Keys {
|
||||||
|
keys[i] = ck.String()
|
||||||
|
}
|
||||||
|
params.Keys = keys
|
||||||
|
}
|
||||||
|
if filter.Author != nil {
|
||||||
|
params.FilterAuthor = true
|
||||||
|
params.Author = *filter.Author
|
||||||
|
}
|
||||||
|
if filter.EarliestDate != nil {
|
||||||
|
params.FilterEarliestDate = true
|
||||||
|
params.EarliestDate = filter.EarliestDate.In(time.UTC)
|
||||||
|
}
|
||||||
|
if filter.LatestDate != nil {
|
||||||
|
params.FilterLatestDate = true
|
||||||
|
params.LatestDate = filter.LatestDate.In(time.UTC)
|
||||||
|
}
|
||||||
|
if filter.Limit != nil && *filter.Limit <= 0 {
|
||||||
|
params.LimitSize = int32(*filter.Limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := psqlcore.New(r.db).SelectChanges(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.changes(rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *changeRepository) Insert(ctx context.Context, change models.Change) (*models.Change, error) {
|
||||||
|
tx, err := r.db.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() { _ = tx.Rollback() }()
|
||||||
|
|
||||||
|
q := psqlcore.New(tx)
|
||||||
|
|
||||||
|
if !r.insertWithIDs || change.ID == "" {
|
||||||
|
next, err := q.IncrementCounter(ctx, "data_change_id")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
change.ID = fmt.Sprintf("Change_%d", next)
|
||||||
|
} else {
|
||||||
|
if len(change.ID) < 8 {
|
||||||
|
return nil, errors.New("invalid change id")
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := strconv.Atoi(change.ID[7:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = q.BumpCounter(ctx, psqlcore.BumpCounterParams{ID: "data_change_id", Value: int32(n)})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := r.dataChange(change)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = q.InsertChange(ctx, psqlcore.InsertChangeParams(*data))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &change, tx.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *changeRepository) Remove(ctx context.Context, change models.Change) error {
|
||||||
|
return psqlcore.New(r.db).DeleteChange(ctx, change.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *changeRepository) dataChange(change models.Change) (*psqlcore.DataChange, error) {
|
||||||
|
objectsJSON, err := json.Marshal(change.ChangeObjectSet)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := make([]string, len(change.Keys))
|
||||||
|
for i, ck := range change.Keys {
|
||||||
|
keys[i] = ck.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return &psqlcore.DataChange{
|
||||||
|
ID: change.ID,
|
||||||
|
Model: string(change.Model),
|
||||||
|
Op: change.Op,
|
||||||
|
Author: change.Author,
|
||||||
|
Listed: change.Listed,
|
||||||
|
Date: change.Date.In(time.UTC),
|
||||||
|
Keys: keys,
|
||||||
|
Objects: objectsJSON,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *changeRepository) change(row psqlcore.DataChange) (*models.Change, error) {
|
||||||
|
objects := models.ChangeObjectSet{}
|
||||||
|
|
||||||
|
err := json.Unmarshal(row.Objects, &objects)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := make([]models.ChangeKey, len(row.Keys))
|
||||||
|
for i := range keys {
|
||||||
|
err := keys[i].Decode(row.Keys[i])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
model := models.ChangeModel(row.Model)
|
||||||
|
if !model.IsValid() {
|
||||||
|
return nil, errors.New("invalid model")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &models.Change{
|
||||||
|
ID: row.ID,
|
||||||
|
Model: model,
|
||||||
|
Op: row.Op,
|
||||||
|
Author: row.Author,
|
||||||
|
Listed: row.Listed,
|
||||||
|
Date: row.Date,
|
||||||
|
Keys: keys,
|
||||||
|
ChangeObjectSet: objects,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *changeRepository) changes(rows []psqlcore.DataChange) ([]*models.Change, error) {
|
||||||
|
results := make([]*models.Change, len(rows))
|
||||||
|
for i, row := range rows {
|
||||||
|
change, err := r.change(row)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
results[i] = change
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"git.aiterp.net/rpdata/api/database/postgres/psqlcore"
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type channelRepository struct {
|
||||||
|
insertWithIDs bool
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *channelRepository) Find(ctx context.Context, name string) (*models.Channel, error) {
|
||||||
|
row, err := psqlcore.New(r.db).SelectChannelByName(ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.channel(row), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *channelRepository) List(ctx context.Context, filter models.ChannelFilter) ([]*models.Channel, error) {
|
||||||
|
params := psqlcore.SelectChannelsParams{
|
||||||
|
LimitSize: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
if filter.Names != nil {
|
||||||
|
params.FilterName = true
|
||||||
|
params.Names = filter.Names
|
||||||
|
}
|
||||||
|
if filter.LocationName != nil {
|
||||||
|
params.FilterLocationName = true
|
||||||
|
params.LocationName = *filter.LocationName
|
||||||
|
}
|
||||||
|
if filter.EventName != nil {
|
||||||
|
params.FilterEventName = true
|
||||||
|
params.EventName = *filter.EventName
|
||||||
|
}
|
||||||
|
if filter.Logged != nil {
|
||||||
|
params.FilterLogged = true
|
||||||
|
params.Logged = *filter.Logged
|
||||||
|
}
|
||||||
|
if filter.Limit > 0 {
|
||||||
|
params.LimitSize = int32(filter.Limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := psqlcore.New(r.db).SelectChannels(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.channels(rows), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *channelRepository) Insert(ctx context.Context, channel models.Channel) (*models.Channel, error) {
|
||||||
|
err := psqlcore.New(r.db).InsertChannel(ctx, psqlcore.InsertChannelParams(channel))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &channel, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *channelRepository) Update(ctx context.Context, channel models.Channel, update models.ChannelUpdate) (*models.Channel, error) {
|
||||||
|
channel.ApplyUpdate(update)
|
||||||
|
|
||||||
|
err := psqlcore.New(r.db).UpdateChannel(ctx, psqlcore.UpdateChannelParams{
|
||||||
|
Name: channel.Name,
|
||||||
|
Logged: channel.Logged,
|
||||||
|
Hub: channel.Hub,
|
||||||
|
EventName: channel.EventName,
|
||||||
|
LocationName: channel.LocationName,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &channel, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *channelRepository) Remove(ctx context.Context, channel models.Channel) error {
|
||||||
|
return psqlcore.New(r.db).DeleteChannel(ctx, channel.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *channelRepository) channel(row psqlcore.DataChannel) *models.Channel {
|
||||||
|
return &models.Channel{
|
||||||
|
Name: row.Name,
|
||||||
|
Logged: row.Logged,
|
||||||
|
Hub: row.Hub,
|
||||||
|
EventName: row.EventName,
|
||||||
|
LocationName: row.LocationName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *channelRepository) channels(rows []psqlcore.DataChannel) []*models.Channel {
|
||||||
|
results := make([]*models.Channel, 0, len(rows))
|
||||||
|
for _, row := range rows {
|
||||||
|
results = append(results, r.channel(row))
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"git.aiterp.net/rpdata/api/database/postgres/psqlcore"
|
||||||
|
"git.aiterp.net/rpdata/api/internal/generate"
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type chapterRepository struct {
|
||||||
|
insertWithIDs bool
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *chapterRepository) Find(ctx context.Context, id string) (*models.Chapter, error) {
|
||||||
|
chapter, err := psqlcore.New(r.db).SelectChapter(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.chapter(chapter), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *chapterRepository) List(ctx context.Context, filter models.ChapterFilter) ([]*models.Chapter, error) {
|
||||||
|
params := psqlcore.SelectChaptersParams{
|
||||||
|
StoryID: "",
|
||||||
|
LimitSize: 0,
|
||||||
|
}
|
||||||
|
if filter.StoryID != nil {
|
||||||
|
params.StoryID = *filter.StoryID
|
||||||
|
}
|
||||||
|
if filter.Limit > 0 {
|
||||||
|
params.LimitSize = int32(filter.Limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
chapters, err := psqlcore.New(r.db).SelectChapters(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.chapters(chapters), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *chapterRepository) Insert(ctx context.Context, chapter models.Chapter) (*models.Chapter, error) {
|
||||||
|
if !r.insertWithIDs || len(chapter.ID) < 8 {
|
||||||
|
chapter.ID = generate.ChapterID()
|
||||||
|
}
|
||||||
|
|
||||||
|
err := psqlcore.New(r.db).InsertChapter(ctx, psqlcore.InsertChapterParams{
|
||||||
|
ID: chapter.ID,
|
||||||
|
StoryID: chapter.StoryID,
|
||||||
|
Title: chapter.Title,
|
||||||
|
Author: chapter.Author,
|
||||||
|
Source: chapter.Source,
|
||||||
|
CreatedDate: chapter.CreatedDate.UTC(),
|
||||||
|
FictionalDate: chapter.FictionalDate.UTC(),
|
||||||
|
EditedDate: chapter.EditedDate.UTC(),
|
||||||
|
CommentMode: string(chapter.CommentMode),
|
||||||
|
CommentsLocked: chapter.CommentsLocked,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &chapter, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *chapterRepository) Update(ctx context.Context, chapter models.Chapter, update models.ChapterUpdate) (*models.Chapter, error) {
|
||||||
|
chapter.ApplyUpdate(update)
|
||||||
|
|
||||||
|
err := psqlcore.New(r.db).UpdateChapter(ctx, psqlcore.UpdateChapterParams{
|
||||||
|
Title: chapter.Title,
|
||||||
|
Source: chapter.Source,
|
||||||
|
FictionalDate: chapter.FictionalDate.UTC(),
|
||||||
|
CommentMode: string(chapter.CommentMode),
|
||||||
|
CommentsLocked: chapter.CommentsLocked,
|
||||||
|
ID: chapter.ID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &chapter, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *chapterRepository) Move(ctx context.Context, chapter models.Chapter, _, to models.Story) (*models.Chapter, error) {
|
||||||
|
err := psqlcore.New(r.db).UpdateChapterStoryID(ctx, psqlcore.UpdateChapterStoryIDParams{
|
||||||
|
StoryID: to.ID,
|
||||||
|
ID: chapter.ID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
chapter.StoryID = to.ID
|
||||||
|
|
||||||
|
return &chapter, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *chapterRepository) Delete(ctx context.Context, chapter models.Chapter) error {
|
||||||
|
tx, err := r.db.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() { _ = tx.Rollback() }()
|
||||||
|
q := psqlcore.New(tx)
|
||||||
|
|
||||||
|
err = q.DeleteCommentsByChapterID(ctx, chapter.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = q.DeleteChapter(ctx, chapter.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *chapterRepository) chapter(chapter psqlcore.StoryChapter) *models.Chapter {
|
||||||
|
return &models.Chapter{
|
||||||
|
ID: chapter.ID,
|
||||||
|
StoryID: chapter.StoryID,
|
||||||
|
Title: chapter.Title,
|
||||||
|
Author: chapter.Author,
|
||||||
|
Source: chapter.Source,
|
||||||
|
CreatedDate: chapter.CreatedDate,
|
||||||
|
FictionalDate: chapter.FictionalDate,
|
||||||
|
EditedDate: chapter.EditedDate,
|
||||||
|
CommentMode: models.ChapterCommentMode(chapter.CommentMode),
|
||||||
|
CommentsLocked: chapter.CommentsLocked,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *chapterRepository) chapters(chapters []psqlcore.StoryChapter) []*models.Chapter {
|
||||||
|
results := make([]*models.Chapter, 0, len(chapters))
|
||||||
|
for _, chapter := range chapters {
|
||||||
|
results = append(results, r.chapter(chapter))
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
@@ -0,0 +1,234 @@
|
|||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"git.aiterp.net/rpdata/api/database/postgres/psqlcore"
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrNickConflict = errors.New("nick already in use by another character")
|
||||||
|
|
||||||
|
type characterRepository struct {
|
||||||
|
insertWithIDs bool
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *characterRepository) Find(ctx context.Context, id string) (*models.Character, error) {
|
||||||
|
row, err := psqlcore.New(r.db).SelectCharacterByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.character(row), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *characterRepository) FindNick(ctx context.Context, nick string) (*models.Character, error) {
|
||||||
|
row, err := psqlcore.New(r.db).SelectCharacterByNick(ctx, nick)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.character(psqlcore.SelectCharacterByIDRow(row)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *characterRepository) FindName(ctx context.Context, name string) (*models.Character, error) {
|
||||||
|
row, err := psqlcore.New(r.db).SelectCharacterByName(ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.character(psqlcore.SelectCharacterByIDRow(row)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *characterRepository) List(ctx context.Context, filter models.CharacterFilter) ([]*models.Character, error) {
|
||||||
|
params := psqlcore.SelectCharactersParams{
|
||||||
|
LimitSize: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
if filter.IDs != nil {
|
||||||
|
params.FilterID = true
|
||||||
|
params.Ids = filter.IDs
|
||||||
|
}
|
||||||
|
if filter.Nicks != nil {
|
||||||
|
params.FilterNick = true
|
||||||
|
params.Nicks = filter.Nicks
|
||||||
|
}
|
||||||
|
if filter.Names != nil {
|
||||||
|
params.FilterName = true
|
||||||
|
params.Names = filter.Names
|
||||||
|
}
|
||||||
|
if filter.Author != nil {
|
||||||
|
params.FilterAuthor = true
|
||||||
|
params.Author = *filter.Author
|
||||||
|
}
|
||||||
|
if filter.Search != nil {
|
||||||
|
params.FilterSearch = true
|
||||||
|
params.Search = TSQueryFromSearch(*filter.Search)
|
||||||
|
}
|
||||||
|
if filter.Limit > 0 {
|
||||||
|
params.LimitSize = int32(filter.Limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := psqlcore.New(r.db).SelectCharacters(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.characters(rows), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *characterRepository) Insert(ctx context.Context, character models.Character) (*models.Character, error) {
|
||||||
|
tx, err := r.db.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() { _ = tx.Rollback() }()
|
||||||
|
q := psqlcore.New(tx)
|
||||||
|
|
||||||
|
if !r.insertWithIDs || character.ID == "" {
|
||||||
|
next, err := q.IncrementCounter(ctx, "data_character_id")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
character.ID = fmt.Sprintf("C%d", next)
|
||||||
|
} else {
|
||||||
|
n, err := strconv.Atoi(character.ID[1:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = q.BumpCounter(ctx, psqlcore.BumpCounterParams{ID: "data_character_id", Value: int32(n)})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := q.SelectCharacters(ctx, psqlcore.SelectCharactersParams{
|
||||||
|
FilterNick: true,
|
||||||
|
Nicks: character.Nicks,
|
||||||
|
LimitSize: 1,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to select: %s", err)
|
||||||
|
}
|
||||||
|
if len(rows) != 0 {
|
||||||
|
return nil, ErrNickConflict
|
||||||
|
}
|
||||||
|
|
||||||
|
err = q.InsertCharacter(ctx, psqlcore.InsertCharacterParams{
|
||||||
|
ID: character.ID,
|
||||||
|
Nicks: character.Nicks,
|
||||||
|
Name: character.Name,
|
||||||
|
ShortName: character.ShortName,
|
||||||
|
Author: character.Author,
|
||||||
|
Description: character.Description,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &character, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *characterRepository) Update(ctx context.Context, character models.Character, update models.CharacterUpdate) (*models.Character, error) {
|
||||||
|
character.ApplyUpdate(update)
|
||||||
|
|
||||||
|
err := psqlcore.New(r.db).UpdateCharacter(ctx, psqlcore.UpdateCharacterParams{
|
||||||
|
ID: character.ID,
|
||||||
|
Name: character.Name,
|
||||||
|
ShortName: character.ShortName,
|
||||||
|
Description: character.Description,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &character, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *characterRepository) AddNick(ctx context.Context, character models.Character, nick string) (*models.Character, error) {
|
||||||
|
tx, err := r.db.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() { _ = tx.Rollback() }()
|
||||||
|
|
||||||
|
q := psqlcore.New(tx)
|
||||||
|
|
||||||
|
rows, err := q.SelectCharacters(ctx, psqlcore.SelectCharactersParams{
|
||||||
|
FilterNick: true,
|
||||||
|
Nicks: []string{nick},
|
||||||
|
LimitSize: 1,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(rows) != 0 {
|
||||||
|
return nil, ErrNickConflict
|
||||||
|
}
|
||||||
|
|
||||||
|
err = q.AddCharacterNick(ctx, psqlcore.AddCharacterNickParams{ID: character.ID, Nick: nick})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
character.Nicks = append(character.Nicks, nick)
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &character, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *characterRepository) RemoveNick(ctx context.Context, character models.Character, nick string) (*models.Character, error) {
|
||||||
|
err := psqlcore.New(r.db).RemoveCharacterNick(ctx, psqlcore.RemoveCharacterNickParams{ID: character.ID, Nick: nick})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, nick2 := range character.Nicks {
|
||||||
|
if nick2 == nick {
|
||||||
|
character.Nicks = append(character.Nicks[:i], character.Nicks[i+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &character, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *characterRepository) Delete(ctx context.Context, character models.Character) error {
|
||||||
|
return psqlcore.New(r.db).DeleteCharacter(ctx, character.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *characterRepository) character(row psqlcore.SelectCharacterByIDRow) *models.Character {
|
||||||
|
return &models.Character{
|
||||||
|
ID: strings.Trim(row.ID, " "),
|
||||||
|
Nicks: row.Nicks,
|
||||||
|
Name: row.Name,
|
||||||
|
ShortName: row.ShortName,
|
||||||
|
Author: row.Author,
|
||||||
|
Description: row.Description,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *characterRepository) characters(rows []psqlcore.SelectCharactersRow) []*models.Character {
|
||||||
|
results := make([]*models.Character, 0, len(rows))
|
||||||
|
for _, row := range rows {
|
||||||
|
results = append(results, r.character(psqlcore.SelectCharacterByIDRow(row)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"git.aiterp.net/rpdata/api/database/postgres/psqlcore"
|
||||||
|
"git.aiterp.net/rpdata/api/internal/generate"
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type commentRepository struct {
|
||||||
|
insertWithIDs bool
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *commentRepository) Find(ctx context.Context, id string) (*models.Comment, error) {
|
||||||
|
comment, err := psqlcore.New(r.db).SelectComment(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.comment(comment), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *commentRepository) List(ctx context.Context, filter models.CommentFilter) ([]*models.Comment, error) {
|
||||||
|
params := psqlcore.SelectCommentsParams{
|
||||||
|
ChapterID: "",
|
||||||
|
LimitSize: 0,
|
||||||
|
}
|
||||||
|
if filter.ChapterID != nil {
|
||||||
|
params.ChapterID = *filter.ChapterID
|
||||||
|
}
|
||||||
|
if filter.Limit > 0 {
|
||||||
|
params.LimitSize = int32(filter.Limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
comments, err := psqlcore.New(r.db).SelectComments(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.comments(comments), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *commentRepository) Insert(ctx context.Context, comment models.Comment) (*models.Comment, error) {
|
||||||
|
if !r.insertWithIDs || len(comment.ID) < 8 {
|
||||||
|
comment.ID = generate.CommentID()
|
||||||
|
}
|
||||||
|
|
||||||
|
err := psqlcore.New(r.db).InsertComment(ctx, psqlcore.InsertCommentParams{
|
||||||
|
ID: comment.ID,
|
||||||
|
ChapterID: comment.ChapterID,
|
||||||
|
CharacterID: comment.CharacterID,
|
||||||
|
Subject: comment.Subject,
|
||||||
|
Author: comment.Author,
|
||||||
|
CharacterName: comment.CharacterID,
|
||||||
|
Source: comment.Source,
|
||||||
|
CreatedDate: comment.CreatedDate.UTC(),
|
||||||
|
FictionalDate: comment.FictionalDate.UTC(),
|
||||||
|
EditedDate: comment.EditedDate.UTC(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &comment, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *commentRepository) Update(ctx context.Context, comment models.Comment, update models.CommentUpdate) (*models.Comment, error) {
|
||||||
|
comment.ApplyUpdate(update)
|
||||||
|
|
||||||
|
err := psqlcore.New(r.db).UpdateComment(ctx, psqlcore.UpdateCommentParams{
|
||||||
|
Subject: comment.Subject,
|
||||||
|
Source: comment.Source,
|
||||||
|
FictionalDate: comment.FictionalDate.UTC(),
|
||||||
|
CharacterName: comment.ChapterID,
|
||||||
|
CharacterID: comment.CharacterID,
|
||||||
|
ID: comment.ID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &comment, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *commentRepository) Delete(ctx context.Context, comment models.Comment) error {
|
||||||
|
return psqlcore.New(r.db).DeleteComment(ctx, comment.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *commentRepository) comment(comment psqlcore.StoryComment) *models.Comment {
|
||||||
|
return &models.Comment{
|
||||||
|
ID: comment.ID,
|
||||||
|
ChapterID: comment.ChapterID,
|
||||||
|
Subject: comment.Subject,
|
||||||
|
Author: comment.Author,
|
||||||
|
CharacterName: comment.CharacterName,
|
||||||
|
CharacterID: comment.CharacterID,
|
||||||
|
FictionalDate: comment.FictionalDate,
|
||||||
|
CreatedDate: comment.CreatedDate,
|
||||||
|
EditedDate: comment.EditedDate,
|
||||||
|
Source: comment.Source,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *commentRepository) comments(comments []psqlcore.StoryComment) []*models.Comment {
|
||||||
|
results := make([]*models.Comment, 0, len(comments))
|
||||||
|
for _, comment := range comments {
|
||||||
|
results = append(results, r.comment(comment))
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"git.aiterp.net/rpdata/api/database/postgres/psqlcore"
|
||||||
|
"git.aiterp.net/rpdata/api/internal/config"
|
||||||
|
"git.aiterp.net/rpdata/api/repositories"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Connect(cfg config.Database) (*DB, error) {
|
||||||
|
sslMode := "disable"
|
||||||
|
if cfg.SSL {
|
||||||
|
sslMode = "enable"
|
||||||
|
}
|
||||||
|
psqlInfo := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
|
||||||
|
cfg.Host, cfg.Port, cfg.Username, cfg.Password, cfg.Db, sslMode,
|
||||||
|
)
|
||||||
|
|
||||||
|
timeout, cancel := context.WithTimeout(context.Background(), time.Second*15)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
db, err := sql.Open("postgres", psqlInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := db.PingContext(timeout); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
db.SetMaxIdleConns(32)
|
||||||
|
|
||||||
|
q := psqlcore.New(db)
|
||||||
|
if err := q.EnsureCounter(timeout, "data_character_id"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := q.EnsureCounter(timeout, "data_change_id"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := q.EnsureCounter(timeout, "log_short_id"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DB{
|
||||||
|
db: db,
|
||||||
|
insertWithIDs: cfg.RestoreIDs,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DB struct {
|
||||||
|
insertWithIDs bool
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) Changes() repositories.ChangeRepository {
|
||||||
|
return &changeRepository{insertWithIDs: d.insertWithIDs, db: d.db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) Channels() repositories.ChannelRepository {
|
||||||
|
return &channelRepository{insertWithIDs: d.insertWithIDs, db: d.db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) Characters() repositories.CharacterRepository {
|
||||||
|
return &characterRepository{insertWithIDs: d.insertWithIDs, db: d.db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) Tags() repositories.TagRepository {
|
||||||
|
return &tagRepository{db: d.db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) Logs() repositories.LogRepository {
|
||||||
|
return &logRepository{insertWithIDs: d.insertWithIDs, db: d.db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) Posts() repositories.PostRepository {
|
||||||
|
return &postRepository{insertWithIDs: d.insertWithIDs, db: d.db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) Stories() repositories.StoryRepository {
|
||||||
|
return &storyRepository{insertWithIDs: d.insertWithIDs, db: d.db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) Chapters() repositories.ChapterRepository {
|
||||||
|
return &chapterRepository{insertWithIDs: d.insertWithIDs, db: d.db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) Comments() repositories.CommentRepository {
|
||||||
|
return &commentRepository{insertWithIDs: d.insertWithIDs, db: d.db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) Keys() repositories.KeyRepository {
|
||||||
|
return &keyRepository{insertWithIDs: d.insertWithIDs, db: d.db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) Users() repositories.UserRepository {
|
||||||
|
return &userRepository{db: d.db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) Files() repositories.FileRepository {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DB) Close(context.Context) error {
|
||||||
|
return d.db.Close()
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"git.aiterp.net/rpdata/api/database/postgres/psqlcore"
|
||||||
|
"git.aiterp.net/rpdata/api/internal/generate"
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type keyRepository struct {
|
||||||
|
db *sql.DB
|
||||||
|
insertWithIDs bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *keyRepository) Find(ctx context.Context, id string) (*models.Key, error) {
|
||||||
|
key, err := psqlcore.New(r.db).SelectKey(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &models.Key{
|
||||||
|
ID: key.ID,
|
||||||
|
Name: key.Name,
|
||||||
|
User: key.User,
|
||||||
|
Secret: key.Secret,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *keyRepository) List(ctx context.Context, filter models.KeyFilter) ([]*models.Key, error) {
|
||||||
|
var keys []psqlcore.Key
|
||||||
|
|
||||||
|
if filter.UserID != nil {
|
||||||
|
keys2, err := psqlcore.New(r.db).SelectKeysByUser(ctx, *filter.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
keys = keys2
|
||||||
|
} else {
|
||||||
|
keys2, err := psqlcore.New(r.db).SelectKeys(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
keys = keys2
|
||||||
|
}
|
||||||
|
|
||||||
|
results := make([]*models.Key, 0, len(keys))
|
||||||
|
for _, key := range keys {
|
||||||
|
results = append(results, &models.Key{
|
||||||
|
ID: key.ID,
|
||||||
|
Name: key.Name,
|
||||||
|
User: key.User,
|
||||||
|
Secret: key.Secret,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *keyRepository) Insert(ctx context.Context, key models.Key) (*models.Key, error) {
|
||||||
|
if !r.insertWithIDs || len(key.ID) < 4 {
|
||||||
|
key.ID = generate.KeyID()
|
||||||
|
}
|
||||||
|
|
||||||
|
err := psqlcore.New(r.db).InsertKey(ctx, psqlcore.InsertKeyParams{
|
||||||
|
ID: key.ID,
|
||||||
|
Name: key.Name,
|
||||||
|
UserID: key.User,
|
||||||
|
Secret: key.Secret,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *keyRepository) Delete(ctx context.Context, key models.Key) error {
|
||||||
|
return psqlcore.New(r.db).DeleteKey(ctx, key.ID)
|
||||||
|
}
|
||||||
@@ -0,0 +1,249 @@
|
|||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"git.aiterp.net/rpdata/api/database/postgres/psqlcore"
|
||||||
|
"git.aiterp.net/rpdata/api/internal/generate"
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type logRepository struct {
|
||||||
|
insertWithIDs bool
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *logRepository) Find(ctx context.Context, id string) (*models.Log, error) {
|
||||||
|
log, err := psqlcore.New(r.db).SelectLog(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.log(log), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *logRepository) List(ctx context.Context, filter models.LogFilter) ([]*models.Log, error) {
|
||||||
|
q := psqlcore.New(r.db)
|
||||||
|
params := psqlcore.SelectLogsParams{
|
||||||
|
LimitSize: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
if filter.Search != nil {
|
||||||
|
params.FilterSearch = true
|
||||||
|
params.Search = TSQueryFromSearch(*filter.Search)
|
||||||
|
}
|
||||||
|
if filter.Open != nil {
|
||||||
|
params.FilterOpen = true
|
||||||
|
params.Open = *filter.Open
|
||||||
|
}
|
||||||
|
if len(filter.Characters) > 0 {
|
||||||
|
params.FilterCharacterID = true
|
||||||
|
params.CharacterIds = filter.Characters
|
||||||
|
}
|
||||||
|
if len(filter.Channels) > 0 {
|
||||||
|
params.FilterChannelName = true
|
||||||
|
params.ChannelNames = filter.Channels
|
||||||
|
}
|
||||||
|
if len(filter.Events) > 0 {
|
||||||
|
params.FilterEventName = true
|
||||||
|
params.EventNames = filter.Events
|
||||||
|
}
|
||||||
|
if filter.MinDate != nil {
|
||||||
|
params.FilterEarlistDate = true
|
||||||
|
params.EarliestDate = *filter.MinDate
|
||||||
|
}
|
||||||
|
if filter.MaxDate != nil {
|
||||||
|
params.FilterLastestDate = true
|
||||||
|
params.LatestDate = *filter.MaxDate
|
||||||
|
}
|
||||||
|
if filter.Limit > 0 {
|
||||||
|
params.LimitSize = int32(filter.Limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
logs, err := q.SelectLogs(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.logs(logs), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *logRepository) Insert(ctx context.Context, log models.Log) (*models.Log, error) {
|
||||||
|
tx, err := r.db.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() { _ = tx.Rollback() }()
|
||||||
|
q := psqlcore.New(tx)
|
||||||
|
|
||||||
|
if !r.insertWithIDs || log.ID == "" {
|
||||||
|
log.ID = generate.LogID(log)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !r.insertWithIDs || log.ShortID == "" {
|
||||||
|
next, err := q.IncrementCounter(ctx, "log_short_id")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.ShortID = fmt.Sprintf("L%d", next)
|
||||||
|
} else {
|
||||||
|
n, err := strconv.Atoi(log.ShortID[1:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = q.BumpCounter(ctx, psqlcore.BumpCounterParams{
|
||||||
|
ID: "log_short_id",
|
||||||
|
Value: int32(n),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if log.CharacterIDs == nil {
|
||||||
|
log.CharacterIDs = []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = q.InsertLog(ctx, psqlcore.InsertLogParams{
|
||||||
|
ID: log.ID,
|
||||||
|
ShortID: log.ShortID,
|
||||||
|
Date: log.Date.UTC(),
|
||||||
|
ChannelName: log.ChannelName,
|
||||||
|
EventName: log.EventName,
|
||||||
|
Title: log.Title,
|
||||||
|
Description: log.Description,
|
||||||
|
Open: log.Open,
|
||||||
|
CharacterIds: log.CharacterIDs,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = r.updateTags(log)
|
||||||
|
|
||||||
|
return &log, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *logRepository) Update(ctx context.Context, log models.Log, update models.LogUpdate) (*models.Log, error) {
|
||||||
|
log.ApplyUpdate(update)
|
||||||
|
|
||||||
|
err := psqlcore.New(r.db).UpdateLog(ctx, psqlcore.UpdateLogParams{
|
||||||
|
Title: log.Title,
|
||||||
|
EventName: log.EventName,
|
||||||
|
Description: log.Description,
|
||||||
|
Open: log.Open,
|
||||||
|
CharacterIds: log.CharacterIDs,
|
||||||
|
ID: log.ID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = r.updateTags(log)
|
||||||
|
|
||||||
|
return &log, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *logRepository) Delete(ctx context.Context, log models.Log) error {
|
||||||
|
tx, err := r.db.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() { _ = tx.Rollback() }()
|
||||||
|
q := psqlcore.New(tx)
|
||||||
|
|
||||||
|
err = q.DeleteLog(ctx, log.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = q.DeletePostsByLogShortID(ctx, log.ShortID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *logRepository) log(log psqlcore.SelectLogRow) *models.Log {
|
||||||
|
return &models.Log{
|
||||||
|
ID: log.ID,
|
||||||
|
ShortID: log.ShortID,
|
||||||
|
Date: log.Date,
|
||||||
|
ChannelName: log.ChannelName,
|
||||||
|
EventName: log.EventName,
|
||||||
|
Title: log.Title,
|
||||||
|
Description: log.Description,
|
||||||
|
Open: log.Open,
|
||||||
|
CharacterIDs: log.CharacterIds,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *logRepository) logs(logs []psqlcore.SelectLogsRow) []*models.Log {
|
||||||
|
results := make([]*models.Log, 0, len(logs))
|
||||||
|
for _, log := range logs {
|
||||||
|
results = append(results, r.log(psqlcore.SelectLogRow(log)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *logRepository) updateTags(log models.Log) error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
tx, err := r.db.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() { _ = tx.Rollback() }()
|
||||||
|
q := psqlcore.New(tx)
|
||||||
|
|
||||||
|
err = q.ClearTagsByTarget(ctx, psqlcore.ClearTagsByTargetParams{TargetKind: "Log", TargetID: log.ID})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(log.CharacterIDs) > 0 {
|
||||||
|
err := q.SetCharacterTagsFromIDs(ctx, psqlcore.SetCharacterTagsFromIDsParams{
|
||||||
|
TargetKind: "log",
|
||||||
|
TargetID: log.ID,
|
||||||
|
CharacterIds: log.CharacterIDs,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = q.SetLocationTagFromChannelName(ctx, psqlcore.SetLocationTagFromChannelNameParams{
|
||||||
|
TargetKind: "log",
|
||||||
|
TargetID: log.ID,
|
||||||
|
ChannelName: log.ChannelName,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if log.EventName != "" {
|
||||||
|
err := q.SetTag(ctx, psqlcore.SetTagParams{
|
||||||
|
Tag: "Event:" + log.EventName,
|
||||||
|
TargetKind: "Log",
|
||||||
|
TargetID: log.ID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx.Commit()
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE TABLE data_character (
|
||||||
|
id CHAR(8) NOT NULL PRIMARY KEY,
|
||||||
|
nicks TEXT[] NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
short_name TEXT NOT NULL,
|
||||||
|
author TEXT NOT NULL,
|
||||||
|
description TEXT NOT NULL,
|
||||||
|
ts_vector TSVECTOR NOT NULL
|
||||||
|
);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP TABLE data_character;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE INDEX data_character_index_character_name ON data_character(name);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP INDEX IF EXISTS data_character_index_character_name;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE INDEX data_character_index_character_short_name ON data_character(short_name);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP INDEX IF EXISTS data_character_index_character_short_name;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE INDEX data_character_index_character_author ON data_character(author);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP INDEX IF EXISTS data_character_index_character_author;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE TABLE core_counter (
|
||||||
|
id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
value INTEGER
|
||||||
|
);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP TABLE core_counter;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE INDEX data_character_index_character_nicks on data_character USING GIN (nicks);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP INDEX IF EXISTS data_character_index_character_nicks;
|
||||||
|
-- +goose StatementEnd
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE FUNCTION immutable_array_to_string(arr ANYARRAY, sep TEXT)
|
||||||
|
RETURNS text
|
||||||
|
AS $$
|
||||||
|
SELECT array_to_string(arr, sep);
|
||||||
|
$$
|
||||||
|
LANGUAGE SQL
|
||||||
|
IMMUTABLE;
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP FUNCTION IF EXISTS immutable_array_to_string;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE INDEX data_character_index_search ON data_character USING GIN (ts_vector);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP INDEX IF EXISTS data_character_index_search;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE TABLE data_channel (
|
||||||
|
name TEXT NOT NULL PRIMARY KEY,
|
||||||
|
logged BOOLEAN NOT NULL,
|
||||||
|
hub BOOLEAN NOT NULL,
|
||||||
|
event_name TEXT NOT NULL,
|
||||||
|
location_name TEXT NOT NULL
|
||||||
|
);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP TABLE IF EXISTS data_channel;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE TABLE data_change (
|
||||||
|
id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
model TEXT NOT NULL,
|
||||||
|
op TEXT NOT NULL,
|
||||||
|
author TEXT NOT NULL,
|
||||||
|
listed BOOLEAN NOT NULL,
|
||||||
|
date TIMESTAMP NOT NULL,
|
||||||
|
keys TEXT[] NOT NULL,
|
||||||
|
objects JSONB NOT NULL
|
||||||
|
);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP TABLE IF EXISTS data_change;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE INDEX data_change_index_keys on data_change USING GIN (keys);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP INDEX IF EXISTS data_change_index_keys;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE INDEX data_change_index_date ON data_change (date);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP INDEX IF EXISTS data_change_index_date;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE INDEX data_change_index_author ON data_change (author);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP INDEX IF EXISTS data_change_index_author;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE TABLE common_tag (
|
||||||
|
tag TEXT NOT NULL,
|
||||||
|
target_kind TEXT NOT NULL,
|
||||||
|
target_id TEXT NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (tag, target_kind, target_id)
|
||||||
|
);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP TABLE common_tag;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE INDEX common_tag_index_target ON common_tag (target_kind, target_id);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP INDEX IF EXISTS common_tag_index_target;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE TABLE story (
|
||||||
|
id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
author TEXT NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
category TEXT NOT NULL,
|
||||||
|
open BOOLEAN NOT NULL,
|
||||||
|
listed BOOLEAN NOT NULL,
|
||||||
|
sort_by_fictional_date BOOLEAN NOT NULL,
|
||||||
|
created_date TIMESTAMP NOT NULL,
|
||||||
|
fictional_date TIMESTAMP NOT NULL,
|
||||||
|
updated_date TIMESTAMP NOT NULL
|
||||||
|
);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP TABLE story;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE INDEX story_index_author ON story (author);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP INDEX IF EXISTS story_index_author
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE INDEX story_index_fictional_date ON story (fictional_date);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP INDEX IF EXISTS story_index_fictional_date
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE INDEX story_index_created_date ON story (created_date);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP INDEX IF EXISTS story_index_created_date
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE INDEX story_index_updated_date ON story (updated_date);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP INDEX IF EXISTS story_index_updated_date
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE TABLE story_chapter (
|
||||||
|
id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
story_id TEXT NOT NULL,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
author TEXT NOT NULL,
|
||||||
|
source TEXT NOT NULL,
|
||||||
|
created_date TIMESTAMP NOT NULL,
|
||||||
|
fictional_date TIMESTAMP NOT NULL,
|
||||||
|
edited_date TIMESTAMP NOT NULL,
|
||||||
|
comment_mode TEXT NOT NULL,
|
||||||
|
comments_locked BOOLEAN NOT NULL
|
||||||
|
);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP TABLE story_chapter;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE INDEX story_chapter_index_story_id ON story_chapter (story_id);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP INDEX IF EXISTS story_chapter_index_story_id;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE TABLE story_comment (
|
||||||
|
id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
chapter_id TEXT NOT NULL,
|
||||||
|
character_id TEXT NOT NULL,
|
||||||
|
subject TEXT NOT NULL,
|
||||||
|
author TEXT NOT NULL,
|
||||||
|
character_name TEXT NOT NULL,
|
||||||
|
source TEXT NOT NULL,
|
||||||
|
created_date TIMESTAMP NOT NULL,
|
||||||
|
fictional_date TIMESTAMP NOT NULL,
|
||||||
|
edited_date TIMESTAMP NOT NULL
|
||||||
|
);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP TABLE story_comment;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE INDEX story_comment_index_chapter_id ON story_comment (chapter_id);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP INDEX IF EXISTS story_comment_index_chapter_id;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE TABLE log (
|
||||||
|
id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
short_id TEXT NOT NULL UNIQUE,
|
||||||
|
character_ids TEXT[] NOT NULL,
|
||||||
|
date TIMESTAMP NOT NULL,
|
||||||
|
channel_name TEXT NOT NULL,
|
||||||
|
event_name TEXT NOT NULL,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
description TEXT NOT NULL,
|
||||||
|
open BOOLEAN NOT NULL
|
||||||
|
);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP TABLE log;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE TABLE log_post (
|
||||||
|
id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
log_short_id TEXT NOT NULL,
|
||||||
|
time TIMESTAMP NOT NULL,
|
||||||
|
kind TEXT NOT NULL,
|
||||||
|
nick TEXT NOT NULL,
|
||||||
|
text TEXT NOT NULL,
|
||||||
|
position INT NOT NULL,
|
||||||
|
ts_vector TSVECTOR NOT NULL,
|
||||||
|
|
||||||
|
UNIQUE (log_short_id, position)
|
||||||
|
);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP TABLE log_post;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE INDEX log_post_index_search ON log_post USING GIN (ts_vector);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP INDEX IF EXISTS log_post_index_search;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE INDEX log_index_open ON log (open);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP INDEX IF EXISTS log_index_open
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE INDEX log_index_character_ids on log USING GIN (character_ids);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP INDEX IF EXISTS log_index_character_ids;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE INDEX log_index_channel_name ON log (channel_name);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP INDEX IF EXISTS log_index_channel_name
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE INDEX log_index_event_name ON log (event_name);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP INDEX IF EXISTS log_index_event_name
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE INDEX log_index_date ON log (date);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP INDEX IF EXISTS log_index_date
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE INDEX log_post_index_kind ON log_post (kind);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP INDEX IF EXISTS log_post_index_kind
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE TABLE key (
|
||||||
|
id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
"user" TEXT NOT NULL,
|
||||||
|
secret TEXT NOT NULL
|
||||||
|
);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP TABLE key;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE TABLE "user" (
|
||||||
|
id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
nick TEXT NOT NULL,
|
||||||
|
permissions TEXT[] NOT NULL
|
||||||
|
);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP TABLE "user";
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
ALTER TABLE log ADD COLUMN "ts_vector" TSVECTOR;
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
ALTER TABLE log DROP COLUMN "ts_vector";
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE INDEX log_index_search ON log USING GIN (ts_vector);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP INDEX IF EXISTS log_index_search;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -0,0 +1,303 @@
|
|||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"git.aiterp.net/rpdata/api/database/postgres/psqlcore"
|
||||||
|
"git.aiterp.net/rpdata/api/internal/generate"
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type postRepository struct {
|
||||||
|
insertWithIDs bool
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *postRepository) Find(ctx context.Context, id string) (*models.Post, error) {
|
||||||
|
post, err := psqlcore.New(r.db).SelectPost(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.post(post), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *postRepository) List(ctx context.Context, filter models.PostFilter) ([]*models.Post, error) {
|
||||||
|
q := psqlcore.New(r.db)
|
||||||
|
params := psqlcore.SelectPostsParams{LimitSize: 0}
|
||||||
|
if filter.LogID != nil {
|
||||||
|
params.FilterLogShortID = true
|
||||||
|
|
||||||
|
if !strings.HasPrefix(*filter.LogID, "L") {
|
||||||
|
log, err := q.SelectLog(ctx, *filter.LogID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
params.LogShortID = log.ShortID
|
||||||
|
} else {
|
||||||
|
params.LogShortID = *filter.LogID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if filter.Kinds != nil {
|
||||||
|
params.FilterKinds = true
|
||||||
|
params.Kinds = filter.Kinds
|
||||||
|
}
|
||||||
|
if filter.Search != nil {
|
||||||
|
params.FilterSearch = true
|
||||||
|
params.Search = TSQueryFromSearch(*filter.Search)
|
||||||
|
}
|
||||||
|
if filter.Limit > 0 {
|
||||||
|
params.LimitSize = int32(filter.Limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
posts, err := q.SelectPosts(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.posts(posts), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *postRepository) Insert(ctx context.Context, post models.Post) (*models.Post, error) {
|
||||||
|
if !r.insertWithIDs || len(post.ID) < 8 {
|
||||||
|
post.ID = generate.PostID()
|
||||||
|
}
|
||||||
|
|
||||||
|
position, err := psqlcore.New(r.db).InsertPost(ctx, psqlcore.InsertPostParams{
|
||||||
|
ID: post.ID,
|
||||||
|
LogShortID: post.LogID,
|
||||||
|
Time: post.Time.UTC(),
|
||||||
|
Kind: post.Kind,
|
||||||
|
Nick: post.Nick,
|
||||||
|
Text: post.Text,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = psqlcore.New(r.db).GenerateLogTSVector(ctx, post.LogID)
|
||||||
|
|
||||||
|
post.Position = int(position)
|
||||||
|
|
||||||
|
return &post, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *postRepository) InsertMany(ctx context.Context, posts ...*models.Post) ([]*models.Post, error) {
|
||||||
|
if len(posts) == 0 {
|
||||||
|
return []*models.Post{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
allowedLogID := ""
|
||||||
|
for _, post := range posts {
|
||||||
|
if allowedLogID == "" {
|
||||||
|
allowedLogID = post.LogID
|
||||||
|
} else if allowedLogID != post.LogID {
|
||||||
|
return nil, errors.New("cannot insert multiple posts with different log IDs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params := psqlcore.InsertPostsParams{
|
||||||
|
LogShortID: posts[0].LogID,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, post := range posts {
|
||||||
|
if !r.insertWithIDs || len(post.ID) < 8 {
|
||||||
|
post.ID = generate.PostID()
|
||||||
|
}
|
||||||
|
|
||||||
|
params.Ids = append(params.Ids, post.ID)
|
||||||
|
params.Kinds = append(params.Kinds, post.Kind)
|
||||||
|
params.Nicks = append(params.Nicks, post.Nick)
|
||||||
|
params.Offsets = append(params.Offsets, int32(len(params.Offsets)+1))
|
||||||
|
params.Times = append(params.Times, post.Time.UTC())
|
||||||
|
params.Texts = append(params.Texts, post.Text)
|
||||||
|
}
|
||||||
|
|
||||||
|
offset, err := psqlcore.New(r.db).InsertPosts(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, post := range posts {
|
||||||
|
post.Position = int(offset) + i
|
||||||
|
}
|
||||||
|
|
||||||
|
err = psqlcore.New(r.db).GenerateLogTSVector(ctx, posts[0].LogID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return posts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *postRepository) Update(ctx context.Context, post models.Post, update models.PostUpdate) (*models.Post, error) {
|
||||||
|
post.ApplyUpdate(update)
|
||||||
|
|
||||||
|
err := psqlcore.New(r.db).UpdatePost(ctx, psqlcore.UpdatePostParams{
|
||||||
|
Time: post.Time,
|
||||||
|
Kind: post.Kind,
|
||||||
|
Nick: post.Nick,
|
||||||
|
Text: post.Text,
|
||||||
|
ID: post.ID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = psqlcore.New(r.db).GenerateLogTSVector(ctx, post.LogID)
|
||||||
|
|
||||||
|
return &post, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *postRepository) Move(ctx context.Context, post models.Post, position int) ([]*models.Post, error) {
|
||||||
|
if position == post.Position {
|
||||||
|
return r.List(ctx, models.PostFilter{LogID: &post.LogID})
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := r.db.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() { _ = tx.Rollback() }()
|
||||||
|
|
||||||
|
_, err = tx.Exec("LOCK TABLE log_post IN SHARE UPDATE EXCLUSIVE MODE")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var lowest, highest int32
|
||||||
|
|
||||||
|
q := psqlcore.New(tx)
|
||||||
|
|
||||||
|
err = q.MovePost(ctx, psqlcore.MovePostParams{ID: post.ID, Position: -1})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if position > post.Position {
|
||||||
|
lowest = int32(post.Position)
|
||||||
|
highest = int32(position)
|
||||||
|
|
||||||
|
err := q.ShiftPostsBetween(ctx, psqlcore.ShiftPostsBetweenParams{
|
||||||
|
ShiftOffset: -1,
|
||||||
|
LogShortID: post.LogID,
|
||||||
|
FromPosition: lowest + 1,
|
||||||
|
ToPosition: highest,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lowest = int32(position)
|
||||||
|
highest = int32(post.Position)
|
||||||
|
|
||||||
|
err := q.ShiftPostsBetween(ctx, psqlcore.ShiftPostsBetweenParams{
|
||||||
|
ShiftOffset: 1,
|
||||||
|
LogShortID: post.LogID,
|
||||||
|
FromPosition: lowest,
|
||||||
|
ToPosition: highest - 1,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = q.MovePost(ctx, psqlcore.MovePostParams{ID: post.ID, Position: int32(position)})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
posts, err := q.SelectPostsByPositionRange(ctx, psqlcore.SelectPostsByPositionRangeParams{
|
||||||
|
LogShortID: post.LogID,
|
||||||
|
FromPosition: lowest,
|
||||||
|
ToPosition: highest,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
positions, err := q.SelectPositionsByLogShortID(ctx, post.LogID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
prev := int32(0)
|
||||||
|
for _, pos := range positions {
|
||||||
|
if pos != prev+1 {
|
||||||
|
return nil, errors.New("post discontinuity detected")
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = pos
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.posts2(posts), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *postRepository) Delete(ctx context.Context, post models.Post) error {
|
||||||
|
tx, err := r.db.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() { _ = tx.Rollback() }()
|
||||||
|
q := psqlcore.New(tx)
|
||||||
|
|
||||||
|
_, err = tx.Exec("LOCK TABLE log_post IN SHARE UPDATE EXCLUSIVE MODE")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = q.DeletePost(ctx, post.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = q.ShiftPostsAfter(ctx, psqlcore.ShiftPostsAfterParams{
|
||||||
|
ShiftOffset: -1,
|
||||||
|
LogShortID: post.LogID,
|
||||||
|
FromPosition: int32(post.Position + 1),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = q.GenerateLogTSVector(ctx, post.LogID)
|
||||||
|
|
||||||
|
return tx.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *postRepository) post(post psqlcore.SelectPostRow) *models.Post {
|
||||||
|
return &models.Post{
|
||||||
|
ID: post.ID,
|
||||||
|
LogID: post.LogShortID,
|
||||||
|
Time: post.Time,
|
||||||
|
Kind: post.Kind,
|
||||||
|
Nick: post.Nick,
|
||||||
|
Text: post.Text,
|
||||||
|
Position: int(post.Position),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *postRepository) posts(posts []psqlcore.SelectPostsRow) []*models.Post {
|
||||||
|
results := make([]*models.Post, 0, len(posts))
|
||||||
|
for _, post := range posts {
|
||||||
|
results = append(results, r.post(psqlcore.SelectPostRow(post)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *postRepository) posts2(posts []psqlcore.SelectPostsByPositionRangeRow) []*models.Post {
|
||||||
|
results := make([]*models.Post, 0, len(posts))
|
||||||
|
for _, post := range posts {
|
||||||
|
results = append(results, r.post(psqlcore.SelectPostRow(post)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
//go:generate go run github.com/kyleconroy/sqlc/cmd/sqlc generate -f ../../../sqlc.yaml
|
||||||
|
|
||||||
|
// Package psqlcore contains the generated code for the PostgresSQL drivers. It's generated using sqlc.
|
||||||
|
package psqlcore
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
-- name: SelectChangeByID :one
|
||||||
|
SELECT * FROM data_change WHERE id = $1 LIMIT 1;
|
||||||
|
|
||||||
|
-- name: SelectChanges :many
|
||||||
|
SELECT * FROM data_change
|
||||||
|
WHERE (@filter_keys::bool = false OR keys && (@keys::text[]))
|
||||||
|
AND (@filter_earliest_date::bool = false OR date >= @earliest_date::timestamp)
|
||||||
|
AND (@filter_latest_date::bool = false OR date <= @latest_date::timestamp)
|
||||||
|
AND (@filter_author::bool = false OR author = @author::text)
|
||||||
|
ORDER BY date DESC
|
||||||
|
LIMIT NULLIF(@limit_size::INT, 0);
|
||||||
|
|
||||||
|
-- name: InsertChange :exec
|
||||||
|
INSERT INTO data_change (id, model, op, author, listed, date, keys, objects)
|
||||||
|
VALUES (
|
||||||
|
@id::text, @model::text, @op::text, @author::text,
|
||||||
|
@listed::boolean, @date::timestamp, @keys::text[],
|
||||||
|
@objects::jsonb
|
||||||
|
);
|
||||||
|
|
||||||
|
-- name: DeleteChange :exec
|
||||||
|
DELETE FROM data_change WHERE id = $1;
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
-- name: SelectChannelByName :one
|
||||||
|
SELECT * FROM data_channel WHERE name = $1 LIMIT 1;
|
||||||
|
|
||||||
|
-- name: InsertChannel :exec
|
||||||
|
INSERT INTO data_channel (name, logged, hub, event_name, location_name)
|
||||||
|
VALUES (
|
||||||
|
@name::text,
|
||||||
|
@logged::boolean, @hub::boolean,
|
||||||
|
@event_name::text, @location_name::text
|
||||||
|
);
|
||||||
|
|
||||||
|
-- name: SelectChannels :many
|
||||||
|
SELECT * FROM data_channel
|
||||||
|
WHERE (@filter_name::bool = false OR name = ANY(@names::text[]))
|
||||||
|
AND (@filter_logged::bool = false OR logged = @logged)
|
||||||
|
AND (@filter_event_name::bool = false OR event_name = @event_name)
|
||||||
|
AND (@filter_location_name::bool = false OR location_name = @location_name)
|
||||||
|
ORDER BY name
|
||||||
|
LIMIT NULLIF(@limit_size::INT, 0);
|
||||||
|
|
||||||
|
-- name: UpdateChannel :exec
|
||||||
|
UPDATE data_channel
|
||||||
|
SET logged = @logged::boolean,
|
||||||
|
hub = @hub::boolean,
|
||||||
|
event_name = @event_name::text,
|
||||||
|
location_name = @location_name::text
|
||||||
|
WHERE name = @name::text;
|
||||||
|
|
||||||
|
-- name: DeleteChannel :exec
|
||||||
|
DELETE FROM data_channel WHERE name=$1;
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
-- name: SelectChapter :one
|
||||||
|
SELECT * FROM story_chapter WHERE id=$1::TEXT LIMIT 1;
|
||||||
|
|
||||||
|
-- name: SelectChapters :many
|
||||||
|
SELECT * FROM story_chapter
|
||||||
|
WHERE (@story_id::TEXT = '' OR story_id = @story_id::TEXT)
|
||||||
|
ORDER BY created_date
|
||||||
|
LIMIT NULLIF(@limit_size::INT, 0);
|
||||||
|
|
||||||
|
-- name: InsertChapter :exec
|
||||||
|
INSERT INTO story_chapter (id, story_id, title, author, source, created_date, fictional_date, edited_date, comment_mode, comments_locked)
|
||||||
|
VALUES (
|
||||||
|
@id::TEXT, @story_id::TEXT, @title::TEXT, @author::TEXT, @source::TEXT,
|
||||||
|
@created_date::TIMESTAMP, @fictional_date::TIMESTAMP, @edited_date::TIMESTAMP,
|
||||||
|
@comment_mode::TEXT, @comments_locked::BOOLEAN
|
||||||
|
);
|
||||||
|
|
||||||
|
-- name: UpdateChapterStoryID :exec
|
||||||
|
UPDATE story_chapter
|
||||||
|
SET story_id = @story_id::TEXT
|
||||||
|
WHERE id = @id;
|
||||||
|
|
||||||
|
-- name: UpdateChapter :exec
|
||||||
|
UPDATE story_chapter
|
||||||
|
SET title = @title,
|
||||||
|
source = @source,
|
||||||
|
fictional_date = @fictional_date,
|
||||||
|
comment_mode = @comment_mode,
|
||||||
|
comments_locked = @comments_locked
|
||||||
|
WHERE id = @id;
|
||||||
|
|
||||||
|
-- name: DeleteChapter :exec
|
||||||
|
DELETE FROM story_chapter WHERE id=$1;
|
||||||
|
|
||||||
|
-- name: DeleteChaptersByStoryID :exec
|
||||||
|
DELETE FROM story_chapter WHERE story_id=$1;
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
-- name: SelectCharacterByID :one
|
||||||
|
SELECT id, nicks, name, short_name, author, description FROM data_character WHERE id = @id::text;
|
||||||
|
|
||||||
|
-- name: SelectCharacterByNick :one
|
||||||
|
SELECT id, nicks, name, short_name, author, description FROM data_character WHERE nicks <@ ARRAY[@nick::text];
|
||||||
|
|
||||||
|
-- name: SelectCharacterByName :one
|
||||||
|
SELECT id, nicks, name, short_name, author, description FROM data_character WHERE name = @name::text;
|
||||||
|
|
||||||
|
-- name: SelectCharacters :many
|
||||||
|
SELECT id, nicks, name, short_name, author, description FROM data_character
|
||||||
|
WHERE (@filter_id::bool = false OR id = ANY(@ids::text[]))
|
||||||
|
AND (@filter_name::bool = false OR name = ANY(@names::text[]))
|
||||||
|
AND (@filter_nick::bool = false OR nicks && (@nicks::text[]))
|
||||||
|
AND (@filter_author::bool = false OR author = @author::text)
|
||||||
|
AND (@filter_search::bool = false OR "ts_vector" @@ to_tsquery(@search::text))
|
||||||
|
LIMIT NULLIF(@limit_size::INT, 0);
|
||||||
|
|
||||||
|
-- name: InsertCharacter :exec
|
||||||
|
INSERT INTO data_character (id, nicks, name, short_name, author, description, ts_vector) VALUES (
|
||||||
|
@id::text, @nicks::text[], @name::text,
|
||||||
|
@short_name::text, @author::text, @description::text,
|
||||||
|
to_tsvector(
|
||||||
|
'english', @name::text || ' ' || @description::text || ' ' || @author::text || ' '
|
||||||
|
|| immutable_array_to_string(@nicks::text[], ' ')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- name: UpdateCharacter :exec
|
||||||
|
UPDATE data_character
|
||||||
|
SET name = @name::text,
|
||||||
|
short_name = @short_name::text,
|
||||||
|
description = @description::text
|
||||||
|
WHERE id = @id::text;
|
||||||
|
|
||||||
|
-- name: AddCharacterNick :exec
|
||||||
|
UPDATE data_character
|
||||||
|
SET nicks=array_append(nicks, @nick::text)
|
||||||
|
WHERE id = @id::text;
|
||||||
|
|
||||||
|
-- name: RemoveCharacterNick :exec
|
||||||
|
UPDATE data_character
|
||||||
|
SET nicks=array_remove(nicks, @nick::text)
|
||||||
|
WHERE id = @id::text;
|
||||||
|
|
||||||
|
-- name: DeleteCharacter :exec
|
||||||
|
DELETE FROM data_character WHERE id=$1;
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
-- name: SelectComment :one
|
||||||
|
SELECT * FROM story_comment WHERE id=$1;
|
||||||
|
|
||||||
|
-- name: InsertComment :exec
|
||||||
|
INSERT INTO story_comment (id, chapter_id, character_id, subject, author, character_name, source, created_date, fictional_date, edited_date)
|
||||||
|
VALUES (
|
||||||
|
@id, @chapter_id, @character_id,
|
||||||
|
@subject, @author, @character_name, @source,
|
||||||
|
@created_date, @fictional_date, @edited_date
|
||||||
|
);
|
||||||
|
|
||||||
|
-- name: UpdateComment :exec
|
||||||
|
UPDATE story_comment
|
||||||
|
SET subject = @subject,
|
||||||
|
source = @source,
|
||||||
|
fictional_date = @fictional_date,
|
||||||
|
character_name = @character_name,
|
||||||
|
character_id = @character_id
|
||||||
|
WHERE id = @id;
|
||||||
|
|
||||||
|
-- name: SelectComments :many
|
||||||
|
SELECT * FROM story_comment
|
||||||
|
WHERE (@chapter_id = '' OR chapter_id = @chapter_id)
|
||||||
|
LIMIT NULLIF(@limit_size::INT, 0);
|
||||||
|
|
||||||
|
-- name: DeleteComment :exec
|
||||||
|
DELETE FROM story_comment WHERE id = $1;
|
||||||
|
|
||||||
|
-- name: DeleteCommentsByChapterID :exec
|
||||||
|
DELETE FROM story_comment WHERE chapter_id = $1;
|
||||||
|
|
||||||
|
-- name: DeleteCommentsByStoryID :exec
|
||||||
|
DELETE FROM story_comment
|
||||||
|
WHERE chapter_id IN (
|
||||||
|
SELECT id FROM story_chapter WHERE story_id = $1
|
||||||
|
);
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
-- name: EnsureCounter :exec
|
||||||
|
INSERT INTO core_counter (id, value) VALUES (@id::text, 0) ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
-- name: IncrementCounter :one
|
||||||
|
UPDATE core_counter SET value = value + 1 WHERE id = @id::text RETURNING value::int;
|
||||||
|
|
||||||
|
-- name: BumpCounter :exec
|
||||||
|
UPDATE core_counter SET value = @value::int WHERE id = @id::text AND value <= @value::int;
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
-- name: SelectKey :one
|
||||||
|
SELECT * FROM "key" WHERE id=$1 LIMIT 1;
|
||||||
|
|
||||||
|
-- name: SelectKeys :many
|
||||||
|
SELECT * FROM "key";
|
||||||
|
|
||||||
|
-- name: SelectKeysByUser :many
|
||||||
|
SELECT * FROM "key" WHERE "user" = $1;
|
||||||
|
|
||||||
|
-- name: InsertKey :exec
|
||||||
|
INSERT INTO key (id, name, "user", secret)
|
||||||
|
VALUES (@id, @name, @user_id, @secret);
|
||||||
|
|
||||||
|
-- name: DeleteKey :exec
|
||||||
|
DELETE FROM "key" WHERE id = $1;
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
-- name: SelectLog :one
|
||||||
|
SELECT id, short_id, character_ids, date, channel_name, event_name, title, description, open FROM log WHERE id=$1 OR short_id=$1 LIMIT 1;
|
||||||
|
|
||||||
|
-- name: SelectLogs :many
|
||||||
|
SELECT id, short_id, character_ids, date, channel_name, event_name, title, description, open FROM log
|
||||||
|
WHERE (@filter_short_id::BOOL = false OR short_id = ANY(@short_ids::TEXT[]))
|
||||||
|
AND (@filter_character_id::BOOL = false OR character_ids @> (@character_ids::TEXT[]))
|
||||||
|
AND (@filter_channel_name::BOOL = false OR channel_name = ANY(@channel_names::TEXT[]))
|
||||||
|
AND (@filter_event_name::BOOL = false OR event_name = ANY(@event_names::TEXT[]))
|
||||||
|
AND (@filter_open::BOOL = false OR open = @open::BOOL)
|
||||||
|
AND (@filter_earlist_date::BOOL = false OR date >= @earliest_date::TIMESTAMP)
|
||||||
|
AND (@filter_lastest_date::BOOL = false OR date <= @latest_date::TIMESTAMP)
|
||||||
|
AND (@filter_search::BOOL = false OR "ts_vector" @@ to_tsquery(@search::TEXT))
|
||||||
|
ORDER BY date DESC
|
||||||
|
LIMIT NULLIF(@limit_size::INT, 0);
|
||||||
|
|
||||||
|
-- name: InsertLog :exec
|
||||||
|
INSERT INTO log (id, short_id, character_ids, date, channel_name, event_name, title, description, open)
|
||||||
|
VALUES (
|
||||||
|
@id, @short_id, @character_ids, @date, @channel_name, @event_name, @title, @description, @open
|
||||||
|
);
|
||||||
|
|
||||||
|
-- name: UpdateLog :exec
|
||||||
|
UPDATE log
|
||||||
|
SET title = @title,
|
||||||
|
event_name = @event_name,
|
||||||
|
description = @description,
|
||||||
|
open = @open,
|
||||||
|
character_ids = @character_ids
|
||||||
|
WHERE id = @id;
|
||||||
|
|
||||||
|
-- name: GenerateLogTSVector :exec
|
||||||
|
UPDATE log
|
||||||
|
SET "ts_vector" = (
|
||||||
|
SELECT TO_TSVECTOR(
|
||||||
|
immutable_array_to_string(array_agg(text), '\n\n')
|
||||||
|
)
|
||||||
|
FROM log_post
|
||||||
|
WHERE log_short_id = @short_id
|
||||||
|
)
|
||||||
|
WHERE short_id = @short_id;
|
||||||
|
|
||||||
|
-- name: DeleteLog :exec
|
||||||
|
DELETE FROM log WHERE id=$1;
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
-- name: SelectPost :one
|
||||||
|
SELECT id, log_short_id, time, kind, nick, text, position FROM log_post WHERE id=$1 LIMIT 1;
|
||||||
|
|
||||||
|
-- name: SelectLogIDsFromPostSearch :many
|
||||||
|
SELECT DISTINCT(log_short_id) FROM log_post WHERE "ts_vector" @@ to_tsquery(@search::TEXT);
|
||||||
|
|
||||||
|
-- name: SelectPostsByPositionRange :many
|
||||||
|
SELECT id, log_short_id, time, kind, nick, text, position FROM log_post WHERE log_short_id = @log_short_id AND position >= @from_position AND position <= @to_position;
|
||||||
|
|
||||||
|
-- name: SelectPositionsByLogShortID :many
|
||||||
|
SELECT position FROM log_post WHERE log_short_id = $1 ORDER BY position;
|
||||||
|
|
||||||
|
-- name: SelectPosts :many
|
||||||
|
SELECT id, log_short_id, time, kind, nick, text, position FROM log_post
|
||||||
|
WHERE (@filter_ids::BOOL = false OR id = ANY(@ids::TEXT[]))
|
||||||
|
AND (@filter_kinds::BOOL = false OR kind = ANY(@kinds::TEXT[]))
|
||||||
|
AND (@filter_log_short_id::BOOL = false OR log_short_id = @log_short_id)
|
||||||
|
AND (@filter_search::BOOL = false OR "ts_vector" @@ to_tsquery(@search::TEXT))
|
||||||
|
ORDER BY log_short_id, position
|
||||||
|
LIMIT NULLIF(@limit_size::INT, 0);
|
||||||
|
|
||||||
|
-- name: InsertPost :one
|
||||||
|
INSERT INTO log_post (id, log_short_id, time, kind, nick, text, position, ts_vector)
|
||||||
|
SELECT @id, @log_short_id, @time, @kind, @nick, @text, COALESCE(MAX(position), 0)+1, to_tsvector(@nick || ' ' || @text)
|
||||||
|
FROM log_post
|
||||||
|
WHERE log_short_id = @log_short_id
|
||||||
|
RETURNING position;
|
||||||
|
|
||||||
|
-- name: InsertPosts :one
|
||||||
|
INSERT INTO log_post (id, log_short_id, time, kind, nick, text, position, ts_vector)
|
||||||
|
SELECT UNNEST(@ids::TEXT[]), @log_short_id, UNNEST(@times::TIMESTAMP[]), UNNEST(@kinds::TEXT[]),
|
||||||
|
UNNEST(@nicks::TEXT[]), UNNEST(@texts::TEXT[]), COALESCE(MAX(position), 0) + UNNEST(@offsets::INT[]),
|
||||||
|
to_tsvector(UNNEST(@nicks::TEXT[]) || ' ' || UNNEST(@texts::TEXT[]))
|
||||||
|
FROM log_post
|
||||||
|
WHERE log_short_id = @log_short_id
|
||||||
|
RETURNING position;
|
||||||
|
|
||||||
|
-- name: UpdatePost :exec
|
||||||
|
UPDATE log_post
|
||||||
|
SET time = @time,
|
||||||
|
kind = @kind,
|
||||||
|
nick = @nick,
|
||||||
|
text = @text,
|
||||||
|
ts_vector = to_tsvector(@nick || ' ' || @text)
|
||||||
|
WHERE id = @id;
|
||||||
|
|
||||||
|
-- name: MovePost :exec
|
||||||
|
UPDATE log_post SET position = @position WHERE id = @id;
|
||||||
|
|
||||||
|
-- name: ShiftPostsBetween :exec
|
||||||
|
UPDATE log_post SET position = position + @shift_offset::INT WHERE log_short_id = @log_short_id AND position >= @from_position AND position <= @to_position;
|
||||||
|
|
||||||
|
-- name: ShiftPostsAfter :exec
|
||||||
|
UPDATE log_post SET position = position + @shift_offset::INT WHERE log_short_id = @log_short_id AND position >= @from_position;
|
||||||
|
|
||||||
|
-- name: ShiftPostsBefore :exec
|
||||||
|
UPDATE log_post SET position = position + @shift_offset::INT WHERE log_short_id = @log_short_id AND position <= @to_position;
|
||||||
|
|
||||||
|
-- name: DeletePost :exec
|
||||||
|
DELETE FROM log_post WHERE id = @id;
|
||||||
|
|
||||||
|
-- name: DeletePostsByLogShortID :exec
|
||||||
|
DELETE FROM log_post WHERE log_short_id = @log_short_id;
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
-- name: SelectStory :one
|
||||||
|
SELECT * FROM story WHERE id = $1 LIMIT 1;
|
||||||
|
|
||||||
|
-- name: SelectStories :many
|
||||||
|
SELECT * FROM story
|
||||||
|
WHERE (@filter_id::bool = false OR id = ANY(@ids::text[]))
|
||||||
|
AND (@filter_author::bool = false OR author = @author::text)
|
||||||
|
AND (@filter_earlist_fictional_date::bool = false OR fictional_date >= @earliest_fictional_date::timestamp)
|
||||||
|
AND (@filter_lastest_fictional_date::bool = false OR fictional_date <= @latest_fictional_date::timestamp)
|
||||||
|
AND (@filter_category::bool = false OR category = @category::text)
|
||||||
|
AND (@filter_open::bool = false OR open = @open::bool)
|
||||||
|
AND (@filter_listed::bool = false OR listed = @listed::bool)
|
||||||
|
ORDER BY updated_date DESC
|
||||||
|
LIMIT NULLIF(@limit_size::INT, 0);
|
||||||
|
|
||||||
|
-- name: InsertStory :exec
|
||||||
|
INSERT INTO story (id, author, name, category, open, listed, sort_by_fictional_date, created_date, fictional_date, updated_date)
|
||||||
|
VALUES (
|
||||||
|
@id::text, @author::text, @name::text, @category::text,
|
||||||
|
@open::boolean, @listed::boolean, @sort_by_fictional_date::boolean,
|
||||||
|
@created_date::timestamp, @fictional_date::timestamp, @updated_date::timestamp
|
||||||
|
);
|
||||||
|
|
||||||
|
-- name: UpdateStory :exec
|
||||||
|
UPDATE story
|
||||||
|
SET name = @name,
|
||||||
|
category = @category,
|
||||||
|
author = @author,
|
||||||
|
open = @open,
|
||||||
|
listed = @listed,
|
||||||
|
fictional_date = @fictional_date,
|
||||||
|
updated_date = @updated_date,
|
||||||
|
sort_by_fictional_date = @sort_by_fictional_date
|
||||||
|
WHERE id = @id;
|
||||||
|
|
||||||
|
-- name: DeleteStory :exec
|
||||||
|
DELETE FROM story WHERE id=$1;
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
-- name: SetTag :exec
|
||||||
|
INSERT INTO common_tag (tag, target_kind, target_id)
|
||||||
|
VALUES (
|
||||||
|
@tag::TEXT, @target_kind::TEXT, @target_id::TEXT
|
||||||
|
)
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
-- name: SetTags :exec
|
||||||
|
INSERT INTO common_tag (tag, target_kind, target_id)
|
||||||
|
SELECT unnest(@tags::TEXT[]), @target_kind::TEXT as target_kind, @target_id::TEXT as target_id
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
-- name: ClearTag :exec
|
||||||
|
DELETE FROM common_tag
|
||||||
|
WHERE tag = @tag::TEXT
|
||||||
|
AND target_kind = @target_kind::TEXT
|
||||||
|
AND target_id = @target_id::TEXT;
|
||||||
|
|
||||||
|
-- name: ClearTagsByTargetLike :exec
|
||||||
|
DELETE FROM common_tag
|
||||||
|
WHERE target_kind = @target_kind::TEXT
|
||||||
|
AND target_id = @target_id::TEXT
|
||||||
|
AND tag LIKE @tag_like::TEXT;
|
||||||
|
|
||||||
|
-- name: ClearTagsByTarget :exec
|
||||||
|
DELETE FROM common_tag
|
||||||
|
WHERE target_kind = @target_kind::TEXT
|
||||||
|
AND target_id = @target_id::TEXT;
|
||||||
|
|
||||||
|
-- name: SetCharacterTagsFromIDs :exec
|
||||||
|
INSERT INTO common_tag
|
||||||
|
SELECT 'Character:'||name AS tag, @target_kind::TEXT as target_kind, @target_id::TEXT as target_id
|
||||||
|
FROM data_character WHERE id = ANY(@character_ids::TEXT[])
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
-- name: SetLocationTagFromChannelName :exec
|
||||||
|
INSERT INTO common_tag
|
||||||
|
SELECT 'Location:'||location_name AS tag, @target_kind::TEXT as target_kind, @target_id::TEXT as target_id
|
||||||
|
FROM data_channel
|
||||||
|
WHERE name = @channel_name AND location_name != ''
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
-- name: SelectDistinctTags :many
|
||||||
|
SELECT DISTINCT tag FROM common_tag ORDER BY tag;
|
||||||
|
|
||||||
|
-- name: SelectDistinctTagsLike :many
|
||||||
|
SELECT DISTINCT tag FROM common_tag WHERE tag LIKE $1::text ORDER BY tag;
|
||||||
|
|
||||||
|
-- name: SelectTagsByTag :many
|
||||||
|
SELECT * FROM common_tag WHERE tag = @tag_name::text ORDER BY tag;
|
||||||
|
|
||||||
|
-- name: SelectTagsByTags :many
|
||||||
|
SELECT * FROM common_tag WHERE tag = ANY(@tag_names::text[]) ORDER BY tag;
|
||||||
|
|
||||||
|
-- name: SelectTargetsByTags :many
|
||||||
|
SELECT DISTINCT target_id FROM common_tag WHERE tag = ANY(@tag_names::text[]) AND target_kind = @target_kind::text;
|
||||||
|
|
||||||
|
-- name: SelectTagsByTarget :many
|
||||||
|
SELECT * FROM common_tag WHERE target_kind = @target_kind AND target_id = @target_id::text ORDER BY tag;
|
||||||
|
|
||||||
|
-- name: SelectTagsByTargets :many
|
||||||
|
SELECT * FROM common_tag WHERE target_kind = @target_kind AND target_id = ANY(@target_ids::text[]) ORDER BY tag;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
-- name: SelectUser :one
|
||||||
|
SELECT * FROM "user" WHERE id=$1 LIMIT 1;
|
||||||
|
|
||||||
|
-- name: SelectUsers :many
|
||||||
|
SELECT * FROM "user";
|
||||||
|
|
||||||
|
-- name: InsertUser :exec
|
||||||
|
INSERT INTO "user" (id, nick, permissions)
|
||||||
|
VALUES (
|
||||||
|
@id, @nick, @permissions
|
||||||
|
);
|
||||||
@@ -0,0 +1,250 @@
|
|||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"git.aiterp.net/rpdata/api/database/postgres/psqlcore"
|
||||||
|
"git.aiterp.net/rpdata/api/internal/generate"
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type storyRepository struct {
|
||||||
|
insertWithIDs bool
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *storyRepository) Find(ctx context.Context, id string) (*models.Story, error) {
|
||||||
|
q := psqlcore.New(r.db)
|
||||||
|
story, err := q.SelectStory(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tags, err := q.SelectTagsByTarget(ctx, psqlcore.SelectTagsByTargetParams{
|
||||||
|
TargetKind: "Story",
|
||||||
|
TargetID: story.ID,
|
||||||
|
})
|
||||||
|
if err != nil && err != sql.ErrNoRows {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.story(story, tags), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *storyRepository) List(ctx context.Context, filter models.StoryFilter) ([]*models.Story, error) {
|
||||||
|
q := psqlcore.New(r.db)
|
||||||
|
params := psqlcore.SelectStoriesParams{LimitSize: 0}
|
||||||
|
|
||||||
|
if len(filter.Tags) > 0 {
|
||||||
|
targets, err := q.SelectTargetsByTags(ctx, psqlcore.SelectTargetsByTagsParams{
|
||||||
|
TagNames: models.EncodeTagArray(filter.Tags),
|
||||||
|
TargetKind: "Story",
|
||||||
|
})
|
||||||
|
if err != nil && err != sql.ErrNoRows {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
params.FilterID = true
|
||||||
|
params.Ids = targets
|
||||||
|
|
||||||
|
if len(params.Ids) == 0 {
|
||||||
|
return []*models.Story{}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if filter.Author != nil {
|
||||||
|
params.FilterAuthor = true
|
||||||
|
params.Author = *filter.Author
|
||||||
|
}
|
||||||
|
if filter.Category != nil {
|
||||||
|
params.FilterCategory = true
|
||||||
|
params.Category = string(*filter.Category)
|
||||||
|
}
|
||||||
|
if !filter.EarliestFictionalDate.IsZero() {
|
||||||
|
params.FilterEarlistFictionalDate = true
|
||||||
|
params.EarliestFictionalDate = filter.EarliestFictionalDate.UTC()
|
||||||
|
}
|
||||||
|
if !filter.LatestFictionalDate.IsZero() {
|
||||||
|
params.FilterLastestFictionalDate = true
|
||||||
|
params.LatestFictionalDate = filter.LatestFictionalDate.UTC()
|
||||||
|
}
|
||||||
|
if filter.Open != nil {
|
||||||
|
params.FilterOpen = true
|
||||||
|
params.Open = *filter.Open
|
||||||
|
}
|
||||||
|
if filter.Unlisted != nil {
|
||||||
|
params.FilterListed = true
|
||||||
|
params.Listed = !*filter.Unlisted
|
||||||
|
}
|
||||||
|
if filter.Limit > 0 {
|
||||||
|
params.LimitSize = int32(filter.Limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
stories, err := q.SelectStories(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
targetIDs := make([]string, len(stories))
|
||||||
|
for i, story := range stories {
|
||||||
|
targetIDs[i] = story.ID
|
||||||
|
}
|
||||||
|
tags, err := q.SelectTagsByTargets(ctx, psqlcore.SelectTagsByTargetsParams{
|
||||||
|
TargetKind: "Story",
|
||||||
|
TargetIds: targetIDs,
|
||||||
|
})
|
||||||
|
|
||||||
|
return r.stories(stories, tags), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *storyRepository) Insert(ctx context.Context, story models.Story) (*models.Story, error) {
|
||||||
|
tx, err := r.db.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() { _ = tx.Rollback() }()
|
||||||
|
q := psqlcore.New(tx)
|
||||||
|
|
||||||
|
if !r.insertWithIDs || len(story.ID) < 8 {
|
||||||
|
story.ID = generate.StoryID()
|
||||||
|
}
|
||||||
|
|
||||||
|
if story.UpdatedDate.Before(story.CreatedDate) {
|
||||||
|
story.UpdatedDate = story.CreatedDate
|
||||||
|
}
|
||||||
|
|
||||||
|
err = q.InsertStory(ctx, psqlcore.InsertStoryParams{
|
||||||
|
ID: story.ID,
|
||||||
|
Author: story.Author,
|
||||||
|
Name: story.Name,
|
||||||
|
Category: string(story.Category),
|
||||||
|
Open: story.Open,
|
||||||
|
Listed: story.Listed,
|
||||||
|
SortByFictionalDate: story.SortByFictionalDate,
|
||||||
|
CreatedDate: story.CreatedDate.UTC(),
|
||||||
|
FictionalDate: story.FictionalDate.UTC(),
|
||||||
|
UpdatedDate: story.UpdatedDate.UTC(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is inefficient, but tags are very rarely added before the story is submitted.
|
||||||
|
err = q.SetTags(ctx, psqlcore.SetTagsParams{
|
||||||
|
Tags: models.EncodeTagArray(story.Tags),
|
||||||
|
TargetKind: "Story",
|
||||||
|
TargetID: story.ID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &story, tx.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *storyRepository) Update(ctx context.Context, story models.Story, update models.StoryUpdate) (*models.Story, error) {
|
||||||
|
story.ApplyUpdate(update)
|
||||||
|
|
||||||
|
err := psqlcore.New(r.db).UpdateStory(ctx, psqlcore.UpdateStoryParams{
|
||||||
|
Name: story.Name,
|
||||||
|
Category: string(story.Category),
|
||||||
|
Author: story.Author,
|
||||||
|
Open: story.Open,
|
||||||
|
Listed: story.Listed,
|
||||||
|
FictionalDate: story.FictionalDate.UTC(),
|
||||||
|
UpdatedDate: story.UpdatedDate.UTC(),
|
||||||
|
SortByFictionalDate: story.SortByFictionalDate,
|
||||||
|
ID: story.ID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &story, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *storyRepository) AddTag(ctx context.Context, story models.Story, tag models.Tag) error {
|
||||||
|
return psqlcore.New(r.db).SetTag(ctx, psqlcore.SetTagParams{
|
||||||
|
TargetKind: "Story",
|
||||||
|
TargetID: story.ID,
|
||||||
|
Tag: tag.String(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *storyRepository) RemoveTag(ctx context.Context, story models.Story, tag models.Tag) error {
|
||||||
|
return psqlcore.New(r.db).ClearTag(ctx, psqlcore.ClearTagParams{
|
||||||
|
TargetKind: "Story",
|
||||||
|
TargetID: story.ID,
|
||||||
|
Tag: tag.String(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *storyRepository) Delete(ctx context.Context, story models.Story) error {
|
||||||
|
tx, err := r.db.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() { _ = tx.Rollback() }()
|
||||||
|
q := psqlcore.New(tx)
|
||||||
|
|
||||||
|
err = q.ClearTagsByTarget(ctx, psqlcore.ClearTagsByTargetParams{
|
||||||
|
TargetKind: "Story",
|
||||||
|
TargetID: story.ID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = q.DeleteCommentsByStoryID(ctx, story.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = q.DeleteChaptersByStoryID(ctx, story.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = q.DeleteStory(ctx, story.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *storyRepository) story(story psqlcore.Story, tags []psqlcore.CommonTag) *models.Story {
|
||||||
|
storyTags := make([]models.Tag, 0, 8)
|
||||||
|
for _, tag := range tags {
|
||||||
|
if tag.TargetKind == "Story" && tag.TargetID == story.ID {
|
||||||
|
newTag := models.Tag{}
|
||||||
|
err := newTag.Decode(tag.Tag)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
storyTags = append(storyTags, newTag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &models.Story{
|
||||||
|
ID: story.ID,
|
||||||
|
Author: story.Author,
|
||||||
|
Name: story.Name,
|
||||||
|
Category: models.StoryCategory(story.Category),
|
||||||
|
Open: story.Open,
|
||||||
|
Listed: story.Listed,
|
||||||
|
Tags: storyTags,
|
||||||
|
CreatedDate: story.CreatedDate,
|
||||||
|
FictionalDate: story.FictionalDate,
|
||||||
|
UpdatedDate: story.UpdatedDate,
|
||||||
|
SortByFictionalDate: story.SortByFictionalDate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *storyRepository) stories(stories []psqlcore.Story, tags []psqlcore.CommonTag) []*models.Story {
|
||||||
|
results := make([]*models.Story, 0, len(stories))
|
||||||
|
for _, story := range stories {
|
||||||
|
results = append(results, r.story(story, tags))
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"git.aiterp.net/rpdata/api/database/postgres/psqlcore"
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tagRepository struct {
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *tagRepository) Find(ctx context.Context, kind models.TagKind, name string) (*models.Tag, error) {
|
||||||
|
tag, err := psqlcore.New(r.db).SelectTagsByTag(ctx, fmt.Sprintf("%s:%s", kind, name))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tag2 := &models.Tag{}
|
||||||
|
err = tag2.Decode(tag[0].Tag)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tag2, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *tagRepository) List(ctx context.Context, filter models.TagFilter) ([]*models.Tag, error) {
|
||||||
|
var tags []string
|
||||||
|
var err error
|
||||||
|
if filter.Kind != nil {
|
||||||
|
tags, err = psqlcore.New(r.db).SelectDistinctTagsLike(ctx, string(*filter.Kind)+"%")
|
||||||
|
} else {
|
||||||
|
tags, err = psqlcore.New(r.db).SelectDistinctTags(ctx)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return models.DecodeTagArray(tags)
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"git.aiterp.net/rpdata/api/database/postgres/psqlcore"
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type userRepository struct {
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *userRepository) Find(ctx context.Context, id string) (*models.User, error) {
|
||||||
|
user, err := psqlcore.New(r.db).SelectUser(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &models.User{
|
||||||
|
ID: user.ID,
|
||||||
|
Nick: user.Nick,
|
||||||
|
Permissions: user.Permissions,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *userRepository) List(ctx context.Context) ([]*models.User, error) {
|
||||||
|
users, err := psqlcore.New(r.db).SelectUsers(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
results := make([]*models.User, 0, len(users))
|
||||||
|
for _, user := range users {
|
||||||
|
results = append(results, &models.User{
|
||||||
|
ID: user.ID,
|
||||||
|
Nick: user.Nick,
|
||||||
|
Permissions: user.Permissions,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *userRepository) Insert(ctx context.Context, user models.User) (*models.User, error) {
|
||||||
|
err := psqlcore.New(r.db).InsertUser(ctx, psqlcore.InsertUserParams{
|
||||||
|
ID: user.ID,
|
||||||
|
Nick: user.Nick,
|
||||||
|
Permissions: user.Permissions,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package postgres
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// TSQueryFromSearch generates a TS query from a typical search string.
|
||||||
|
func TSQueryFromSearch(search string) string {
|
||||||
|
if strings.HasPrefix(search, "tsq:") {
|
||||||
|
return search[4:]
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens := strings.Split(search, " ")
|
||||||
|
inQuotes := false
|
||||||
|
result := ""
|
||||||
|
|
||||||
|
for i, token := range tokens {
|
||||||
|
clearQuotes := false
|
||||||
|
startQuotes := false
|
||||||
|
|
||||||
|
if strings.HasPrefix(token, "\"") {
|
||||||
|
if len(token) > 1 {
|
||||||
|
token = token[1:]
|
||||||
|
}
|
||||||
|
startQuotes = true
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(token, "\"") {
|
||||||
|
token = token[:len(token)-1]
|
||||||
|
startQuotes = false
|
||||||
|
clearQuotes = true
|
||||||
|
}
|
||||||
|
|
||||||
|
token := strings.Trim(token, ",.;:()\"")
|
||||||
|
if len(token) > 0 {
|
||||||
|
if startQuotes {
|
||||||
|
if i > 0 {
|
||||||
|
result += " & "
|
||||||
|
}
|
||||||
|
|
||||||
|
result += "("
|
||||||
|
} else if inQuotes {
|
||||||
|
result += "<->"
|
||||||
|
} else if i != 0 {
|
||||||
|
result += " & "
|
||||||
|
}
|
||||||
|
|
||||||
|
result += token
|
||||||
|
}
|
||||||
|
|
||||||
|
if startQuotes {
|
||||||
|
inQuotes = true
|
||||||
|
}
|
||||||
|
if clearQuotes {
|
||||||
|
inQuotes = false
|
||||||
|
result += ")"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if inQuotes {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimLeft(result, " ")
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTSQueryFromSearch(t *testing.T) {
|
||||||
|
rows := [][2]string{
|
||||||
|
{`asari matron`, `asari & matron`},
|
||||||
|
{`"asari matron"`, `(asari<->matron)`},
|
||||||
|
{`"asari matron" blue`, `(asari<->matron) & blue`},
|
||||||
|
{`"christmas present" "wrapping paper"`, `(christmas<->present) & (wrapping<->paper)`},
|
||||||
|
{`stuff`, `stuff`},
|
||||||
|
{`"her slowly, rubbing the back of his head awkwardly. "`, `(her<->slowly<->rubbing<->the<->back<->of<->his<->head<->awkwardly)`},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, row := range rows {
|
||||||
|
t.Run(fmt.Sprintf("Row_%d", i), func(t *testing.T) {
|
||||||
|
assert.Equal(t, row[1], TSQueryFromSearch(row[0]))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,32 +4,22 @@ go 1.12
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/99designs/gqlgen v0.9.0
|
github.com/99designs/gqlgen v0.9.0
|
||||||
github.com/beorn7/perks v1.0.0 // indirect
|
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||||
github.com/globalsign/mgo v0.0.0-20180403085842-f76e4f9da92e
|
github.com/globalsign/mgo v0.0.0-20180403085842-f76e4f9da92e
|
||||||
github.com/go-ini/ini v1.35.0 // indirect
|
github.com/go-ini/ini v1.35.0 // indirect
|
||||||
github.com/golang/protobuf v1.3.1 // indirect
|
|
||||||
github.com/google/go-cmp v0.3.0 // indirect
|
|
||||||
github.com/gorilla/websocket v1.4.0 // indirect
|
|
||||||
github.com/h2non/filetype v1.0.8
|
github.com/h2non/filetype v1.0.8
|
||||||
|
github.com/kyleconroy/sqlc v1.7.0
|
||||||
|
github.com/lib/pq v1.10.0
|
||||||
github.com/minio/minio-go v0.0.0-20180409193742-3d2d02921f05
|
github.com/minio/minio-go v0.0.0-20180409193742-3d2d02921f05
|
||||||
github.com/mitchellh/go-homedir v0.0.0-20161203194507-b8bc1bf76747 // indirect
|
github.com/prometheus/client_golang v0.9.3
|
||||||
github.com/prometheus/client_golang v0.9.1
|
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 // indirect
|
|
||||||
github.com/prometheus/common v0.4.0 // indirect
|
|
||||||
github.com/prometheus/procfs v0.0.0-20190517135640-51af30a78b0e // indirect
|
github.com/prometheus/procfs v0.0.0-20190517135640-51af30a78b0e // indirect
|
||||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect
|
github.com/stretchr/testify v1.5.1
|
||||||
github.com/stretchr/testify v1.3.0
|
|
||||||
github.com/tidwall/pretty v1.0.0 // indirect
|
github.com/tidwall/pretty v1.0.0 // indirect
|
||||||
github.com/vektah/dataloaden v0.3.0
|
github.com/vektah/dataloaden v0.3.0
|
||||||
github.com/vektah/gqlparser v1.1.2
|
github.com/vektah/gqlparser v1.1.2
|
||||||
go.mongodb.org/mongo-driver v1.0.3
|
go.mongodb.org/mongo-driver v1.0.3
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 // indirect
|
|
||||||
golang.org/x/net v0.0.0-20190514140710-3ec191127204 // indirect
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58
|
||||||
golang.org/x/text v0.3.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
gopkg.in/ini.v1 v1.42.0 // indirect
|
|
||||||
gopkg.in/yaml.v2 v2.2.2
|
|
||||||
)
|
)
|
||||||
|
|
||||||
replace golang.org/x/net => golang.org/x/net v0.0.0-20180416171110-a35a21de978d
|
replace golang.org/x/net => golang.org/x/net v0.0.0-20180416171110-a35a21de978d
|
||||||
|
|||||||
@@ -1,102 +1,322 @@
|
|||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||||
|
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||||
|
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||||
|
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||||
|
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||||
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
|
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||||
|
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||||
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
|
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||||
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
github.com/99designs/gqlgen v0.9.0 h1:g1arBPML74Vqv0L3Q+TqIhGXLspV+2MYtRLkBxuZrlE=
|
github.com/99designs/gqlgen v0.9.0 h1:g1arBPML74Vqv0L3Q+TqIhGXLspV+2MYtRLkBxuZrlE=
|
||||||
github.com/99designs/gqlgen v0.9.0/go.mod h1:HrrG7ic9EgLPsULxsZh/Ti+p0HNWgR3XRuvnD0pb5KY=
|
github.com/99designs/gqlgen v0.9.0/go.mod h1:HrrG7ic9EgLPsULxsZh/Ti+p0HNWgR3XRuvnD0pb5KY=
|
||||||
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ=
|
github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ=
|
||||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
|
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
|
||||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||||
|
github.com/antlr/antlr4 v0.0.0-20200209180723-1177c0b58d07 h1:ylxsz+1ifp/XBbiaFMqhB5YKshU47EzuZWpUIiH8urY=
|
||||||
|
github.com/antlr/antlr4 v0.0.0-20200209180723-1177c0b58d07/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y=
|
||||||
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||||
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
|
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
|
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||||
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||||
|
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||||
|
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
|
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
|
github.com/cznic/golex v0.0.0-20181122101858-9c343928389c/go.mod h1:+bmmJDNmKlhWNG+gwWCkaBoTy39Fs+bzRxVBzoTQbIc=
|
||||||
|
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
|
||||||
|
github.com/cznic/parser v0.0.0-20160622100904-31edd927e5b1/go.mod h1:2B43mz36vGZNZEwkWi8ayRSSUXLfjL8OkbzwW4NcPMM=
|
||||||
|
github.com/cznic/sortutil v0.0.0-20181122101858-f5f958428db8/go.mod h1:q2w6Bg5jeox1B+QkJ6Wp/+Vn0G/bo3f1uY7Fn3vivIQ=
|
||||||
|
github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc=
|
||||||
|
github.com/cznic/y v0.0.0-20170802143616-045f81c6662a/go.mod h1:1rk5VM7oSnA4vjp+hrLQ3HWHa+Y4yPCa3/CsJrcNnvs=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/globalsign/mgo v0.0.0-20180403085842-f76e4f9da92e h1:hmrbHva/wKD6X4M7pgHVbg/KfVV1wwgr1NEkQaVD3rU=
|
github.com/globalsign/mgo v0.0.0-20180403085842-f76e4f9da92e h1:hmrbHva/wKD6X4M7pgHVbg/KfVV1wwgr1NEkQaVD3rU=
|
||||||
github.com/globalsign/mgo v0.0.0-20180403085842-f76e4f9da92e/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
github.com/globalsign/mgo v0.0.0-20180403085842-f76e4f9da92e/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||||
github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||||
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-ini/ini v1.35.0 h1:D/my3+xOfqZMkJpciRcyqU7XMBUgiZa9qXjZIa8uv2k=
|
github.com/go-ini/ini v1.35.0 h1:D/my3+xOfqZMkJpciRcyqU7XMBUgiZa9qXjZIa8uv2k=
|
||||||
github.com/go-ini/ini v1.35.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
github.com/go-ini/ini v1.35.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
|
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||||
|
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
|
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
|
||||||
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||||
github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||||
github.com/h2non/filetype v1.0.8 h1:le8gpf+FQA0/DlDABbtisA1KiTS0Xi+YSC/E8yY3Y14=
|
github.com/h2non/filetype v1.0.8 h1:le8gpf+FQA0/DlDABbtisA1KiTS0Xi+YSC/E8yY3Y14=
|
||||||
github.com/h2non/filetype v1.0.8/go.mod h1:isekKqOuhMj+s/7r3rIeTErIRy4Rub5uBWHfvMusLMU=
|
github.com/h2non/filetype v1.0.8/go.mod h1:isekKqOuhMj+s/7r3rIeTErIRy4Rub5uBWHfvMusLMU=
|
||||||
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
|
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||||
|
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||||
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
|
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||||
|
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||||
|
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||||
|
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||||
|
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
|
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||||
|
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||||
|
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||||
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
||||||
|
github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk=
|
||||||
|
github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
|
||||||
|
github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
|
||||||
|
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
||||||
|
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||||
|
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
|
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||||
|
github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0=
|
||||||
|
github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po=
|
||||||
|
github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
|
||||||
|
github.com/jackc/pgtype v1.6.2/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||||
|
github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA=
|
||||||
|
github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
|
||||||
|
github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
|
||||||
|
github.com/jackc/pgx/v4 v4.10.1/go.mod h1:QlrWebbs3kqEZPHCTGyxecvzG6tvIsYu+A5b1raylkA=
|
||||||
|
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kyleconroy/sqlc v1.7.0 h1:a6/SblJ/NB48NhymCXsHHJlQzEqUOCKjvVJXTeqLn24=
|
||||||
|
github.com/kyleconroy/sqlc v1.7.0/go.mod h1:BZmkzwSB91So4gfoDpwXoMqni9zfenoF/3Rv4t8NEj0=
|
||||||
|
github.com/lfittl/pg_query_go v1.0.2 h1:adHtAAb0LpoXI3cYqdJTbo5W682+V9vg3/8ONAhnhqE=
|
||||||
|
github.com/lfittl/pg_query_go v1.0.2/go.mod h1:Ch/Oiv5pRI5jf9M7Hvf0XwCvFwa4PWRcNoFcLtVU8pc=
|
||||||
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
|
github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E=
|
||||||
|
github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
|
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
|
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||||
|
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
|
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||||
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
github.com/minio/minio-go v0.0.0-20180409193742-3d2d02921f05 h1:YEyFCqmHcmbcTLqZ7yEAg7VIUat6PuDzGbIctZy888k=
|
github.com/minio/minio-go v0.0.0-20180409193742-3d2d02921f05 h1:YEyFCqmHcmbcTLqZ7yEAg7VIUat6PuDzGbIctZy888k=
|
||||||
github.com/minio/minio-go v0.0.0-20180409193742-3d2d02921f05/go.mod h1:7guKYtitv8dktvNUGrhzmNlA5wrAABTQXCoesZdFQO8=
|
github.com/minio/minio-go v0.0.0-20180409193742-3d2d02921f05/go.mod h1:7guKYtitv8dktvNUGrhzmNlA5wrAABTQXCoesZdFQO8=
|
||||||
github.com/mitchellh/go-homedir v0.0.0-20161203194507-b8bc1bf76747 h1:eQox4Rh4ewJF+mqYPxCkmBAirRnPaHEB26UkNuPyjlk=
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
github.com/mitchellh/go-homedir v0.0.0-20161203194507-b8bc1bf76747/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
|
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||||
|
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||||
|
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
||||||
github.com/opentracing/opentracing-go v1.0.2 h1:3jA2P6O1F9UOrWVpwrIo17pu01KWvNWg4X946/Y5Zwg=
|
|
||||||
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||||
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
|
github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8 h1:USx2/E1bX46VG32FIw034Au6seQ2fY9NEILmNh/UlQg=
|
||||||
|
github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8/go.mod h1:B1+S9LNcuMyLH/4HMTViQOJevkGiik3wW2AN9zb2fNQ=
|
||||||
|
github.com/pingcap/errors v0.11.0/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
||||||
|
github.com/pingcap/errors v0.11.5-0.20201021055732-210aacd3fd99 h1:PVuEvTi/LlviMG7X3av44NRwcdPf0tiqL/YdVOIKCpA=
|
||||||
|
github.com/pingcap/errors v0.11.5-0.20201021055732-210aacd3fd99/go.mod h1:G7x87le1poQzLB/TqvTJI2ILrSgobnq4Ut7luOwvfvI=
|
||||||
|
github.com/pingcap/log v0.0.0-20200511115504-543df19646ad h1:SveG82rmu/GFxYanffxsSF503SiQV+2JLnWEiGiF+Tc=
|
||||||
|
github.com/pingcap/log v0.0.0-20200511115504-543df19646ad/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8=
|
||||||
|
github.com/pingcap/parser v0.0.0-20201024025010-3b2fb4b41d73 h1:MN/e9n6GUBmmi6sKtriW0Dv1dfIohZRoKr/ZU1B4jrM=
|
||||||
|
github.com/pingcap/parser v0.0.0-20201024025010-3b2fb4b41d73/go.mod h1:74+OEdwM4B/jMpBRl92ch6CSmSYkQtv2TNxIjFdT/GE=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v0.9.1 h1:K47Rk0v/fkEfwfQet2KWhscE0cJzjgCCDBG2KHZoVno=
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
|
github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
|
||||||
|
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
|
github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
|
||||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/procfs v0.0.0-20190517135640-51af30a78b0e h1:zK8d1aZ+gw/Ne4uMfZTFRxj08PUOp+gGwm4HWUeGI1k=
|
github.com/prometheus/procfs v0.0.0-20190517135640-51af30a78b0e h1:zK8d1aZ+gw/Ne4uMfZTFRxj08PUOp+gGwm4HWUeGI1k=
|
||||||
github.com/prometheus/procfs v0.0.0-20190517135640-51af30a78b0e/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.0-20190517135640-51af30a78b0e/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||||
|
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||||
|
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||||
|
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||||
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
|
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||||
|
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
|
github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
|
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||||
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
|
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
|
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||||
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
|
github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M=
|
||||||
|
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
|
||||||
|
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
|
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
|
||||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||||
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
|
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
|
||||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U=
|
github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U=
|
||||||
@@ -104,39 +324,174 @@ github.com/vektah/dataloaden v0.3.0 h1:ZfVN2QD6swgvp+tDqdH/OIT/wu3Dhu0cus0k5gIZS
|
|||||||
github.com/vektah/dataloaden v0.3.0/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U=
|
github.com/vektah/dataloaden v0.3.0/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U=
|
||||||
github.com/vektah/gqlparser v1.1.2 h1:ZsyLGn7/7jDNI+y4SEhI4yAxRChlv15pUHMjijT+e68=
|
github.com/vektah/gqlparser v1.1.2 h1:ZsyLGn7/7jDNI+y4SEhI4yAxRChlv15pUHMjijT+e68=
|
||||||
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
|
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
|
||||||
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
|
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||||
|
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
go.mongodb.org/mongo-driver v1.0.3 h1:GKoji1ld3tw2aC+GX1wbr/J2fX13yNacEYoJ8Nhr0yU=
|
go.mongodb.org/mongo-driver v1.0.3 h1:GKoji1ld3tw2aC+GX1wbr/J2fX13yNacEYoJ8Nhr0yU=
|
||||||
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
|
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
|
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||||
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
|
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
|
||||||
|
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||||
|
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
|
||||||
|
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||||
|
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
|
||||||
|
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||||
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||||
|
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||||
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
|
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/net v0.0.0-20180416171110-a35a21de978d h1:O2P57H5Cc+d+DJos+iweraI9rmzMYwV+45vkZdDY0Oo=
|
golang.org/x/net v0.0.0-20180416171110-a35a21de978d h1:O2P57H5Cc+d+DJos+iweraI9rmzMYwV+45vkZdDY0Oo=
|
||||||
golang.org/x/net v0.0.0-20180416171110-a35a21de978d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180416171110-a35a21de978d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5 h1:mzjBh+S5frKOsOBobWIMAbXavqjmgO17k/2puhcFR94=
|
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6 h1:iZgcI2DDp6zW5v9Z/5+f0NuqoxNdmzg4hivjk2WLXpY=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd h1:oMEQDWVXVNpceQoVd1JN3CQ7LYJJzs5qWqZIUcxXHHw=
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191219041853-979b82bfef62 h1:vDaiisQl0rGVXqk3wT2yc43gSnwlj4haEG5J78IGZP4=
|
||||||
|
golang.org/x/tools v0.0.0-20191219041853-979b82bfef62/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
|
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||||
|
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||||
|
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||||
|
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
||||||
|
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||||
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
|
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
sourcegraph.com/sourcegraph/appdash v0.0.0-20180110180208-2cc67fd64755/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
|
sourcegraph.com/sourcegraph/appdash v0.0.0-20180110180208-2cc67fd64755/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
|
||||||
sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67/go.mod h1:L5q+DGLGOQFpo1snNEkLOJT2d1YTW66rWNzatr3He1k=
|
sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67/go.mod h1:L5q+DGLGOQFpo1snNEkLOJT2d1YTW66rWNzatr3He1k=
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func complexity() (cr graphcore.ComplexityRoot) {
|
func complexity() (cr graphcore.ComplexityRoot) {
|
||||||
|
tinySubListComplexity := 2
|
||||||
subListComplexity := 25
|
subListComplexity := 25
|
||||||
bigSublistComplexity := 75
|
bigSublistComplexity := 75
|
||||||
hugeSublistComplexity := 100
|
hugeSublistComplexity := 100
|
||||||
@@ -175,7 +176,7 @@ func complexity() (cr graphcore.ComplexityRoot) {
|
|||||||
return childComplexity + bigSublistComplexity
|
return childComplexity + bigSublistComplexity
|
||||||
}
|
}
|
||||||
cr.Story.Tags = func(childComplexity int) int {
|
cr.Story.Tags = func(childComplexity int) int {
|
||||||
return childComplexity + subListComplexity
|
return childComplexity + tinySubListComplexity
|
||||||
}
|
}
|
||||||
|
|
||||||
cr.Chapter.Comments = func(childComplexity int, limit *int) int {
|
cr.Chapter.Comments = func(childComplexity int, limit *int) int {
|
||||||
|
|||||||
@@ -35,13 +35,15 @@ type Space struct {
|
|||||||
|
|
||||||
// Database is configuration for spaces.
|
// Database is configuration for spaces.
|
||||||
type Database struct {
|
type Database struct {
|
||||||
Driver string `json:"driver" yaml:"driver"`
|
Driver string `json:"driver" yaml:"driver"`
|
||||||
Host string `json:"host" yaml:"host"`
|
Host string `json:"host" yaml:"host"`
|
||||||
Port int `json:"port" yaml:"port"`
|
Port int `json:"port" yaml:"port"`
|
||||||
Db string `json:"db" yaml:"db"`
|
Db string `json:"db" yaml:"db"`
|
||||||
Username string `json:"username" yaml:"username"`
|
Username string `json:"username" yaml:"username"`
|
||||||
Password string `json:"password" yaml:"password"`
|
Password string `json:"password" yaml:"password"`
|
||||||
Mechanism string `json:"mechanism" yaml:"mechanism"`
|
Mechanism string `json:"mechanism" yaml:"mechanism"`
|
||||||
|
RestoreIDs bool `json:"restoreIDs" yaml:"restoreIDs"`
|
||||||
|
SSL bool `json:"ssl" yaml:"ssl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wiki is the wiki stuff.
|
// Wiki is the wiki stuff.
|
||||||
|
|||||||
+38
-10
@@ -1,7 +1,9 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -15,19 +17,23 @@ type Change struct {
|
|||||||
Keys []ChangeKey `bson:"keys"`
|
Keys []ChangeKey `bson:"keys"`
|
||||||
Date time.Time `bson:"date"`
|
Date time.Time `bson:"date"`
|
||||||
|
|
||||||
Logs []*Log `bson:"logs"`
|
ChangeObjectSet
|
||||||
Characters []*Character `bson:"characters"`
|
}
|
||||||
Channels []*Channel `bson:"channels"`
|
|
||||||
Posts []*Post `bson:"posts"`
|
type ChangeObjectSet struct {
|
||||||
Stories []*Story `bson:"stories"`
|
Logs []*Log `bson:"logs" json:"logs,omitempty"`
|
||||||
Tags []*Tag `bson:"tags"`
|
Characters []*Character `bson:"characters" json:"characters,omitempty"`
|
||||||
Chapters []*Chapter `bson:"chapters"`
|
Channels []*Channel `bson:"channels" json:"channels,omitempty"`
|
||||||
Comments []*Comment `bson:"comments"`
|
Posts []*Post `bson:"posts" json:"posts,omitempty"`
|
||||||
Files []*File `bson:"files"`
|
Stories []*Story `bson:"stories" json:"stories,omitempty"`
|
||||||
|
Tags []*Tag `bson:"tags" json:"tags,omitempty"`
|
||||||
|
Chapters []*Chapter `bson:"chapters" json:"chapters,omitempty"`
|
||||||
|
Comments []*Comment `bson:"comments" json:"comments,omitempty"`
|
||||||
|
Files []*File `bson:"files" json:"files,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddObject adds the model into the appropriate array.
|
// AddObject adds the model into the appropriate array.
|
||||||
func (change *Change) AddObject(object interface{}) bool {
|
func (change *ChangeObjectSet) AddObject(object interface{}) bool {
|
||||||
if v := reflect.ValueOf(object); v.Kind() == reflect.Struct {
|
if v := reflect.ValueOf(object); v.Kind() == reflect.Struct {
|
||||||
ptr := reflect.PtrTo(v.Type())
|
ptr := reflect.PtrTo(v.Type())
|
||||||
ptrValue := reflect.New(ptr.Elem())
|
ptrValue := reflect.New(ptr.Elem())
|
||||||
@@ -174,10 +180,32 @@ type ChangeKey struct {
|
|||||||
ID string `bson:"id"`
|
ID string `bson:"id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ck *ChangeKey) String() string {
|
||||||
|
return ck.Model.String() + ":" + ck.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ck *ChangeKey) Decode(str string) error {
|
||||||
|
split := strings.Split(str, ":")
|
||||||
|
if len(split) != 2 {
|
||||||
|
return fmt.Errorf("invalid change key: %s", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
model := ChangeModel(split[0])
|
||||||
|
if !model.IsValid() {
|
||||||
|
return fmt.Errorf("invalid change key model: %s", model)
|
||||||
|
}
|
||||||
|
|
||||||
|
ck.Model = model
|
||||||
|
ck.ID = split[1]
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ChangeFilter is a filter for listing changes.
|
// ChangeFilter is a filter for listing changes.
|
||||||
type ChangeFilter struct {
|
type ChangeFilter struct {
|
||||||
Keys []ChangeKey
|
Keys []ChangeKey
|
||||||
EarliestDate *time.Time
|
EarliestDate *time.Time
|
||||||
|
LatestDate *time.Time
|
||||||
Author *string
|
Author *string
|
||||||
Limit *int
|
Limit *int
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,21 @@ type Channel struct {
|
|||||||
LocationName string `bson:"locationName,omitempty"`
|
LocationName string `bson:"locationName,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (channel *Channel) ApplyUpdate(update ChannelUpdate) {
|
||||||
|
if update.Logged != nil {
|
||||||
|
channel.Logged = *update.Logged
|
||||||
|
}
|
||||||
|
if update.Hub != nil {
|
||||||
|
channel.Hub = *update.Hub
|
||||||
|
}
|
||||||
|
if update.EventName != nil {
|
||||||
|
channel.EventName = *update.EventName
|
||||||
|
}
|
||||||
|
if update.LocationName != nil {
|
||||||
|
channel.LocationName = *update.LocationName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// IsChangeObject is an interface implementation to identify it as a valid
|
// IsChangeObject is an interface implementation to identify it as a valid
|
||||||
// ChangeObject in GQL.
|
// ChangeObject in GQL.
|
||||||
func (*Channel) IsChangeObject() {
|
func (*Channel) IsChangeObject() {
|
||||||
|
|||||||
@@ -16,6 +16,24 @@ type Chapter struct {
|
|||||||
CommentsLocked bool `bson:"commentsLocked"`
|
CommentsLocked bool `bson:"commentsLocked"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (chapter *Chapter) ApplyUpdate(update ChapterUpdate) {
|
||||||
|
if update.Title != nil {
|
||||||
|
chapter.Title = *update.Title
|
||||||
|
}
|
||||||
|
if update.Source != nil {
|
||||||
|
chapter.Source = *update.Source
|
||||||
|
}
|
||||||
|
if update.FictionalDate != nil {
|
||||||
|
chapter.FictionalDate = update.FictionalDate.UTC()
|
||||||
|
}
|
||||||
|
if update.CommentMode != nil {
|
||||||
|
chapter.CommentMode = *update.CommentMode
|
||||||
|
}
|
||||||
|
if update.CommentsLocked != nil {
|
||||||
|
chapter.CommentsLocked = *update.CommentsLocked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// CanComment returns true if the chapter can be commented to.
|
// CanComment returns true if the chapter can be commented to.
|
||||||
func (chapter *Chapter) CanComment() bool {
|
func (chapter *Chapter) CanComment() bool {
|
||||||
return !chapter.CommentsLocked && chapter.CommentMode.IsEnabled()
|
return !chapter.CommentsLocked && chapter.CommentMode.IsEnabled()
|
||||||
|
|||||||
+18
-6
@@ -2,12 +2,24 @@ package models
|
|||||||
|
|
||||||
// Character is a common data model representing an RP character or NPC.
|
// Character is a common data model representing an RP character or NPC.
|
||||||
type Character struct {
|
type Character struct {
|
||||||
ID string `json:"id" bson:"_id"`
|
ID string `json:"id" bson:"_id" db:"id"`
|
||||||
Nicks []string `json:"nicks" bson:"nicks"`
|
Nicks []string `json:"nicks" bson:"nicks" db:"nicks"`
|
||||||
Name string `json:"name" bson:"name"`
|
Name string `json:"name" bson:"name" db:"name"`
|
||||||
ShortName string `json:"shortName" bson:"shortName"`
|
ShortName string `json:"shortName" bson:"shortName" db:"short_name"`
|
||||||
Author string `json:"author" bson:"author"`
|
Author string `json:"author" bson:"author" db:"author"`
|
||||||
Description string `json:"description" bson:"description"`
|
Description string `json:"description" bson:"description" db:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (character *Character) ApplyUpdate(update CharacterUpdate) {
|
||||||
|
if update.Name != nil {
|
||||||
|
character.Name = *update.Name
|
||||||
|
}
|
||||||
|
if update.ShortName != nil {
|
||||||
|
character.ShortName = *update.ShortName
|
||||||
|
}
|
||||||
|
if update.Description != nil {
|
||||||
|
character.Description = *update.Description
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nick gets the character's nick.
|
// Nick gets the character's nick.
|
||||||
|
|||||||
@@ -16,6 +16,24 @@ type Comment struct {
|
|||||||
Source string `bson:"source"`
|
Source string `bson:"source"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (comment *Comment) ApplyUpdate(update CommentUpdate) {
|
||||||
|
if update.Source != nil {
|
||||||
|
comment.Source = *update.Source
|
||||||
|
}
|
||||||
|
if update.CharacterName != nil {
|
||||||
|
comment.CharacterName = *update.CharacterName
|
||||||
|
}
|
||||||
|
if update.CharacterID != nil {
|
||||||
|
comment.CharacterID = *update.CharacterID
|
||||||
|
}
|
||||||
|
if update.Subject != nil {
|
||||||
|
comment.Subject = *update.Subject
|
||||||
|
}
|
||||||
|
if update.FictionalDate != nil {
|
||||||
|
comment.FictionalDate = *update.FictionalDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// IsChangeObject is an interface implementation to identify it as a valid
|
// IsChangeObject is an interface implementation to identify it as a valid
|
||||||
// ChangeObject in GQL.
|
// ChangeObject in GQL.
|
||||||
func (*Comment) IsChangeObject() {
|
func (*Comment) IsChangeObject() {
|
||||||
|
|||||||
+19
-1
@@ -15,6 +15,24 @@ type Log struct {
|
|||||||
CharacterIDs []string `bson:"characterIds"`
|
CharacterIDs []string `bson:"characterIds"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (log *Log) ApplyUpdate(update LogUpdate) {
|
||||||
|
if update.Open != nil {
|
||||||
|
log.Open = *update.Open
|
||||||
|
}
|
||||||
|
if update.EventName != nil {
|
||||||
|
log.EventName = *update.EventName
|
||||||
|
}
|
||||||
|
if update.Title != nil {
|
||||||
|
log.Title = *update.Title
|
||||||
|
}
|
||||||
|
if update.Description != nil {
|
||||||
|
log.Description = *update.Description
|
||||||
|
}
|
||||||
|
if update.CharacterIDs != nil {
|
||||||
|
log.CharacterIDs = update.CharacterIDs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// A LogSuggestion is a suggestion for a log.
|
// A LogSuggestion is a suggestion for a log.
|
||||||
type LogSuggestion struct {
|
type LogSuggestion struct {
|
||||||
Log *Log
|
Log *Log
|
||||||
@@ -31,13 +49,13 @@ func (*Log) IsChangeObject() {
|
|||||||
|
|
||||||
// A LogFilter is a filter that can be used to list logs.
|
// A LogFilter is a filter that can be used to list logs.
|
||||||
type LogFilter struct {
|
type LogFilter struct {
|
||||||
Search *string
|
|
||||||
Open *bool
|
Open *bool
|
||||||
Characters []string
|
Characters []string
|
||||||
Channels []string
|
Channels []string
|
||||||
Events []string
|
Events []string
|
||||||
MinDate *time.Time
|
MinDate *time.Time
|
||||||
MaxDate *time.Time
|
MaxDate *time.Time
|
||||||
|
Search *string
|
||||||
Limit int
|
Limit int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,21 @@ type Post struct {
|
|||||||
Position int `bson:"position"`
|
Position int `bson:"position"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (post *Post) ApplyUpdate(update PostUpdate) {
|
||||||
|
if update.Time != nil {
|
||||||
|
post.Time = *update.Time
|
||||||
|
}
|
||||||
|
if update.Kind != nil {
|
||||||
|
post.Kind = *update.Kind
|
||||||
|
}
|
||||||
|
if update.Nick != nil {
|
||||||
|
post.Nick = *update.Nick
|
||||||
|
}
|
||||||
|
if update.Text != nil {
|
||||||
|
post.Text = *update.Text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// IsChangeObject is an interface implementation to identify it as a valid
|
// IsChangeObject is an interface implementation to identify it as a valid
|
||||||
// ChangeObject in GQL.
|
// ChangeObject in GQL.
|
||||||
func (*Post) IsChangeObject() {
|
func (*Post) IsChangeObject() {
|
||||||
|
|||||||
+28
-1
@@ -18,9 +18,36 @@ type Story struct {
|
|||||||
SortByFictionalDate bool `bson:"sortByFictionalDate"`
|
SortByFictionalDate bool `bson:"sortByFictionalDate"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (story *Story) ApplyUpdate(update StoryUpdate) {
|
||||||
|
if update.Name != nil {
|
||||||
|
story.Name = *update.Name
|
||||||
|
}
|
||||||
|
if update.Category != nil {
|
||||||
|
story.Category = *update.Category
|
||||||
|
}
|
||||||
|
if update.Author != nil {
|
||||||
|
story.Author = *update.Author
|
||||||
|
}
|
||||||
|
if update.Open != nil {
|
||||||
|
story.Open = *update.Open
|
||||||
|
}
|
||||||
|
if update.Listed != nil {
|
||||||
|
story.Listed = *update.Listed
|
||||||
|
}
|
||||||
|
if update.SortByFictionalDate != nil {
|
||||||
|
story.SortByFictionalDate = *update.SortByFictionalDate
|
||||||
|
}
|
||||||
|
if update.FictionalDate != nil {
|
||||||
|
story.FictionalDate = *update.FictionalDate
|
||||||
|
}
|
||||||
|
if update.UpdatedDate != nil {
|
||||||
|
story.UpdatedDate = *update.UpdatedDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// IsChangeObject is an interface implementation to identify it as a valid
|
// IsChangeObject is an interface implementation to identify it as a valid
|
||||||
// ChangeObject in GQL.
|
// ChangeObject in GQL.
|
||||||
func (*Story) IsChangeObject() {
|
func (_ *Story) IsChangeObject() {
|
||||||
panic("this method is a dummy, and so is its caller")
|
panic("this method is a dummy, and so is its caller")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+47
-1
@@ -1,8 +1,10 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Tag associates a story with other content, like other stories, logs and more.
|
// A Tag associates a story with other content, like other stories, logs and more.
|
||||||
@@ -11,6 +13,28 @@ type Tag struct {
|
|||||||
Name string `bson:"name"`
|
Name string `bson:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tag *Tag) Decode(v string) error {
|
||||||
|
split := strings.SplitN(v, ":", 2)
|
||||||
|
if len(split) == 1 {
|
||||||
|
return errors.New("bad tag name: " + v)
|
||||||
|
}
|
||||||
|
|
||||||
|
kind := TagKind("")
|
||||||
|
err := kind.UnmarshalGQL(split[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tag.Kind = kind
|
||||||
|
tag.Name = split[1]
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tag *Tag) String() string {
|
||||||
|
return string(tag.Kind) + ":" + tag.Name
|
||||||
|
}
|
||||||
|
|
||||||
// Equal returns true if the tags match one another.
|
// Equal returns true if the tags match one another.
|
||||||
func (tag *Tag) Equal(other Tag) bool {
|
func (tag *Tag) Equal(other Tag) bool {
|
||||||
return tag.Kind == other.Kind && tag.Name == other.Name
|
return tag.Kind == other.Kind && tag.Name == other.Name
|
||||||
@@ -22,6 +46,28 @@ func (*Tag) IsChangeObject() {
|
|||||||
panic("this method is a dummy, and so is its caller")
|
panic("this method is a dummy, and so is its caller")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func EncodeTagArray(arr []Tag) []string {
|
||||||
|
result := make([]string, len(arr))
|
||||||
|
for i, v := range arr {
|
||||||
|
result[i] = v.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecodeTagArray(arr []string) ([]*Tag, error) {
|
||||||
|
result := make([]*Tag, 0, len(arr))
|
||||||
|
for i, v := range arr {
|
||||||
|
result = append(result, &Tag{})
|
||||||
|
err := result[i].Decode(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
// TagKind represents the kind of tags.
|
// TagKind represents the kind of tags.
|
||||||
type TagKind string
|
type TagKind string
|
||||||
|
|
||||||
@@ -60,7 +106,7 @@ func (e *TagKind) UnmarshalGQL(v interface{}) error {
|
|||||||
|
|
||||||
// MarshalGQL turns it into a JSON string
|
// MarshalGQL turns it into a JSON string
|
||||||
func (e TagKind) MarshalGQL(w io.Writer) {
|
func (e TagKind) MarshalGQL(w io.Writer) {
|
||||||
fmt.Fprint(w, "\""+string(e), "\"")
|
_, _ = fmt.Fprintf(w, "\"%s\"", string(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TagFilter is a filter for tag listing.
|
// TagFilter is a filter for tag listing.
|
||||||
|
|||||||
@@ -7,5 +7,6 @@ import (
|
|||||||
|
|
||||||
type UserRepository interface {
|
type UserRepository interface {
|
||||||
Find(ctx context.Context, id string) (*models.User, error)
|
Find(ctx context.Context, id string) (*models.User, error)
|
||||||
|
List(ctx context.Context) ([]*models.User, error)
|
||||||
Insert(ctx context.Context, user models.User) (*models.User, error)
|
Insert(ctx context.Context, user models.User) (*models.User, error)
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-2
@@ -61,15 +61,36 @@ func (s *CharacterService) List(ctx context.Context, filter models.CharacterFilt
|
|||||||
}
|
}
|
||||||
|
|
||||||
sort.Slice(characters, func(i, j int) bool {
|
sort.Slice(characters, func(i, j int) bool {
|
||||||
|
if len(characters[i].ID) > len(characters[j].ID) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if len(characters[i].ID) < len(characters[j].ID) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return strings.Compare(characters[i].ID, characters[j].ID) < 0
|
return strings.Compare(characters[i].ID, characters[j].ID) < 0
|
||||||
})
|
})
|
||||||
|
|
||||||
return characters, nil
|
return characters, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
go s.refreshLogs()
|
characters, err := s.characters.List(ctx, filter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return s.characters.List(ctx, filter)
|
sort.Slice(characters, func(i, j int) bool {
|
||||||
|
if len(characters[i].ID) > len(characters[j].ID) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(characters[i].ID) < len(characters[j].ID) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Compare(characters[i].ID, characters[j].ID) < 0
|
||||||
|
})
|
||||||
|
|
||||||
|
return characters, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CharacterService) Create(ctx context.Context, nick, name, shortName, author, description string) (*models.Character, error) {
|
func (s *CharacterService) Create(ctx context.Context, nick, name, shortName, author, description string) (*models.Character, error) {
|
||||||
|
|||||||
+9
-1
@@ -718,11 +718,19 @@ func (s *LogService) RefreshAllLogCharacters(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
s.unknownNicksMutex.Unlock()
|
s.unknownNicksMutex.Unlock()
|
||||||
|
|
||||||
|
tokens := make(chan struct{}, 33)
|
||||||
|
for i := 0; i < 32; i++ {
|
||||||
|
tokens <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
eg := errgroup.Group{}
|
eg := errgroup.Group{}
|
||||||
for i := range logs {
|
for i := range logs {
|
||||||
l := logs[i]
|
l := logs[i]
|
||||||
|
|
||||||
eg.Go(func() error {
|
eg.Go(func() error {
|
||||||
|
<-tokens
|
||||||
|
defer func() { tokens <- struct{}{} }()
|
||||||
|
|
||||||
_, err := s.refreshLogCharacters(ctx, *l, characterMap, true)
|
_, err := s.refreshLogCharacters(ctx, *l, characterMap, true)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
@@ -748,7 +756,7 @@ func (s *LogService) RefreshLogCharacters(ctx context.Context, log models.Log) (
|
|||||||
func (s *LogService) refreshLogCharacters(ctx context.Context, log models.Log, characterMap map[string]*models.Character, useUnknownNicks bool) (*models.Log, error) {
|
func (s *LogService) refreshLogCharacters(ctx context.Context, log models.Log, characterMap map[string]*models.Character, useUnknownNicks bool) (*models.Log, error) {
|
||||||
posts, err := s.ListPosts(ctx, &models.PostFilter{LogID: &log.ShortID})
|
posts, err := s.ListPosts(ctx, &models.PostFilter{LogID: &log.ShortID})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &log, nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
counts := make(map[string]int)
|
counts := make(map[string]int)
|
||||||
|
|||||||
@@ -29,11 +29,13 @@ func NewBundle(db database.Database, spaceClient *space.Client) *Bundle {
|
|||||||
changes: db.Changes(),
|
changes: db.Changes(),
|
||||||
authService: bundle.Auth,
|
authService: bundle.Auth,
|
||||||
}
|
}
|
||||||
bundle.Files = &FileService{
|
if spaceClient != nil {
|
||||||
files: db.Files(),
|
bundle.Files = &FileService{
|
||||||
authService: bundle.Auth,
|
files: db.Files(),
|
||||||
changeService: bundle.Changes,
|
authService: bundle.Auth,
|
||||||
space: spaceClient,
|
changeService: bundle.Changes,
|
||||||
|
space: spaceClient,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
bundle.Tags = &TagService{tags: db.Tags()}
|
bundle.Tags = &TagService{tags: db.Tags()}
|
||||||
bundle.Characters = &CharacterService{
|
bundle.Characters = &CharacterService{
|
||||||
|
|||||||
@@ -34,6 +34,23 @@ func (s *StoryService) FindComment(ctx context.Context, id string) (*models.Comm
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *StoryService) ListStories(ctx context.Context, filter models.StoryFilter) ([]*models.Story, error) {
|
func (s *StoryService) ListStories(ctx context.Context, filter models.StoryFilter) ([]*models.Story, error) {
|
||||||
|
if filter.Unlisted != nil && *filter.Unlisted {
|
||||||
|
token := s.authService.TokenFromContext(ctx)
|
||||||
|
if !token.Authenticated() {
|
||||||
|
return nil, errors.New("you cannot view unlisted stories")
|
||||||
|
}
|
||||||
|
if !token.Permitted("story.unlisted") {
|
||||||
|
if filter.Author == nil {
|
||||||
|
filter.Author = &token.UserID
|
||||||
|
} else if *filter.Author != token.UserID {
|
||||||
|
return nil, errors.New("you cannot view your own unlisted stories")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unlistedValue := false
|
||||||
|
filter.Unlisted = &unlistedValue
|
||||||
|
}
|
||||||
|
|
||||||
return s.stories.List(ctx, filter)
|
return s.stories.List(ctx, filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
version: "1"
|
||||||
|
packages:
|
||||||
|
- name: "psqlcore"
|
||||||
|
path: "./database/postgres/psqlcore"
|
||||||
|
queries: "./database/postgres/queries"
|
||||||
|
schema: "./database/postgres/migrations"
|
||||||
|
engine: "postgresql"
|
||||||
|
emit_prepared_queries: false
|
||||||
|
emit_interface: false
|
||||||
|
emit_exact_table_names: false
|
||||||
|
emit_empty_slices: true
|
||||||
|
emit_json_tags: true
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user