better castle
This commit is contained in:
21
main.go
Normal file
21
main.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/alexedwards/argon2id"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
password := "mmmmmmmm"
|
||||||
|
|
||||||
|
// Use default recommended parameters
|
||||||
|
hash, err := argon2id.CreateHash(password, argon2id.DefaultParams)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Argon2 Hash:")
|
||||||
|
fmt.Println(hash)
|
||||||
|
}
|
||||||
67
server/db/db.go
Normal file
67
server/db/db.go
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/alexedwards/argon2id"
|
||||||
|
)
|
||||||
|
|
||||||
|
var DB map[string]interface{}
|
||||||
|
|
||||||
|
type GetService struct{}
|
||||||
|
var Get = GetService{}
|
||||||
|
|
||||||
|
func (g GetService) User(id string) (map[string]interface{}, error) {
|
||||||
|
raw, ok := DB[id]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("user not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
userData, ok := raw.(map[string]interface{})
|
||||||
|
log.Println(userData)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("user data is not in expected format")
|
||||||
|
}
|
||||||
|
|
||||||
|
return userData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitDB() error {
|
||||||
|
file, err := os.Open("../db/users.json")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error opening file:", err)
|
||||||
|
return errors.New("Failed to read db")
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
var data interface{}
|
||||||
|
|
||||||
|
err = json.NewDecoder(file).Decode(&data)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error decoding JSON:", err)
|
||||||
|
return errors.New("Failed to decode db")
|
||||||
|
}
|
||||||
|
|
||||||
|
result, ok := data.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
fmt.Println("Data is not a JSON object (map)")
|
||||||
|
return errors.New("Db is in the wrong format")
|
||||||
|
}
|
||||||
|
|
||||||
|
DB = result
|
||||||
|
|
||||||
|
// Use default recommended parameters
|
||||||
|
hash, err := argon2id.CreateHash("hunter2", argon2id.DefaultParams)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Argon2 Hash:")
|
||||||
|
fmt.Println(hash)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
module hyperia
|
module hyperia
|
||||||
|
|
||||||
go 1.23.0
|
go 1.24.0
|
||||||
|
|
||||||
toolchain go1.24.2
|
toolchain go1.24.5
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alexedwards/argon2id v1.0.0
|
github.com/alexedwards/argon2id v1.0.0
|
||||||
@@ -23,6 +23,11 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/yuin/goldmark v1.7.13 // indirect
|
||||||
golang.org/x/crypto v0.14.0 // indirect
|
golang.org/x/crypto v0.14.0 // indirect
|
||||||
|
golang.org/x/mod v0.27.0 // indirect
|
||||||
golang.org/x/sys v0.35.0 // indirect
|
golang.org/x/sys v0.35.0 // indirect
|
||||||
|
golang.org/x/tools v0.36.1-0.20250903222949-a5c0eb837c9f // indirect
|
||||||
|
golang.org/x/tools/cmd/godoc v0.1.0-deprecated // indirect
|
||||||
|
golang.org/x/tools/godoc v0.1.0-deprecated // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -46,12 +46,16 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
|||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA=
|
||||||
|
github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||||
|
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
@@ -88,6 +92,12 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
|
|||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
|
golang.org/x/tools v0.36.1-0.20250903222949-a5c0eb837c9f h1:jDEaVlf+r7N8Re8Es5pGylGkfnqcx9dfUCsd1T+biTs=
|
||||||
|
golang.org/x/tools v0.36.1-0.20250903222949-a5c0eb837c9f/go.mod h1:n+8pplxVZfXnmHBxWsfPnQRJ5vWroQDk+U2MFpjwtFY=
|
||||||
|
golang.org/x/tools/cmd/godoc v0.1.0-deprecated h1:sEGTwp9aZNTHsdf/2BGaRqE4ZLndRVH17rbQ2OVun9Q=
|
||||||
|
golang.org/x/tools/cmd/godoc v0.1.0-deprecated/go.mod h1:J6VY4iFch6TIm456U3fnw1EJZaIqcYlhHu6GpHQ9HJk=
|
||||||
|
golang.org/x/tools/godoc v0.1.0-deprecated h1:o+aZ1BOj6Hsx/GBdJO/s815sqftjSnrZZwyYTHODvtk=
|
||||||
|
golang.org/x/tools/godoc v0.1.0-deprecated/go.mod h1:qM63CriJ961IHWmnWa9CjZnBndniPt4a3CK0PVB9bIg=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||||
|
|||||||
@@ -2,23 +2,24 @@ package handlers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
// "errors"
|
"errors"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
// "strings"
|
// "strings"
|
||||||
|
|
||||||
|
"hyperia/db"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/alexedwards/argon2id"
|
||||||
|
|
||||||
// "github.com/alexedwards/argon2id"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type loginRequest struct {
|
type loginRequest struct {
|
||||||
Name string `json:"name"`
|
Email string `json:"email"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type user struct {
|
type user struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
Name string `json:"name"`
|
Email string `json:"email"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleLogin(w http.ResponseWriter, r *http.Request) {
|
func HandleLogin(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -33,75 +34,38 @@ func HandleLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// user, err := getUserByCredentials(creds.Name, creds.Password)
|
user, err := getUserByCredentials(creds)
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// http.Error(w, "Unauthorized: "+err.Error(), http.StatusUnauthorized)
|
http.Error(w, "Unauthorized: "+ err.Error(), http.StatusUnauthorized)
|
||||||
// return
|
return
|
||||||
// }
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
http.Error(w, "Not implemented", http.StatusMethodNotAllowed)
|
http.Error(w, "Not implemented", http.StatusMethodNotAllowed)
|
||||||
// json.NewEncoder(w).Encode(user)
|
json.NewEncoder(w).Encode(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
// func getUserByCredentials(name string, password string) (*user, error) {
|
func getUserByCredentials(loginCreds loginRequest) (map[string]interface{}, error) {
|
||||||
// var id int
|
|
||||||
// var dbName, dbHash string
|
|
||||||
|
|
||||||
// name = strings.TrimSpace(strings.ToLower(name))
|
// email := strings.TrimSpace(strings.ToLower(loginCreds.Email))
|
||||||
// err := DB.QueryRow("SELECT id, name, password FROM users WHERE LOWER(name) = LOWER($1)", name).Scan(&id, &dbName, &dbHash)
|
|
||||||
// if err != nil {
|
user, err := db.Get.User("1")
|
||||||
// return nil, errors.New("user not found")
|
// err := DB.QueryRow("SELECT id, name, password FROM users WHERE LOWER(name) = LOWER($1)", name).Scan(&id, &dbName, &dbHash)
|
||||||
// }
|
|
||||||
|
|
||||||
// match, err := argon2id.ComparePasswordAndHash(password, dbHash)
|
|
||||||
// if err != nil || !match {
|
|
||||||
// return nil, errors.New("invalid password")
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return &user{
|
|
||||||
// ID: id,
|
|
||||||
// Name: dbName,
|
|
||||||
// }, nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
func HandleApplicantLogin(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if r.Method != http.MethodPost {
|
|
||||||
http.Error(w, "Only POST allowed", http.StatusMethodNotAllowed)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var creds loginRequest
|
|
||||||
if err := json.NewDecoder(r.Body).Decode(&creds); err != nil {
|
|
||||||
http.Error(w, "Invalid JSON", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// exists, err := EmailExists(creds.Name)
|
|
||||||
// if err != nil {
|
|
||||||
// log.Err(err).Msg("error checking email")
|
|
||||||
// http.Error(w, "Internal server error", http.StatusInternalServerError)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// if !exists {
|
|
||||||
// http.Error(w, "Email does not exist.", http.StatusConflict)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
token, err := generateVerificationToken(creds.Name)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Err(err).Msg("error generating verification token")
|
return nil, errors.New("user not found")
|
||||||
http.Error(w, "Error, please try again later.", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = sendWelcomeEmail(creds.Name, token)
|
dbPassword, ok := user["password"].(string)
|
||||||
if err != nil {
|
if !ok {
|
||||||
log.Err(err).Msg("error sending welcome email")
|
return nil, errors.New("password format is invalid")
|
||||||
http.Error(w, "Failed to send email", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusOK)
|
log.Println("pass: ", loginCreds, loginCreds.Password, dbPassword)
|
||||||
w.Write([]byte("OK"))
|
|
||||||
}
|
match, err := argon2id.ComparePasswordAndHash(loginCreds.Password, dbPassword)
|
||||||
|
if err != nil || !match {
|
||||||
|
return nil, errors.New("invalid password")
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"hyperia/config"
|
"hyperia/config"
|
||||||
|
"hyperia/db"
|
||||||
"hyperia/handlers"
|
"hyperia/handlers"
|
||||||
"hyperia/logger"
|
"hyperia/logger"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
@@ -16,15 +17,14 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
config.SetConfiguration()
|
config.SetConfiguration()
|
||||||
|
|
||||||
logger.ConfigureLogger()
|
logger.ConfigureLogger()
|
||||||
|
|
||||||
// err := handlers.InitDB()
|
err := db.InitDB()
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// log.Fatal().Msgf("failed to connect to database: %v", err)
|
log.Fatal().Msgf("failed to connect to database: %v", err)
|
||||||
// } else {
|
} else {
|
||||||
// log.Info().Msg("successfully connected to PostgreSQL")
|
log.Info().Msg("successfully connected to PostgreSQL")
|
||||||
// }
|
}
|
||||||
|
|
||||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -55,7 +55,7 @@ func main() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
log.Info().Msgf("Server starting on http://localhost: %s", config.PORT)
|
log.Info().Msgf("Server starting on http://localhost: %s", config.PORT)
|
||||||
err := http.ListenAndServe(":"+config.PORT, nil)
|
err = http.ListenAndServe(":"+config.PORT, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Msgf("failed to start server: %v", err)
|
log.Fatal().Msgf("failed to start server: %v", err)
|
||||||
}
|
}
|
||||||
@@ -67,10 +67,6 @@ func handlePublic(w http.ResponseWriter, r *http.Request) {
|
|||||||
handlers.HandleLogin(w, r)
|
handlers.HandleLogin(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if r.URL.Path == "/api/applicantlogin" {
|
|
||||||
handlers.HandleApplicantLogin(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if r.URL.Path == "/api/join" {
|
if r.URL.Path == "/api/join" {
|
||||||
handlers.HandleJoin(w, r)
|
handlers.HandleJoin(w, r)
|
||||||
return
|
return
|
||||||
|
|||||||
3094
ui/_/images/castle-dark3.svg
Normal file
3094
ui/_/images/castle-dark3.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 1.6 MiB |
@@ -20,12 +20,12 @@
|
|||||||
}}
|
}}
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
.main-image {
|
.main-image {
|
||||||
content:url("_/images/castle-dark2.svg");
|
content:url("_/images/castle-dark3.svg");
|
||||||
height: 120vmin; bottom: -17vmin; left: 31vw;
|
height: 1000px; bottom: -34vmin; left: 26vw;
|
||||||
}}
|
}}
|
||||||
@media (prefers-color-scheme: dark) and (max-width: 600px) {
|
@media (prefers-color-scheme: dark) and (max-width: 600px) {
|
||||||
.main-image {
|
.main-image {
|
||||||
max-width: 195vw; height: 80vh; bottom: -17vmin; left: 0vw;
|
max-width: 195vw; height: 80vh; left: 0vw;
|
||||||
}}
|
}}
|
||||||
</style>
|
</style>
|
||||||
<script src="_/code/util.js"></script>
|
<script src="_/code/util.js"></script>
|
||||||
@@ -44,6 +44,15 @@
|
|||||||
<span>|</span>
|
<span>|</span>
|
||||||
<a href="signin">sign in</a>
|
<a href="signin">sign in</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div style="display: flex; flex-direction: column; top: 110vh; left: 50vw; transform: translateX(-50%); align-items: left; position: absolute;">
|
||||||
|
<h1 style="font-family: Canterbury; font-size: 4rem; margin-left: auto">A Classical Christian Society</h1>
|
||||||
|
<p>Hyperia is a society for people who want to uphold European tradition and Christian values.</p>
|
||||||
|
<p>Inspired by the Classical Christian schooling movement that began in the 1990s, Hyperia aims to create a similar space for adults.</p>
|
||||||
|
<div style="height: 20vh"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.link, a {
|
.link, a {
|
||||||
transition: background-color 0.3s ease, scale 0.3s;
|
transition: background-color 0.3s ease, scale 0.3s;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<style>
|
<style>
|
||||||
#items {
|
#items {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 45vh;
|
top: 50vh;
|
||||||
left: 50vw;
|
left: 50vw;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
|
|
||||||
@@ -18,29 +18,6 @@
|
|||||||
text-align: center; /* ensures text inside spans is centered */
|
text-align: center; /* ensures text inside spans is centered */
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
|
||||||
cursor: default;
|
|
||||||
text-decoration: underline;
|
|
||||||
text-underline-offset: 5px;
|
|
||||||
transition: background .02s, color .2s;
|
|
||||||
user-select: none;
|
|
||||||
padding: 4px;
|
|
||||||
border-radius: 5px;
|
|
||||||
display: inline-block; /* makes background and padding behave */
|
|
||||||
padding: 0.2em 0.5em; /* adds breathing room */
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
text-decoration: none;
|
|
||||||
background: var(--green);
|
|
||||||
color: var(--tan);
|
|
||||||
}
|
|
||||||
|
|
||||||
a:active {
|
|
||||||
background: var(--red); /* background color works now */
|
|
||||||
color: white; /* optional: change text color for contrast */
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
input {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
@@ -78,8 +55,6 @@
|
|||||||
width: 50vw
|
width: 50vw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script src="_/code/util.js"></script>
|
<script src="_/code/util.js"></script>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
@@ -91,9 +66,43 @@
|
|||||||
<body>
|
<body>
|
||||||
<span id="title" onclick='console.log("hey"); window.location.href="/"'>hyperia</span>
|
<span id="title" onclick='console.log("hey"); window.location.href="/"'>hyperia</span>
|
||||||
<div id="items">
|
<div id="items">
|
||||||
<input placeholder="email"></input>
|
<input id="email" placeholder="email"></input>
|
||||||
<br>
|
<br>
|
||||||
<input placeholder="password"></input>
|
<input id="password" placeholder="password"></input>
|
||||||
|
<br>
|
||||||
|
<p id="applicant-message" style="color: green; margin-left: 10px; display: inline-block; margin: 0px; margin-left: 20px; font-size: 1em"></p>
|
||||||
|
<br>
|
||||||
|
<div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button onclick="login()" style="background-color: rgb(193, 135, 29)">Sign In
|
||||||
|
<script>
|
||||||
|
async function login() {
|
||||||
|
const email = document.getElementById('email').value;
|
||||||
|
const password = document.getElementById('password').value;
|
||||||
|
const messageEl = document.getElementById('applicant-message');
|
||||||
|
|
||||||
|
const res = await fetch('/api/login', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ email, password }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
const data = await res.text();
|
||||||
|
messageEl.style.color = "green"
|
||||||
|
messageEl.textContent = "Check your email for a login link.";
|
||||||
|
} else {
|
||||||
|
const error = await res.text();
|
||||||
|
console.log(error)
|
||||||
|
messageEl.style.color = "red"
|
||||||
|
messageEl.textContent = "Error: " + error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
See https://github.com/return-to-the-land/go-backend for instructions.
|
See https://github.com/return-to-the-land/go-backend for instructions.
|
||||||
|
|
||||||
# Style Guidelines
|
# Style Guidelines
|
||||||
- Font size is defined in the index.html. Only use rem as a unit.
|
- Font size is defined in the index.html. Only use rem as a unit.
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
```go install golang.org/x/tools/cmd/godoc@latest```
|
||||||
|
```$HOME/go/bin/godoc -http=:6060``` (to run on Mac)
|
||||||
Reference in New Issue
Block a user