convert to go (#1)
This commit is contained in:
@@ -1,4 +1,2 @@
|
||||
dist/
|
||||
node_modules/
|
||||
.vscode/
|
||||
.env
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
PORT=8080
|
||||
HOST=0.0.0.0
|
||||
REDIS_URI=redis://localhost:6379
|
||||
REDIS_URI=localhost:6379
|
||||
|
||||
DISCORD_CLIENT_ID=
|
||||
DISCORD_CLIENT_SECRET=
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": [
|
||||
"@typescript-eslint",
|
||||
"header",
|
||||
"simple-import-sort",
|
||||
"unused-imports"
|
||||
],
|
||||
"rules": {
|
||||
"header/header": [
|
||||
2,
|
||||
"block",
|
||||
[
|
||||
{
|
||||
"pattern": "!?",
|
||||
"template": " "
|
||||
},
|
||||
" * Vencord, a modification for Discord's desktop app",
|
||||
{
|
||||
"pattern": " \\* Copyright \\(c\\) \\d{4}",
|
||||
"template": " * Copyright (c) 2023 Vendicated and contributors"
|
||||
},
|
||||
" *",
|
||||
" * This program is free software: you can redistribute it and/or modify",
|
||||
" * it under the terms of the GNU General Public License as published by",
|
||||
" * the Free Software Foundation, either version 3 of the License, or",
|
||||
" * (at your option) any later version.",
|
||||
" *",
|
||||
" * This program is distributed in the hope that it will be useful,",
|
||||
" * but WITHOUT ANY WARRANTY; without even the implied warranty of",
|
||||
" * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the",
|
||||
" * GNU General Public License for more details.",
|
||||
" *",
|
||||
" * You should have received a copy of the GNU General Public License",
|
||||
" * along with this program. If not, see <https://www.gnu.org/licenses/>.",
|
||||
""
|
||||
],
|
||||
2
|
||||
],
|
||||
"quotes": ["error", "double", { "avoidEscape": true }],
|
||||
"no-mixed-spaces-and-tabs": "error",
|
||||
"indent": ["error", 4, { "SwitchCase": 1 }],
|
||||
"arrow-parens": ["error", "as-needed"],
|
||||
"eol-last": ["error", "always"],
|
||||
"func-call-spacing": ["error", "never"],
|
||||
"no-multi-spaces": "error",
|
||||
"no-trailing-spaces": "error",
|
||||
"no-whitespace-before-property": "error",
|
||||
"semi": ["error", "always"],
|
||||
"semi-style": ["error", "last"],
|
||||
"space-in-parens": ["error", "never"],
|
||||
"block-spacing": ["error", "always"],
|
||||
"object-curly-spacing": ["error", "always"],
|
||||
"eqeqeq": ["error", "always", { "null": "ignore" }],
|
||||
"spaced-comment": ["error", "always", { "markers": ["!"] }],
|
||||
"yoda": "error",
|
||||
"prefer-destructuring": ["error", { "object": true, "array": false }],
|
||||
"operator-assignment": ["error", "always"],
|
||||
"no-useless-computed-key": "error",
|
||||
"no-unneeded-ternary": ["error", { "defaultAssignment": false }],
|
||||
"no-invalid-regexp": "error",
|
||||
"no-constant-condition": ["error", { "checkLoops": false }],
|
||||
"no-duplicate-imports": "error",
|
||||
"no-extra-semi": "error",
|
||||
"dot-notation": "error",
|
||||
"no-useless-escape": ["error"],
|
||||
"no-fallthrough": "error",
|
||||
"for-direction": "error",
|
||||
"no-async-promise-executor": "error",
|
||||
"no-cond-assign": "error",
|
||||
"no-dupe-else-if": "error",
|
||||
"no-duplicate-case": "error",
|
||||
"no-irregular-whitespace": "error",
|
||||
"no-loss-of-precision": "error",
|
||||
"no-misleading-character-class": "error",
|
||||
"no-prototype-builtins": "error",
|
||||
"no-regex-spaces": "error",
|
||||
"no-shadow-restricted-names": "error",
|
||||
"no-unexpected-multiline": "error",
|
||||
"no-unsafe-optional-chaining": "error",
|
||||
"no-useless-backreference": "error",
|
||||
"use-isnan": "error",
|
||||
"prefer-const": "error",
|
||||
"prefer-spread": "error",
|
||||
|
||||
"simple-import-sort/imports": "error",
|
||||
"simple-import-sort/exports": "error",
|
||||
|
||||
"unused-imports/no-unused-imports": "error"
|
||||
}
|
||||
}
|
||||
-14
@@ -1,21 +1,7 @@
|
||||
dist
|
||||
node_modules
|
||||
|
||||
.env
|
||||
|
||||
.idea
|
||||
.DS_Store
|
||||
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
*.tsbuildinfo
|
||||
|
||||
redis-data
|
||||
docker-compose.override.yml
|
||||
|
||||
Vendored
+1
-14
@@ -1,16 +1,3 @@
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "vscode.typescript-language-features"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "vscode.typescript-language-features"
|
||||
},
|
||||
"javascript.format.semicolons": "insert",
|
||||
"typescript.format.semicolons": "insert",
|
||||
"typescript.preferences.quoteStyle": "double",
|
||||
"javascript.preferences.quoteStyle": "double"
|
||||
"editor.foldingStrategy": "indentation"
|
||||
}
|
||||
|
||||
+5
-7
@@ -1,13 +1,11 @@
|
||||
FROM node:lts-slim
|
||||
|
||||
RUN npm install --global pnpm
|
||||
FROM golang:1.20
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ADD package.json pnpm-lock.yaml ./
|
||||
RUN pnpm install --frozen-lockfile
|
||||
ADD go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
ADD . ./
|
||||
RUN pnpm compile
|
||||
RUN go build -o /backend
|
||||
|
||||
CMD ["node", "dist/index.js"]
|
||||
CMD ["/backend"]
|
||||
|
||||
+274
@@ -0,0 +1,274 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
||||
reqHttp "github.com/imroc/req/v3"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
type DiscordAccessTokenResult struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
}
|
||||
|
||||
type DiscordUserResult struct {
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
func hash(s string) string {
|
||||
return fmt.Sprintf("%x", sha1.Sum([]byte(s)))
|
||||
}
|
||||
|
||||
func main() {
|
||||
// environment
|
||||
HOST := os.Getenv("HOST")
|
||||
PORT := os.Getenv("PORT")
|
||||
REDIS_URI := os.Getenv("REDIS_URI")
|
||||
|
||||
DISCORD_CLIENT_ID := os.Getenv("DISCORD_CLIENT_ID")
|
||||
DISCORD_CLIENT_SECRET := os.Getenv("DISCORD_CLIENT_SECRET")
|
||||
DISCORD_REDIRECT_URI := os.Getenv("DISCORD_REDIRECT_URI")
|
||||
|
||||
PEPPER_SECRETS := os.Getenv("PEPPER_SECRETS")
|
||||
PEPPER_SETTINGS := os.Getenv("PEPPER_SETTINGS")
|
||||
|
||||
slRaw, _ := strconv.ParseInt(os.Getenv("SIZE_LIMIT"), 10, 0)
|
||||
SIZE_LIMIT := int(slRaw)
|
||||
|
||||
app := fiber.New()
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: REDIS_URI,
|
||||
})
|
||||
req := reqHttp.C()
|
||||
|
||||
app.Use(cors.New(cors.Config{
|
||||
ExposeHeaders: "ETag",
|
||||
}))
|
||||
app.Use(logger.New())
|
||||
|
||||
// #region settings
|
||||
app.All("/settings", func(c *fiber.Ctx) error {
|
||||
authToken := c.Get("Authorization")
|
||||
|
||||
if authToken == "" {
|
||||
return c.Status(401).JSON(&fiber.Map{
|
||||
"error": "Missing authorization",
|
||||
})
|
||||
}
|
||||
|
||||
// decode base64 token and split by :
|
||||
// token[0] = username
|
||||
// token[1] = password
|
||||
token, err := base64.StdEncoding.DecodeString(authToken)
|
||||
|
||||
if err != nil {
|
||||
return c.Status(401).JSON(&fiber.Map{
|
||||
"error": "Invalid authorization",
|
||||
})
|
||||
}
|
||||
|
||||
tokenStr := string(token)
|
||||
tokenSplit := strings.Split(tokenStr, ":")
|
||||
|
||||
if len(tokenSplit) != 2 {
|
||||
return c.Status(401).JSON(&fiber.Map{
|
||||
"error": "Invalid authorization",
|
||||
})
|
||||
}
|
||||
|
||||
userId := tokenSplit[0]
|
||||
secret := tokenSplit[1]
|
||||
|
||||
storedSecret, err := rdb.Get(c.Context(), "secrets:" + hash(PEPPER_SECRETS + userId)).Result()
|
||||
|
||||
if err == redis.Nil {
|
||||
return c.Status(401).JSON(&fiber.Map{
|
||||
"error": "Invalid authorization",
|
||||
})
|
||||
} else if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if storedSecret != secret {
|
||||
return c.Status(401).JSON(&fiber.Map{
|
||||
"error": "Invalid authorization",
|
||||
})
|
||||
}
|
||||
|
||||
c.Context().SetUserValue("userId", userId)
|
||||
|
||||
return c.Next()
|
||||
})
|
||||
|
||||
app.Head("/settings", func(c *fiber.Ctx) error {
|
||||
userId := c.Context().UserValue("userId").(string)
|
||||
|
||||
written, err := rdb.HGet(c.Context(), "settings:" + hash(PEPPER_SETTINGS + userId), "written").Result()
|
||||
|
||||
if err == redis.Nil {
|
||||
return c.Status(404).Send(nil)
|
||||
} else if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Set("ETag", written)
|
||||
return c.SendStatus(204)
|
||||
})
|
||||
|
||||
app.Get("/settings", func(c *fiber.Ctx) error {
|
||||
userId := c.Context().UserValue("userId").(string)
|
||||
|
||||
settings, err := rdb.HMGet(c.Context(), "settings:" + hash(PEPPER_SETTINGS + userId), "value", "written").Result()
|
||||
|
||||
if err == redis.Nil {
|
||||
return c.Status(404).Send(nil)
|
||||
} else if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// value is compressed data, written is a timestamp
|
||||
value, written := []byte(settings[0].(string)), settings[1].(string)
|
||||
|
||||
c.Set("Content-Type", "application/octet-stream")
|
||||
c.Set("ETag", written)
|
||||
return c.Send(value)
|
||||
})
|
||||
|
||||
app.Put("/settings", func(c *fiber.Ctx) error {
|
||||
if (c.Get("Content-Type") != "application/octet-stream") {
|
||||
return c.Status(415).JSON(&fiber.Map{
|
||||
"error": "Content type must be application/octet-stream",
|
||||
})
|
||||
}
|
||||
|
||||
if (len(c.Body()) > SIZE_LIMIT) {
|
||||
return c.Status(413).JSON(&fiber.Map{
|
||||
"error": "Settings are too large",
|
||||
})
|
||||
}
|
||||
|
||||
userId := c.Context().UserValue("userId").(string)
|
||||
|
||||
now := time.Now().Unix()
|
||||
|
||||
_, err := rdb.HSet(c.Context(), "settings:" + hash(PEPPER_SETTINGS + userId), map[string]interface{}{
|
||||
"value": c.Body(),
|
||||
"written": now,
|
||||
}).Result()
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return c.JSON(&fiber.Map{
|
||||
"written": now,
|
||||
})
|
||||
})
|
||||
|
||||
app.Delete("/settings", func(c *fiber.Ctx) error {
|
||||
userId := c.Context().UserValue("userId").(string)
|
||||
|
||||
rdb.Del(c.Context(), "settings:" + hash(PEPPER_SETTINGS + userId))
|
||||
|
||||
return c.SendStatus(204)
|
||||
})
|
||||
// #endregion
|
||||
|
||||
// #region discord oauth
|
||||
app.Get("/callback", func(c *fiber.Ctx) error {
|
||||
code := c.Query("code")
|
||||
|
||||
if code == "" {
|
||||
return c.Status(400).JSON(&fiber.Map{
|
||||
"error": "Missing code",
|
||||
})
|
||||
}
|
||||
|
||||
var accessTokenResult DiscordAccessTokenResult
|
||||
|
||||
res, err := req.R().SetFormData(map[string]string{
|
||||
"client_id": DISCORD_CLIENT_ID,
|
||||
"client_secret": DISCORD_CLIENT_SECRET,
|
||||
"grant_type": "authorization_code",
|
||||
"code": code,
|
||||
"redirect_uri": DISCORD_REDIRECT_URI,
|
||||
"scope": "identify",
|
||||
}).SetSuccessResult(&accessTokenResult).Post("https://discord.com/api/oauth2/token")
|
||||
|
||||
if err != nil {
|
||||
return c.Status(500).JSON(&fiber.Map{
|
||||
"error": "Failed to request access token",
|
||||
})
|
||||
}
|
||||
|
||||
if res.IsErrorState() {
|
||||
return c.Status(400).JSON(&fiber.Map{
|
||||
"error": "Invalid code",
|
||||
})
|
||||
}
|
||||
|
||||
accessToken := accessTokenResult.AccessToken
|
||||
|
||||
var userResult DiscordUserResult
|
||||
|
||||
res, err = req.R().SetHeaders(map[string]string{
|
||||
"Authorization": "Bearer " + accessToken,
|
||||
}).SetSuccessResult(&userResult).Get("https://discord.com/api/users/@me")
|
||||
|
||||
if err != nil {
|
||||
return c.Status(500).JSON(&fiber.Map{
|
||||
"error": "Failed to request user",
|
||||
})
|
||||
}
|
||||
|
||||
if res.IsErrorState() {
|
||||
return c.Status(500).JSON(&fiber.Map{
|
||||
"error": "Failed to request user",
|
||||
})
|
||||
}
|
||||
|
||||
userId := userResult.Id
|
||||
|
||||
secretR := rdb.Get(c.Context(), "secrets:" + hash(PEPPER_SECRETS + userId))
|
||||
|
||||
var secret string
|
||||
|
||||
if secretR.Err() == redis.Nil {
|
||||
secret = secretR.Val()
|
||||
} else {
|
||||
key := make([]byte, 48)
|
||||
|
||||
_, err := rand.Read(key)
|
||||
if err != nil {
|
||||
return c.Status(500).JSON(&fiber.Map{
|
||||
"error": "Failed to generate secret",
|
||||
})
|
||||
}
|
||||
|
||||
secret = hex.EncodeToString(key)
|
||||
rdb.Set(c.Context(), "secrets:" + hash(PEPPER_SECRETS + userId), secret, 0)
|
||||
}
|
||||
|
||||
return c.JSON(&fiber.Map{
|
||||
"secret": secret,
|
||||
})
|
||||
})
|
||||
// #endregion
|
||||
|
||||
app.Get("/", func(c *fiber.Ctx) error {
|
||||
return c.SendFile("static/index.html")
|
||||
})
|
||||
|
||||
app.Listen(HOST + ":" + PORT)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
module github.com/Vencord/Backend
|
||||
|
||||
go 1.20
|
||||
|
||||
require github.com/gofiber/fiber/v2 v2.42.0
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/imroc/req/v3 v3.32.0 // indirect
|
||||
github.com/klauspost/compress v1.15.9 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
|
||||
github.com/philhofer/fwd v1.1.1 // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
|
||||
github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
|
||||
github.com/quic-go/quic-go v0.32.0 // indirect
|
||||
github.com/redis/go-redis/v9 v9.0.2 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect
|
||||
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d // indirect
|
||||
github.com/tinylib/msgp v1.1.6 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.44.0 // indirect
|
||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||
golang.org/x/crypto v0.4.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
|
||||
golang.org/x/mod v0.6.0 // indirect
|
||||
golang.org/x/net v0.4.0 // indirect
|
||||
golang.org/x/sys v0.3.0 // indirect
|
||||
golang.org/x/text v0.5.0 // indirect
|
||||
golang.org/x/tools v0.2.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,129 @@
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gofiber/fiber/v2 v2.42.0 h1:Fnp7ybWvS+sjNQsFvkhf4G8OhXswvB6Vee8hM/LyS+8=
|
||||
github.com/gofiber/fiber/v2 v2.42.0/go.mod h1:3+SGNjqMh5VQH5Vz2Wdi43zTIV16ktlFd3x3R6O1Zlc=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imroc/req/v3 v3.32.0 h1:jDGm2pVE8e/PExrJTvxEjOmgItVBJqvt3YjTUtZKyQM=
|
||||
github.com/imroc/req/v3 v3.32.0/go.mod h1:cZ+7C3L/AYOr4tLGG16hZF90F1WzAdAdzt1xFSlizXY=
|
||||
github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
|
||||
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
|
||||
github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
|
||||
github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ=
|
||||
github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0 h1:5ViXqBZ90wpUcZS0ge79rf029yx0dYB0McyPJwqqj7U=
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0/go.mod h1:moGulGHK7o6O8lSPSZNoOwcLvJKJ85vVNc7oJFD65bc=
|
||||
github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4nttk=
|
||||
github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||
github.com/quic-go/quic-go v0.32.0 h1:lY02md31s1JgPiiyfqJijpu/UX/Iun304FI3yUqX7tA=
|
||||
github.com/quic-go/quic-go v0.32.0/go.mod h1:/fCsKANhQIeD5l76c2JFU+07gVE3KaA0FP+0zMWwfwo=
|
||||
github.com/redis/go-redis/v9 v9.0.2 h1:BA426Zqe/7r56kCcvxYLWe1mkaz71LKF77GwgFzSxfE=
|
||||
github.com/redis/go-redis/v9 v9.0.2/go.mod h1:/xDTe9EF1LM61hek62Poq2nzQSGj0xSrEtEHbBQevps=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 h1:rmMl4fXJhKMNWl+K+r/fq4FbbKI+Ia2m9hYBLm2h4G4=
|
||||
github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94/go.mod h1:90zrgN3D/WJsDd1iXHT96alCoN2KJo6/4x1DZC3wZs8=
|
||||
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d h1:Q+gqLBOPkFGHyCJxXMRqtUgUbTjI8/Ze8vu8GGyNFwo=
|
||||
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/tinylib/msgp v1.1.6 h1:i+SbKraHhnrf9M5MYmvQhFnbLhAXSDWF8WWsuyRdocw=
|
||||
github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.44.0 h1:R+gLUhldIsfg1HokMuQjdQ5bh9nuXHPIfvkYUu9eR5Q=
|
||||
github.com/valyala/fasthttp v1.44.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY=
|
||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
|
||||
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
|
||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
|
||||
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
|
||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
||||
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-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@@ -1,57 +0,0 @@
|
||||
{
|
||||
"name": "vencord-settings-server",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"description": "Global settings sync server",
|
||||
"keywords": [],
|
||||
"homepage": "",
|
||||
"author": "lewisakura",
|
||||
"scripts": {
|
||||
"clean": "rimraf dist/",
|
||||
"compile": "tsc",
|
||||
"build": "pnpm clean && pnpm compile",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"lint:fix": "pnpm lint --fix",
|
||||
"test": "pnpm lint && pnpm testTsc",
|
||||
"testTsc": "tsc --noEmit"
|
||||
},
|
||||
"license": "GPL-3.0",
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.13.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.52.0",
|
||||
"@typescript-eslint/parser": "^5.52.0",
|
||||
"eslint": "^8.34.0",
|
||||
"eslint-import-resolver-alias": "^1.1.2",
|
||||
"eslint-plugin-header": "^3.1.1",
|
||||
"eslint-plugin-path-alias": "^1.0.0",
|
||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"rimraf": "^4.1.2",
|
||||
"typescript": "^4.9.5"
|
||||
},
|
||||
"packageManager": "pnpm@7.27.0",
|
||||
"pnpm": {
|
||||
"peerDependencyRules": {
|
||||
"ignoreMissing": [
|
||||
"eslint-plugin-import",
|
||||
"eslint"
|
||||
]
|
||||
},
|
||||
"allowedDeprecatedVersions": {
|
||||
"source-map-resolve": "*",
|
||||
"resolve-url": "*",
|
||||
"source-map-url": "*",
|
||||
"urix": "*"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^8.2.0",
|
||||
"dotenv": "^16.0.3",
|
||||
"fastify": "^4.13.0",
|
||||
"ioredis": "^5.3.1",
|
||||
"msgpackr": "^1.8.3",
|
||||
"node-fetch": "^3.3.0",
|
||||
"source-map-support": "^0.5.21"
|
||||
}
|
||||
}
|
||||
Generated
-2080
File diff suppressed because it is too large
Load Diff
-208
@@ -1,208 +0,0 @@
|
||||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import "source-map-support/register.js";
|
||||
import "dotenv/config";
|
||||
|
||||
import crypto from "node:crypto";
|
||||
|
||||
import cors from "@fastify/cors";
|
||||
import Fastify, { FastifyRequest } from "fastify";
|
||||
import Redis from "ioredis";
|
||||
import fetch from "node-fetch";
|
||||
|
||||
const fastify = Fastify();
|
||||
const redis = new Redis(process.env.REDIS_URI!);
|
||||
|
||||
const SIZE_LIMIT = parseInt(process.env.SIZE_LIMIT!);
|
||||
const ABOUT = `
|
||||
<h1>Vencord API</h1>
|
||||
<p>This is the API used by Vencord features like Settings Sync. If you are wondering why requests are sent here, it's because you explicitly opted into Settings Sync. You can disable it at any point!</p>
|
||||
<p>But no worries, this api <a href="/basic-privacy-policy">takes your privacy serious</a> and is <a href="https://github.com/Vencord/Backend">free and open source!</a></p>
|
||||
`.trim();
|
||||
|
||||
function hash(data: string) {
|
||||
return crypto.createHash("sha1").update(data).digest("hex");
|
||||
}
|
||||
|
||||
await fastify.register(cors, { exposedHeaders: ["ETag"] });
|
||||
|
||||
// #region request decoration & correction
|
||||
fastify.decorateRequest("userId", null);
|
||||
fastify.addContentTypeParser("application/octet-stream", { parseAs: "buffer" }, async (_request: FastifyRequest, body: Buffer) => {
|
||||
return body;
|
||||
});
|
||||
|
||||
declare module "fastify" {
|
||||
export interface FastifyRequest {
|
||||
userId: string;
|
||||
}
|
||||
}
|
||||
// #endregion
|
||||
|
||||
// #region settings
|
||||
// hook to force authorization when dealing with settings requests
|
||||
fastify.addHook("onRequest", async (request, reply) => {
|
||||
if (request.routerPath !== "/settings") {
|
||||
return;
|
||||
}
|
||||
|
||||
const authToken = request.headers.authorization;
|
||||
|
||||
if (!authToken) {
|
||||
return reply.status(401).send({ error: "Missing authorization" });
|
||||
}
|
||||
|
||||
const auth = Buffer.from(authToken, "base64")
|
||||
.toString("ascii")
|
||||
.split(":");
|
||||
|
||||
if (auth.length !== 2) {
|
||||
return reply.status(401).send({ error: "Invalid authorization" });
|
||||
}
|
||||
|
||||
const userId = auth[0];
|
||||
const secret = auth[1];
|
||||
|
||||
const storedSecret = await redis.get(`secrets:${hash(process.env.PEPPER_SECRETS! + userId)}`);
|
||||
|
||||
if (storedSecret !== secret) {
|
||||
return reply.status(401).send({ error: "Invalid authorization" });
|
||||
}
|
||||
|
||||
request.userId = userId;
|
||||
});
|
||||
|
||||
fastify.head("/settings", async (request, reply) => {
|
||||
const userIdHash = hash(process.env.PEPPER_SETTINGS! + request.userId);
|
||||
const written = await redis.hget(`settings:${userIdHash}`, "written");
|
||||
|
||||
if (!written) {
|
||||
return reply.status(404).send();
|
||||
}
|
||||
|
||||
return reply.header("ETag", written).send();
|
||||
});
|
||||
|
||||
fastify.get("/settings", async (request, reply) => {
|
||||
const userIdHash = hash(process.env.PEPPER_SETTINGS! + request.userId);
|
||||
const [settings, written] = await Promise.all([
|
||||
redis.hgetBuffer(`settings:${userIdHash}`, "value"),
|
||||
redis.hget(`settings:${userIdHash}`, "written")
|
||||
]);
|
||||
|
||||
if (!settings) {
|
||||
return reply.status(404).send({ error: "No settings currently synchronized" });
|
||||
}
|
||||
|
||||
reply.header("ETag", written!);
|
||||
return settings!;
|
||||
});
|
||||
|
||||
fastify.put("/settings", async (request, reply) => {
|
||||
if (request.headers["content-type"] !== "application/octet-stream") {
|
||||
return reply.status(415).send({ error: "Content type must be `application/octet-stream`" });
|
||||
}
|
||||
|
||||
if ((request.body as Buffer).byteLength > SIZE_LIMIT) {
|
||||
return reply.status(413).send({ error: "Settings are too large" });
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
|
||||
await redis.hmset(`settings:${hash(process.env.PEPPER_SETTINGS! + request.userId)}`, {
|
||||
value: request.body,
|
||||
written: now
|
||||
});
|
||||
|
||||
return { written: now };
|
||||
});
|
||||
|
||||
fastify.delete("/settings", async (request, reply) => {
|
||||
await redis.del(`settings:${hash(process.env.PEPPER_SETTINGS! + request.userId)}`);
|
||||
|
||||
return reply.status(204);
|
||||
});
|
||||
// #endregion
|
||||
|
||||
// #region discord oauth
|
||||
fastify.get("/authorize", async (request, reply) => {
|
||||
return reply.redirect(
|
||||
302,
|
||||
`https://discord.com/api/oauth2/authorize?client_id=${process.env.DISCORD_CLIENT_ID}&redirect_uri=${encodeURIComponent(
|
||||
process.env.DISCORD_REDIRECT_URI!
|
||||
)}&response_type=code&scope=identify`
|
||||
);
|
||||
});
|
||||
|
||||
fastify.get("/callback", async (request, reply) => {
|
||||
const code = (request.query as any).code as string;
|
||||
|
||||
if (!code) {
|
||||
return reply.status(400).send({ error: "Missing code" });
|
||||
}
|
||||
|
||||
const body = new URLSearchParams();
|
||||
body.set("client_id", process.env.DISCORD_CLIENT_ID!);
|
||||
body.set("client_secret", process.env.DISCORD_CLIENT_SECRET!);
|
||||
body.set("grant_type", "authorization_code");
|
||||
body.set("code", code);
|
||||
body.set("redirect_uri", process.env.DISCORD_REDIRECT_URI!);
|
||||
|
||||
const res = await fetch("https://discord.com/api/oauth2/token", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
body
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
return reply.status(400).send({ error: "Invalid code" });
|
||||
}
|
||||
|
||||
const { access_token: accessToken } = await res.json() as { access_token: string; };
|
||||
|
||||
const userRes = await fetch("https://discord.com/api/users/@me", {
|
||||
headers: {
|
||||
authorization: `Bearer ${accessToken}`
|
||||
}
|
||||
});
|
||||
|
||||
if (!userRes.ok) {
|
||||
return reply.status(500).send({ error: "Failed to get user" });
|
||||
}
|
||||
|
||||
const { id: userId } = await userRes.json() as { id: string; };
|
||||
|
||||
const hashId = hash(process.env.PEPPER_SECRETS + userId);
|
||||
|
||||
let secret = await redis.get(`secrets:${hashId}`);
|
||||
|
||||
if (!secret) {
|
||||
secret = crypto.randomBytes(48).toString("hex");
|
||||
await redis.set(`secrets:${hashId}`, secret);
|
||||
}
|
||||
|
||||
return { secret };
|
||||
});
|
||||
// #endregion
|
||||
|
||||
fastify.get("/", (_, reply) => reply.type("text/html").send(ABOUT));
|
||||
|
||||
await fastify.listen({ host: process.env.HOST!, port: parseInt(process.env.PORT!) });
|
||||
@@ -0,0 +1,11 @@
|
||||
<h1>Vencord API</h1>
|
||||
<p>
|
||||
This is the API used by Vencord features like Settings Sync. If you are
|
||||
wondering why requests are sent here, it's because you explicitly opted into
|
||||
Settings Sync. You can disable it at any point!
|
||||
</p>
|
||||
<p>
|
||||
But no worries, this api
|
||||
<a href="/basic-privacy-policy">takes your privacy serious</a> and is
|
||||
<a href="https://github.com/Vencord/Backend">free and open source!</a>
|
||||
</p>
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": ["esnext"],
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"noImplicitAny": false,
|
||||
"target": "esnext",
|
||||
"sourceMap": true,
|
||||
"skipLibCheck": true,
|
||||
|
||||
"baseUrl": "./src/",
|
||||
"outDir": "./dist/"
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
Reference in New Issue
Block a user