mirror of
https://github.com/roleypoly/roleypoly.git
synced 2025-04-24 19:39:11 +00:00
finish login story
This commit is contained in:
parent
a23184efd2
commit
c9cb4c95bc
34 changed files with 14564 additions and 21666 deletions
4
.babelrc
Normal file
4
.babelrc
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"presets": ["next/babel"],
|
||||
"plugins": [["styled-components", { "ssr": true }]]
|
||||
}
|
24
.env.example
Normal file
24
.env.example
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Make an application at https://discord.com/developers/applications
|
||||
BOT_CLIENT_ID=000000000000000000
|
||||
BOT_CLIENT_SECRET=RnX8pXXXXXXXXXXXXXXXXXXXXXXXXXu-
|
||||
BOT_TOKEN=Mzk2MjI3MTM0MjI3NXXXXXXXXXXXXXXXXXXXXXPUlYoARXXXXXXXXXXXXXX
|
||||
|
||||
# If 6600 or 6601 is taken, change this, and all other 6600/6601 references.
|
||||
PORT=6600
|
||||
UI_PORT=6601
|
||||
|
||||
# This is probably right, unless you set a different port.
|
||||
OAUTH_REDIRECT_URI=http://localhost:6600/login-handler
|
||||
|
||||
# Again, probably right. Do not put a trailing /
|
||||
UI_PUBLIC_URI=http://localhost:6601
|
||||
API_PUBLIC_URI=http://localhost:6600
|
||||
|
||||
# Google Cloud Project ID -- This only needs to be set for production/public cloud
|
||||
GCP_PROJECT_ID=roleypoly-0000000
|
||||
|
||||
# Unset this if you're planning to use public cloud Firestore as opposed to local
|
||||
# Or set this differently if you changed your local endpoint port.
|
||||
FIRESTORE_EMULATOR_HOST=localhost:6691
|
||||
|
||||
REDIS_ENDPOINT=localhost:6692
|
|
@ -34,7 +34,6 @@ module.exports = {
|
|||
},
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/naming-convention': 'error',
|
||||
'@typescript-eslint/no-empty-function': 'error',
|
||||
'@typescript-eslint/no-floating-promises': 'error',
|
||||
'@typescript-eslint/no-misused-new': 'error',
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -9,3 +9,5 @@ docker-compose.yaml
|
|||
*.log
|
||||
storybook-static
|
||||
dist
|
||||
.firebaserc
|
||||
.next
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
bazel-*
|
||||
dist
|
||||
storybook-static
|
||||
storybook-static
|
||||
.next
|
3
go.mod
3
go.mod
|
@ -3,9 +3,11 @@ module github.com/roleypoly/roleypoly
|
|||
go 1.15
|
||||
|
||||
require (
|
||||
cloud.google.com/go/firestore v1.3.0
|
||||
github.com/GoogleCloudPlatform/functions-framework-go v1.2.0
|
||||
github.com/bwmarrin/discordgo v0.22.0
|
||||
github.com/dghubble/trie v0.0.0-20201011220304-ed6d6b8add55
|
||||
github.com/google/martian v2.1.0+incompatible
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
github.com/joho/godotenv v1.3.0
|
||||
github.com/lampjaw/discordclient v0.0.0-20200923011548-6558fc9e89df
|
||||
|
@ -13,5 +15,6 @@ require (
|
|||
github.com/segmentio/ksuid v1.0.3
|
||||
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9 // indirect
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect
|
||||
google.golang.org/api v0.30.0
|
||||
k8s.io/klog v1.0.0
|
||||
)
|
||||
|
|
14
go.sum
14
go.sum
|
@ -11,9 +11,11 @@ cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6
|
|||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.61.0/go.mod h1:XukKJg4Y7QsUu0Hxg3qQKUWR4VuWivmyMK2+rUyxAqw=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.63.0 h1:A+DfAZQ/eWca7gvu42CS6FNSDX4R8cghF+XfWLn4R6g=
|
||||
cloud.google.com/go v0.63.0/go.mod h1:GmezbQc7T2snqkEXWfZ0sy0VfkB/ivI2DdtJL2DEmlg=
|
||||
cloud.google.com/go v0.72.0 h1:eWRCuwubtDrCJG0oSUMgnsbD4CmPFQF2ei4OFbXvwww=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
|
@ -22,6 +24,8 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g
|
|||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/firestore v1.3.0 h1:QaBSisuvNi9/o+3nCHqUEfduHCPfhEw2jcUofi0n8oY=
|
||||
cloud.google.com/go/firestore v1.3.0/go.mod h1:Qt0gS9Qz9tROrmgFavo36+hdST1FXvmtnGnO0Dr03pU=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
|
@ -98,6 +102,7 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
|
@ -111,6 +116,7 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
|
|||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
|
@ -242,6 +248,7 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG
|
|||
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/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/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=
|
||||
|
@ -327,6 +334,8 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY
|
|||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200713011307-fd294ab11aed/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200727233628-55644ead90ce/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200806022845-90696ccdc692/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
|
@ -350,6 +359,7 @@ google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/
|
|||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0 h1:yfrXXP61wVuLb0vBcG6qaOoIoqYEzOQS8jum51jkv2w=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
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=
|
||||
|
@ -383,8 +393,11 @@ google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfG
|
|||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200711021454-869866162049/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200728010541-3dc8dca74b7b/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98 h1:LCO0fg4kb6WwkXQXRQQgUYsFeFb5taTX5WAx5O/Vt28=
|
||||
google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
|
@ -397,6 +410,7 @@ google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
|
|||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0 h1:T7P4R73V3SSDPhH7WW7ATbfViLtmamH0DKrP3f9AuDI=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
|
|
|
@ -9,16 +9,21 @@ import (
|
|||
|
||||
"github.com/GoogleCloudPlatform/functions-framework-go/funcframework"
|
||||
_ "github.com/joho/godotenv/autoload"
|
||||
"k8s.io/klog"
|
||||
|
||||
botjoin "github.com/roleypoly/roleypoly/src/functions/bot-join"
|
||||
createsession "github.com/roleypoly/roleypoly/src/functions/create-session"
|
||||
loginbounce "github.com/roleypoly/roleypoly/src/functions/login-bounce"
|
||||
loginhandler "github.com/roleypoly/roleypoly/src/functions/login-handler"
|
||||
sessiondata "github.com/roleypoly/roleypoly/src/functions/session-data"
|
||||
sessionprewarm "github.com/roleypoly/roleypoly/src/functions/session-prewarm"
|
||||
)
|
||||
|
||||
var mappings map[string]http.HandlerFunc = map[string]http.HandlerFunc{
|
||||
"/session-prewarm": sessionprewarm.SessionPrewarm,
|
||||
"/session-data": sessiondata.SessionData,
|
||||
"/bot-join": botjoin.BotJoin,
|
||||
"/session-data": sessiondata.SessionData,
|
||||
"/bot-join": botjoin.BotJoin,
|
||||
"/login-bounce": loginbounce.LoginBounce,
|
||||
"/login-handler": loginhandler.LoginHandler,
|
||||
"/create-session": createsession.CreateSession,
|
||||
}
|
||||
|
||||
var port string
|
||||
|
@ -32,7 +37,7 @@ func main() {
|
|||
}
|
||||
|
||||
for path, handler := range mappings {
|
||||
err := funcframework.RegisterHTTPFunctionContext(ctx, path, handler)
|
||||
err := funcframework.RegisterHTTPFunctionContext(ctx, path, wrapLogging(handler))
|
||||
if err != nil {
|
||||
log.Fatalf("funcframework.RegisterHTTPFunctionContext: %v\n", err)
|
||||
}
|
||||
|
@ -62,3 +67,10 @@ func rootHandler(rw http.ResponseWriter, r *http.Request) {
|
|||
|
||||
fmt.Fprintln(rw, body)
|
||||
}
|
||||
|
||||
func wrapLogging(fn func(rw http.ResponseWriter, r *http.Request)) func(rw http.ResponseWriter, r *http.Request) {
|
||||
return func(rw http.ResponseWriter, r *http.Request) {
|
||||
klog.Info(r.Method, " ", r.RequestURI)
|
||||
fn(rw, r)
|
||||
}
|
||||
}
|
||||
|
|
2
next-env.d.ts
vendored
Normal file
2
next-env.d.ts
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/// <reference types="next" />
|
||||
/// <reference types="next/types/global" />
|
21586
package-lock.json
generated
21586
package-lock.json
generated
File diff suppressed because it is too large
Load diff
39
package.json
39
package.json
|
@ -20,40 +20,44 @@
|
|||
"now-build": "run-s storybook:build",
|
||||
"storybook": "start-storybook -p 6006",
|
||||
"storybook:build": "build-storybook",
|
||||
"test": "jest"
|
||||
"test": "jest",
|
||||
"ui:build": "next build",
|
||||
"ui:dev": "next dev -p 6601"
|
||||
},
|
||||
"dependencies": {
|
||||
"chroma-js": "^2.1.0",
|
||||
"next": "^10.0.2",
|
||||
"next": "^10.0.3",
|
||||
"react": "^17.0.1",
|
||||
"react-custom-scrollbars": "^4.2.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-icons": "^3.11.0",
|
||||
"react-icons": "^4.1.0",
|
||||
"react-is": "^17.0.1",
|
||||
"react-tooltip": "^4.2.11",
|
||||
"styled-components": "^5.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.7",
|
||||
"@storybook/addon-actions": "^6.1.2",
|
||||
"@storybook/addon-essentials": "^6.1.2",
|
||||
"@storybook/addon-links": "^6.1.2",
|
||||
"@storybook/addons": "^6.1.2",
|
||||
"@storybook/react": "^6.1.2",
|
||||
"@storybook/theming": "^6.1.2",
|
||||
"@babel/core": "^7.12.9",
|
||||
"@storybook/addon-actions": "^6.1.9",
|
||||
"@storybook/addon-essentials": "^6.1.9",
|
||||
"@storybook/addon-links": "^6.1.9",
|
||||
"@storybook/addons": "^6.1.9",
|
||||
"@storybook/react": "^6.1.9",
|
||||
"@storybook/theming": "^6.1.9",
|
||||
"@types/chroma-js": "^2.1.2",
|
||||
"@types/enzyme": "^3.10.8",
|
||||
"@types/enzyme-adapter-react-16": "^1.0.6",
|
||||
"@types/jest": "^26.0.15",
|
||||
"@types/jest": "^26.0.16",
|
||||
"@types/minimist": "^1.2.1",
|
||||
"@types/node": "^14.14.9",
|
||||
"@types/node": "^14.14.10",
|
||||
"@types/react": "^17.0.0",
|
||||
"@types/react-dom": "^17.0.0",
|
||||
"@types/styled-components": "^5.1.4",
|
||||
"@typescript-eslint/eslint-plugin": "^4.8.1",
|
||||
"@typescript-eslint/eslint-plugin-tslint": "^4.8.1",
|
||||
"@typescript-eslint/parser": "^4.8.1",
|
||||
"@typescript-eslint/eslint-plugin": "^4.9.0",
|
||||
"@typescript-eslint/eslint-plugin-tslint": "^4.9.0",
|
||||
"@typescript-eslint/parser": "^4.9.0",
|
||||
"babel-jest": "^26.6.3",
|
||||
"babel-loader": "^8.2.1",
|
||||
"babel-loader": "^8.2.2",
|
||||
"babel-plugin-styled-components": "1.12.0",
|
||||
"enzyme": "^3.11.0",
|
||||
"enzyme-adapter-react-16": "^1.15.5",
|
||||
"enzyme-to-json": "^3.6.1",
|
||||
|
@ -70,10 +74,9 @@
|
|||
"jest-styled-components": "^7.0.3",
|
||||
"minimist": "^1.2.5",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.2.0",
|
||||
"prettier": "^2.2.1",
|
||||
"prettier-plugin-packagejson": "^2.2.8",
|
||||
"prettier-plugin-sh": "^0.6.0",
|
||||
"react-is": "^17.0.1",
|
||||
"stylelint": "^13.8.0",
|
||||
"stylelint-config-prettier": "^8.0.2",
|
||||
"stylelint-config-standard": "^20.0.0",
|
||||
|
|
|
@ -43,6 +43,15 @@ func (g GetenvValue) StringSlice(optionalDelimiter ...string) []string {
|
|||
return strings.Split(g.value, delimiter)
|
||||
}
|
||||
|
||||
// SafeURL removes any trailing slash
|
||||
func (g GetenvValue) SafeURL() string {
|
||||
if g.value[len(g.value)-1] == '/' {
|
||||
return g.value[:len(g.value)-1]
|
||||
}
|
||||
|
||||
return g.value
|
||||
}
|
||||
|
||||
func (g GetenvValue) Bool() bool {
|
||||
lowercaseValue := strings.ToLower(g.value)
|
||||
if g.value == "1" || lowercaseValue == "true" || lowercaseValue == "yes" {
|
||||
|
|
|
@ -2,6 +2,7 @@ package common_test
|
|||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/onsi/gomega"
|
||||
|
@ -14,6 +15,8 @@ var (
|
|||
"slice": "hello,world",
|
||||
"slice_no_delim": "hello world",
|
||||
"slice_set_delim": "hello|world",
|
||||
"url": "https://google.com",
|
||||
"url_trailing": "https://google.com/",
|
||||
"number": "10005",
|
||||
"number_bad": "abc123",
|
||||
"bool": "true",
|
||||
|
@ -61,6 +64,19 @@ func TestEnvconfigStringSliceSetDelimeter(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestEnvconfigSafeURL(t *testing.T) {
|
||||
testUrl := common.Getenv("test__url").SafeURL()
|
||||
if strings.HasSuffix(testUrl, "/") {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
func TestEnvconfigSafeURLWithTrailing(t *testing.T) {
|
||||
testUrl := common.Getenv("test__url_trailing").SafeURL()
|
||||
if strings.HasSuffix(testUrl, "/") {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvconfigNumber(t *testing.T) {
|
||||
testNum := common.Getenv("test__number").Number()
|
||||
if testNum != 10005 {
|
||||
|
|
|
@ -62,5 +62,14 @@ func Unstash(rw http.ResponseWriter, req *http.Request, defaultURL string) {
|
|||
redirectURL = cookie.Value
|
||||
}
|
||||
|
||||
unsetter := http.Cookie{
|
||||
Name: "rp_stashed_url",
|
||||
Value: "",
|
||||
MaxAge: -1,
|
||||
HttpOnly: true,
|
||||
}
|
||||
|
||||
rw.Header().Set("set-cookie", unsetter.String())
|
||||
|
||||
Bounce(rw, redirectURL)
|
||||
}
|
||||
|
|
15
src/common/faas/fingerprint.go
Normal file
15
src/common/faas/fingerprint.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package faas
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/roleypoly/roleypoly/src/common/types"
|
||||
)
|
||||
|
||||
func Fingerprint(req *http.Request) types.Fingerprint {
|
||||
return types.Fingerprint{
|
||||
UserAgent: req.UserAgent(),
|
||||
ClientIP: req.RemoteAddr,
|
||||
ForwardedFor: req.Header.Get("x-forwarded-for"),
|
||||
}
|
||||
}
|
20
src/common/types/User.go
Normal file
20
src/common/types/User.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
package types
|
||||
|
||||
type DiscordUser struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Discriminator string `json:"discriminator,omitempty"`
|
||||
Avatar string `json:"avatar,omitempty"`
|
||||
Bot bool `json:"bot,omitempty"`
|
||||
}
|
||||
|
||||
type Member struct {
|
||||
GuildID string `json:"guildid,omitempty"`
|
||||
Roles []string `json:"rolesList,omitempty"`
|
||||
Nick string `json:"nick,omitempty"`
|
||||
User DiscordUser `json:"user,omitempty"`
|
||||
}
|
||||
|
||||
type RoleypolyUser struct {
|
||||
DiscordUser DiscordUser `json:"discorduser,omitempty"`
|
||||
}
|
31
src/common/types/session.go
Normal file
31
src/common/types/session.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package types
|
||||
|
||||
import "time"
|
||||
|
||||
// CreateSessionRequest is the payload to /create-session
|
||||
type CreateSessionRequest struct {
|
||||
AccessTokenResponse AccessTokenResponse
|
||||
Fingerprint Fingerprint
|
||||
}
|
||||
|
||||
type Fingerprint struct {
|
||||
UserAgent string
|
||||
ClientIP string
|
||||
ForwardedFor string
|
||||
}
|
||||
|
||||
type CreateSessionResponse struct {
|
||||
SessionID string
|
||||
}
|
||||
|
||||
type SessionData struct {
|
||||
SessionID string
|
||||
Fingerprint Fingerprint
|
||||
AccessTokens AccessTokenResponse
|
||||
UserData UserData
|
||||
}
|
||||
|
||||
type UserData struct {
|
||||
DataExpires time.Time
|
||||
UserID string
|
||||
}
|
10
src/common/types/tokens.go
Normal file
10
src/common/types/tokens.go
Normal file
|
@ -0,0 +1,10 @@
|
|||
package types
|
||||
|
||||
// AccessTokenResponse is the response for Discord's OAuth token grant flow
|
||||
type AccessTokenResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
TokenType string `json:"token_type"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
Scope string `json:"scope"`
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import styled, { createGlobalStyle } from 'styled-components';
|
||||
import { palette } from 'roleypoly/design-system/atoms/colors';
|
||||
import { fontCSS } from 'roleypoly/design-system/atoms/fonts';
|
||||
|
||||
export const Content = styled.div<{ small?: boolean }>`
|
||||
margin: 0 auto;
|
||||
|
@ -15,6 +16,7 @@ export const GlobalStyles = createGlobalStyle`
|
|||
color: ${palette.grey600};
|
||||
overflow-y: hidden;
|
||||
scroll-behavior: smooth;
|
||||
${fontCSS}
|
||||
}
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
import * as React from 'react';
|
||||
import { organismStories } from 'roleypoly/design-system/organisms/organisms.story';
|
||||
import { ErrorBanner } from './ErrorBanner';
|
||||
import { text } from '@storybook/addon-knobs';
|
||||
|
||||
const story = organismStories('Error Banner', module);
|
||||
|
||||
story.add('Error Banner', () => (
|
||||
<ErrorBanner
|
||||
message={{
|
||||
english: text('English', 'Primary Text'),
|
||||
japanese: text('Japanese (Subtext)', 'Subtext'),
|
||||
friendlyCode: text('Friendly Code', 'Oops!'),
|
||||
}}
|
||||
/>
|
||||
));
|
|
@ -6,11 +6,13 @@ import { PreauthGreeting } from 'roleypoly/design-system/molecules/preauth-greet
|
|||
import { PreauthSecretCode } from 'roleypoly/design-system/molecules/preauth-secret-code';
|
||||
import { Guild } from 'roleypoly/common/types';
|
||||
import styled from 'styled-components';
|
||||
import Link from 'next/link';
|
||||
|
||||
export type PreauthProps = {
|
||||
guildSlug?: Guild;
|
||||
onSendSecretCode: (code: string) => void;
|
||||
botName?: string;
|
||||
discordOAuthLink?: string;
|
||||
};
|
||||
|
||||
const Centered = styled.div`
|
||||
|
@ -33,16 +35,18 @@ export const Preauth = (props: PreauthProps) => {
|
|||
<Centered>
|
||||
{props.guildSlug && <PreauthGreeting guildSlug={props.guildSlug} />}
|
||||
<WidthContainer>
|
||||
<Button
|
||||
color="discord"
|
||||
icon={
|
||||
<div style={{ position: 'relative', top: 3 }}>
|
||||
<FaDiscord />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
Sign in with Discord
|
||||
</Button>
|
||||
<Link href={props.discordOAuthLink || '#'}>
|
||||
<Button
|
||||
color="discord"
|
||||
icon={
|
||||
<div style={{ position: 'relative', top: 3 }}>
|
||||
<FaDiscord />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
Sign in with Discord
|
||||
</Button>
|
||||
</Link>
|
||||
</WidthContainer>
|
||||
<Space />
|
||||
<WidthContainer>
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
import * as React from 'react';
|
||||
import { templateStories } from 'templates/templates.story';
|
||||
import { AuthLogin } from './AuthLogin';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { guild } from 'roleypoly/common/types/storyData';
|
||||
|
||||
const story = templateStories('Login', module);
|
||||
|
||||
story.add('No Slug', () => (
|
||||
<AuthLogin botName="roleypoly#3266" onSendSecretCode={action('secret code!')} />
|
||||
));
|
||||
story.add('With Slug', () => (
|
||||
<AuthLogin
|
||||
botName="roleypoly#3266"
|
||||
guildSlug={guild}
|
||||
onSendSecretCode={action('secret code!')}
|
||||
/>
|
||||
));
|
59
src/functions/create-session/createsession.go
Normal file
59
src/functions/create-session/createsession.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
package createsession
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"cloud.google.com/go/firestore"
|
||||
"k8s.io/klog"
|
||||
|
||||
"github.com/roleypoly/roleypoly/src/common"
|
||||
"github.com/roleypoly/roleypoly/src/common/types"
|
||||
)
|
||||
|
||||
var (
|
||||
firestoreClient *firestore.Client
|
||||
projectID = common.Getenv("GCP_PROJECT_ID").String()
|
||||
firestoreEndpoint = common.Getenv("FIRESTORE_ENDPOINT").String()
|
||||
)
|
||||
|
||||
func init() {
|
||||
var err error // shadow avoidance
|
||||
|
||||
ctx := context.Background()
|
||||
firestoreClient, err = firestore.NewClient(ctx, projectID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func CreateSession(rw http.ResponseWriter, req *http.Request) {
|
||||
requestData := types.CreateSessionRequest{}
|
||||
json.NewDecoder(req.Body).Decode(&requestData)
|
||||
|
||||
klog.Info("Creating session...")
|
||||
|
||||
sessionData := types.SessionData{
|
||||
AccessTokens: requestData.AccessTokenResponse,
|
||||
Fingerprint: requestData.Fingerprint,
|
||||
UserData: types.UserData{},
|
||||
}
|
||||
|
||||
ctx, ctxCancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer ctxCancel()
|
||||
|
||||
docRef, _, err := firestoreClient.Collection("sessions").Add(ctx, sessionData)
|
||||
if err != nil {
|
||||
rw.WriteHeader(500)
|
||||
klog.Error("error: create session in firestore: ", err)
|
||||
}
|
||||
|
||||
klog.Info("Created session ", docRef.ID)
|
||||
|
||||
responseData := types.CreateSessionResponse{
|
||||
SessionID: docRef.ID,
|
||||
}
|
||||
json.NewEncoder(rw).Encode(responseData)
|
||||
}
|
|
@ -14,7 +14,7 @@ import (
|
|||
var (
|
||||
redirectPathTemplate = template.Must(
|
||||
template.New("redirect").Parse(
|
||||
`https://discord.com/api/oauth2/authorize?client_id={{.ClientID}}&scope=identify,guilds&redirect_uri={{.RedirectURI}}&state={{.State}}`,
|
||||
`https://discord.com/api/oauth2/authorize?client_id={{.ClientID}}&response_type=code&scope=identify%20guilds&redirect_uri={{urlquery .RedirectURI}}&state={{.State}}`,
|
||||
),
|
||||
)
|
||||
clientID = common.Getenv("BOT_CLIENT_ID").String()
|
||||
|
|
76
src/functions/login-handler/loginhandler.go
Normal file
76
src/functions/login-handler/loginhandler.go
Normal file
|
@ -0,0 +1,76 @@
|
|||
package loginhandler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/roleypoly/roleypoly/src/common"
|
||||
"github.com/roleypoly/roleypoly/src/common/faas"
|
||||
"github.com/roleypoly/roleypoly/src/common/types"
|
||||
"github.com/segmentio/ksuid"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
var (
|
||||
// HTTPClient is an overridable HTTP client with a 15 second timeout
|
||||
HTTPClient = http.Client{Timeout: 15 * time.Second}
|
||||
uiPath = common.Getenv("UI_PUBLIC_URI").SafeURL()
|
||||
apiPath = common.Getenv("API_PUBLIC_URI").SafeURL()
|
||||
clientID = common.Getenv("BOT_CLIENT_ID").String()
|
||||
clientSecret = common.Getenv("BOT_CLIENT_SECRET").String()
|
||||
redirectURI = common.Getenv("OAUTH_REDIRECT_URI").String()
|
||||
)
|
||||
|
||||
func LoginHandler(rw http.ResponseWriter, req *http.Request) {
|
||||
query := req.URL.Query()
|
||||
|
||||
state := query.Get("state")
|
||||
stateID, err := ksuid.Parse(state)
|
||||
if err != nil || stateID.IsNil() || stateID.Time().Add(5*time.Minute).Before(time.Now()) {
|
||||
faas.Bounce(rw, uiPath+"/machinery/error?error_code=auth_failure")
|
||||
return
|
||||
}
|
||||
|
||||
code := query.Get("code")
|
||||
|
||||
body := url.Values{}
|
||||
body.Set("client_id", clientID)
|
||||
body.Set("client_secret", clientSecret)
|
||||
body.Set("grant_type", "authorization_code")
|
||||
body.Set("code", code)
|
||||
body.Set("redirect_uri", redirectURI)
|
||||
body.Set("scope", "identify guilds")
|
||||
|
||||
response, err := HTTPClient.PostForm("https://discord.com/api/v8/oauth2/token", body)
|
||||
if err != nil {
|
||||
klog.Error("token fetch failed: ", err)
|
||||
faas.Bounce(rw, uiPath+"/machinery/error?error_code=auth_failure")
|
||||
return
|
||||
}
|
||||
|
||||
tokens := types.AccessTokenResponse{}
|
||||
json.NewDecoder(response.Body).Decode(&tokens)
|
||||
|
||||
sessionRequest := types.CreateSessionRequest{
|
||||
AccessTokenResponse: tokens,
|
||||
Fingerprint: faas.Fingerprint(req),
|
||||
}
|
||||
|
||||
buf := bytes.Buffer{}
|
||||
json.NewEncoder(&buf).Encode(sessionRequest)
|
||||
|
||||
response, err = HTTPClient.Post(apiPath+"/create-session", "application/json", &buf)
|
||||
if err != nil {
|
||||
klog.Error("create session failed: ", err)
|
||||
faas.Bounce(rw, uiPath+"/machinery/error?error_code=auth_failure")
|
||||
return
|
||||
}
|
||||
|
||||
session := types.CreateSessionResponse{}
|
||||
json.NewDecoder(response.Body).Decode(&session)
|
||||
|
||||
faas.Bounce(rw, uiPath+"/machinery/new-session?session_id="+session.SessionID)
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package sessionprewarm
|
||||
|
||||
import "net/http"
|
||||
|
||||
func SessionPrewarm(rw http.ResponseWriter, r *http.Request) {
|
||||
rw.Write([]byte("hello work!"))
|
||||
}
|
11
src/pages/_app.tsx
Normal file
11
src/pages/_app.tsx
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { AppProps } from 'next/app';
|
||||
import * as React from 'react';
|
||||
import { InjectTypekitFont } from 'roleypoly/design-system/atoms/fonts';
|
||||
|
||||
const App = (props: AppProps) => (
|
||||
<>
|
||||
<InjectTypekitFont />
|
||||
<props.Component {...props.pageProps} />
|
||||
</>
|
||||
);
|
||||
export default App;
|
30
src/pages/_document.tsx
Normal file
30
src/pages/_document.tsx
Normal file
|
@ -0,0 +1,30 @@
|
|||
import Document, { DocumentContext } from 'next/document';
|
||||
import { ServerStyleSheet } from 'styled-components';
|
||||
|
||||
export default class MyDocument extends Document {
|
||||
static async getInitialProps(ctx: DocumentContext) {
|
||||
const sheet = new ServerStyleSheet();
|
||||
const originalRenderPage = ctx.renderPage;
|
||||
|
||||
try {
|
||||
ctx.renderPage = () =>
|
||||
originalRenderPage({
|
||||
enhanceApp: (App) => (props) =>
|
||||
sheet.collectStyles(<App {...props} />),
|
||||
});
|
||||
|
||||
const initialProps = await Document.getInitialProps(ctx);
|
||||
return {
|
||||
...initialProps,
|
||||
styles: (
|
||||
<>
|
||||
{initialProps.styles}
|
||||
{sheet.getStyleElement()}
|
||||
</>
|
||||
),
|
||||
};
|
||||
} finally {
|
||||
sheet.seal();
|
||||
}
|
||||
}
|
||||
}
|
16
src/pages/auth/login.tsx
Normal file
16
src/pages/auth/login.tsx
Normal file
|
@ -0,0 +1,16 @@
|
|||
import * as React from 'react';
|
||||
import { AuthLogin } from 'roleypoly/design-system/templates/auth-login';
|
||||
|
||||
const loginPage = () => {
|
||||
const onSendSecretCode = (code: string) => {
|
||||
console.log(code);
|
||||
};
|
||||
return (
|
||||
<AuthLogin
|
||||
onSendSecretCode={onSendSecretCode}
|
||||
discordOAuthLink="http://localhost:6600/login-bounce"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default loginPage;
|
5
src/pages/index.tsx
Normal file
5
src/pages/index.tsx
Normal file
|
@ -0,0 +1,5 @@
|
|||
import * as React from 'react';
|
||||
import { LandingTemplate } from 'roleypoly/design-system/templates/landing';
|
||||
|
||||
const Index = () => <LandingTemplate />;
|
||||
export default Index;
|
17
src/pages/machinery/error.tsx
Normal file
17
src/pages/machinery/error.tsx
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { NextPageContext } from 'next';
|
||||
import * as React from 'react';
|
||||
import { Error } from 'roleypoly/design-system/templates/errors';
|
||||
|
||||
type Props = {
|
||||
errorCode: string | number | any;
|
||||
};
|
||||
|
||||
const ErrorPage = (props: Props) => <Error code={props.errorCode} />;
|
||||
|
||||
ErrorPage.getInitialProps = (context: NextPageContext): Props => {
|
||||
return {
|
||||
errorCode: context.err || context.query.error_code,
|
||||
};
|
||||
};
|
||||
|
||||
export default ErrorPage;
|
27
src/pages/machinery/new-session.tsx
Normal file
27
src/pages/machinery/new-session.tsx
Normal file
|
@ -0,0 +1,27 @@
|
|||
import { NextPageContext } from 'next';
|
||||
import * as React from 'react';
|
||||
|
||||
type Props = {
|
||||
sessionID: string;
|
||||
};
|
||||
|
||||
const NewSession = (props: Props) => {
|
||||
const { sessionID } = props;
|
||||
React.useEffect(() => {
|
||||
sessionStorage.setItem('session_key', sessionID);
|
||||
location.href = '/';
|
||||
}, [sessionID]);
|
||||
|
||||
return <div>Logging you in...</div>;
|
||||
};
|
||||
|
||||
NewSession.getInitialProps = (context: NextPageContext): Props => {
|
||||
const sessionID = context.query.session_id;
|
||||
if (!sessionID) {
|
||||
throw new Error("I shouldn't be here today.");
|
||||
}
|
||||
|
||||
return { sessionID: sessionID as string };
|
||||
};
|
||||
|
||||
export default NewSession;
|
|
@ -10,7 +10,7 @@
|
|||
"es2015.iterable",
|
||||
"es2015.core"
|
||||
],
|
||||
"jsx": "react",
|
||||
"jsx": "preserve",
|
||||
"baseUrl": ".",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
|
@ -20,6 +20,13 @@
|
|||
"moduleResolution": "node",
|
||||
"paths": {
|
||||
"roleypoly/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"allowJs": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue