diff --git a/Makefile b/Makefile index 59a0f7d..e4f4e18 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ migrate: - go build -v ./scripts/migrate + go run -v ./scripts/migrate +.PHONY: api api: go build -v -o api -ldflags="-buildid= -X gitlab.com/1f320/pronouns/backend/server.Revision=`git rev-parse --short HEAD`" ./backend diff --git a/backend/main.go b/backend/main.go index 9dfb1f3..ee251d8 100644 --- a/backend/main.go +++ b/backend/main.go @@ -9,15 +9,10 @@ import ( "gitlab.com/1f320/pronouns/backend/log" "gitlab.com/1f320/pronouns/backend/server" - "github.com/joho/godotenv" + _ "github.com/joho/godotenv/autoload" ) func main() { - err := godotenv.Load() - if err != nil { - log.Fatalf("Error loading .env file: %v", err) - } - port := ":" + os.Getenv("PORT") s, err := server.New() diff --git a/backend/routes/auth/discord.go b/backend/routes/auth/discord.go index 9f5b84b..8e7440a 100644 --- a/backend/routes/auth/discord.go +++ b/backend/routes/auth/discord.go @@ -1,6 +1,7 @@ package auth import ( + "fmt" "net/http" "os" @@ -24,8 +25,9 @@ var discordOAuthConfig = oauth2.Config{ } type oauthCallbackRequest struct { - Code string `json:"code"` - State string `json:"state"` + CallbackDomain string `json:"callback_domain"` + Code string `json:"code"` + State string `json:"state"` } type discordCallbackResponse struct { @@ -55,7 +57,9 @@ func (s *Server) discordCallback(w http.ResponseWriter, r *http.Request) error { return server.APIError{Code: server.ErrInvalidState} } - token, err := discordOAuthConfig.Exchange(r.Context(), decoded.Code) + cfg := discordOAuthConfig + cfg.RedirectURL = decoded.CallbackDomain + "/login/discord" + token, err := cfg.Exchange(r.Context(), decoded.Code) if err != nil { log.Errorf("exchanging oauth code: %v", err) @@ -80,6 +84,8 @@ func (s *Server) discordCallback(w http.ResponseWriter, r *http.Request) error { return err } + fmt.Println(token) + render.JSON(w, r, discordCallbackResponse{ HasAccount: true, Token: token, diff --git a/backend/routes/auth/routes.go b/backend/routes/auth/routes.go index 854f90f..6586dc0 100644 --- a/backend/routes/auth/routes.go +++ b/backend/routes/auth/routes.go @@ -6,6 +6,7 @@ import ( "emperror.dev/errors" "github.com/go-chi/chi/v5" "github.com/go-chi/render" + "gitlab.com/1f320/pronouns/backend/log" "gitlab.com/1f320/pronouns/backend/server" ) @@ -18,11 +19,11 @@ func Mount(srv *server.Server, r chi.Router) { r.Route("/auth", func(r chi.Router) { // generate csrf token, returns all supported OAuth provider URLs - r.Get("/urls", server.WrapHandler(s.oauthURLs)) + r.Post("/urls", server.WrapHandler(s.oauthURLs)) r.Route("/discord", func(r chi.Router) { // takes code + state, validates it, returns token OR discord signup ticket - r.Post("/callback", nil) + r.Post("/callback", server.WrapHandler(s.discordCallback)) // takes discord signup ticket to register account r.Post("/signup", nil) }) @@ -30,7 +31,7 @@ func Mount(srv *server.Server, r chi.Router) { } type oauthURLsRequest struct { - CallbackURL string `json:"callback_url"` + CallbackDomain string `json:"callback_domain"` } type oauthURLsResponse struct { @@ -40,6 +41,8 @@ type oauthURLsResponse struct { func (s *Server) oauthURLs(w http.ResponseWriter, r *http.Request) error { req, err := Decode[oauthURLsRequest](r) if err != nil { + log.Error(err) + return server.APIError{Code: server.ErrBadRequest} } @@ -51,7 +54,7 @@ func (s *Server) oauthURLs(w http.ResponseWriter, r *http.Request) error { // copy Discord config and set redirect url discordCfg := discordOAuthConfig - discordCfg.RedirectURL = req.CallbackURL + discordCfg.RedirectURL = req.CallbackDomain + "/login/discord" render.JSON(w, r, oauthURLsResponse{ Discord: discordCfg.AuthCodeURL(state), diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 21ac20f..730d59a 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -3,6 +3,8 @@ import "./App.css"; import Container from "./lib/Container"; import Navigation from "./lib/Navigation"; import Home from "./pages/Home"; +import Discord from "./pages/login/Discord"; +import Login from "./pages/login/Login"; import User from "./pages/User"; function App() { @@ -13,6 +15,8 @@ function App() { } /> } /> + } /> + } /> diff --git a/frontend/src/lib/Navigation.tsx b/frontend/src/lib/Navigation.tsx index 889e3ec..6b127d7 100644 --- a/frontend/src/lib/Navigation.tsx +++ b/frontend/src/lib/Navigation.tsx @@ -1,15 +1,30 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; import { Link } from "react-router-dom"; import { MoonStars, Sun, List } from "react-bootstrap-icons"; import NavItem from "./NavItem"; import Logo from "./logo"; +import { useRecoilState } from "recoil"; +import { userState } from "./store"; +import fetchAPI from "./fetch"; +import { MeUser } from "./types"; function Navigation() { + const [user, setUser] = useRecoilState(userState); + + useEffect(() => { + if (user) return; + + fetchAPI("/users/@me").then( + (res) => setUser(res), + (err) => console.log("fetching /users/@me", err) + ); + }, []); + const [darkTheme, setDarkTheme] = useState( localStorage.theme === "dark" || - (!("theme" in localStorage) && - window.matchMedia("(prefers-color-scheme: dark)").matches) + (!("theme" in localStorage) && + window.matchMedia("(prefers-color-scheme: dark)").matches) ); const [showMenu, setShowMenu] = useState(false); @@ -28,6 +43,18 @@ function Navigation() { } }; + const nav = user ? ( + <> + @{user.username} + Settings + Log out + + ) : ( + <> + Log in + + ); + return ( <>
@@ -39,14 +66,7 @@ function Navigation() {
)}
-
setShowMenu(!showMenu)} title="Show menu" className="cursor-pointer flex lg:hidden"> - +
setShowMenu(!showMenu)} + title="Show menu" + className="cursor-pointer flex lg:hidden" + > +
@@ -74,15 +101,12 @@ function Navigation() {
-