✨ Sign up & Sign in
This commit is contained in:
@ -1,7 +1,12 @@
|
||||
{
|
||||
"next": "Next",
|
||||
"email": "Email",
|
||||
"username": "Username",
|
||||
"nickname": "Nickname",
|
||||
"password": "Password",
|
||||
"magicToken": "Magic Token",
|
||||
"signinTitle": "Sign In",
|
||||
"signinCaption": "Sign in to Solarpass to explore entire Solar Network. Explore posts, discover communities, talk with your best friends. All these things in the Solar Network!"
|
||||
"signinCaption": "Sign in to Solarpass to explore entire Solar Network. Explore posts, discover communities, talk with your best friends. All these things in the Solar Network!",
|
||||
"signupTitle": "Sign Up",
|
||||
"signupCaption": "Sign up to create an account on Solarpass, then you can explore the entire Solar Network! Enjoy the next-generation Internet Ecosystem!"
|
||||
}
|
@ -1,7 +1,12 @@
|
||||
{
|
||||
"next": "下一步",
|
||||
"email": "邮件地址",
|
||||
"username": "用户名",
|
||||
"nickname": "昵称",
|
||||
"password": "密码",
|
||||
"magicToken": "魔法令牌",
|
||||
"signinTitle": "登陆",
|
||||
"signinCaption": "登陆 Solarpass 以探索整个 Solar Network,浏览帖子、探索社区、和你的好朋友聊八卦,一切尽在 Solar Network!"
|
||||
"signinCaption": "登陆 Solarpass 以探索整个 Solar Network,浏览帖子、探索社区、和你的好朋友聊八卦,一切尽在 Solar Network!",
|
||||
"signupTitle": "注册",
|
||||
"signupCaption": "注册以在 Solarpass 创建一个账号,之后你就可以探索整个 Solar Network,享受下一代互联网生态系统!"
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.solsynth.dev/hydrogen/passport/pkg/utils"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@ -83,7 +84,7 @@ func editUserinfo(c *fiber.Ctx) error {
|
||||
Birthday time.Time `json:"birthday"`
|
||||
}
|
||||
|
||||
if err := BindAndValidate(c, &data); err != nil {
|
||||
if err := utils.BindAndValidate(c, &data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -133,7 +134,7 @@ func doRegister(c *fiber.Ctx) error {
|
||||
MagicToken string `json:"magic_token"`
|
||||
}
|
||||
|
||||
if err := BindAndValidate(c, &data); err != nil {
|
||||
if err := utils.BindAndValidate(c, &data); err != nil {
|
||||
return err
|
||||
} else if viper.GetBool("use_registration_magic_token") && len(data.MagicToken) <= 0 {
|
||||
return fmt.Errorf("missing magic token in request")
|
||||
@ -162,7 +163,7 @@ func doRegisterConfirm(c *fiber.Ctx) error {
|
||||
Code string `json:"code" validate:"required"`
|
||||
}
|
||||
|
||||
if err := BindAndValidate(c, &data); err != nil {
|
||||
if err := utils.BindAndValidate(c, &data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.solsynth.dev/hydrogen/passport/pkg/utils"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
@ -15,7 +16,7 @@ func doAuthenticate(c *fiber.Ctx) error {
|
||||
Password string `json:"password" validate:"required"`
|
||||
}
|
||||
|
||||
if err := BindAndValidate(c, &data); err != nil {
|
||||
if err := utils.BindAndValidate(c, &data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -47,7 +48,7 @@ func doMultiFactorAuthenticate(c *fiber.Ctx) error {
|
||||
Code string `json:"code" validate:"required"`
|
||||
}
|
||||
|
||||
if err := BindAndValidate(c, &data); err != nil {
|
||||
if err := utils.BindAndValidate(c, &data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -84,7 +85,7 @@ func getToken(c *fiber.Ctx) error {
|
||||
GrantType string `json:"grant_type" form:"grant_type"`
|
||||
}
|
||||
|
||||
if err := BindAndValidate(c, &data); err != nil {
|
||||
if err := utils.BindAndValidate(c, &data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ package server
|
||||
import (
|
||||
"git.solsynth.dev/hydrogen/passport/pkg/models"
|
||||
"git.solsynth.dev/hydrogen/passport/pkg/services"
|
||||
"git.solsynth.dev/hydrogen/passport/pkg/utils"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
@ -70,7 +71,7 @@ func editFriendship(c *fiber.Ctx) error {
|
||||
Status uint8 `json:"status"`
|
||||
}
|
||||
|
||||
if err := BindAndValidate(c, &data); err != nil {
|
||||
if err := utils.BindAndValidate(c, &data); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"git.solsynth.dev/hydrogen/passport/pkg/utils"
|
||||
"time"
|
||||
|
||||
"git.solsynth.dev/hydrogen/passport/pkg/database"
|
||||
@ -72,7 +73,7 @@ func addNotifySubscriber(c *fiber.Ctx) error {
|
||||
DeviceID string `json:"device_id" validate:"required"`
|
||||
}
|
||||
|
||||
if err := BindAndValidate(c, &data); err != nil {
|
||||
if err := utils.BindAndValidate(c, &data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ package server
|
||||
import (
|
||||
"git.solsynth.dev/hydrogen/passport/pkg/models"
|
||||
"git.solsynth.dev/hydrogen/passport/pkg/services"
|
||||
"git.solsynth.dev/hydrogen/passport/pkg/utils"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
@ -18,7 +19,7 @@ func notifyUser(c *fiber.Ctx) error {
|
||||
UserID uint `json:"user_id" validate:"required"`
|
||||
}
|
||||
|
||||
if err := BindAndValidate(c, &data); err != nil {
|
||||
if err := utils.BindAndValidate(c, &data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ package server
|
||||
import (
|
||||
"git.solsynth.dev/hydrogen/passport/pkg/database"
|
||||
"git.solsynth.dev/hydrogen/passport/pkg/models"
|
||||
"git.solsynth.dev/hydrogen/passport/pkg/utils"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
@ -47,7 +48,7 @@ func editPersonalPage(c *fiber.Ctx) error {
|
||||
Links []models.AccountPageLinks `json:"links"`
|
||||
}
|
||||
|
||||
if err := BindAndValidate(c, &data); err != nil {
|
||||
if err := utils.BindAndValidate(c, &data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -1,26 +0,0 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||
)
|
||||
|
||||
func signinPage(c *fiber.Ctx) error {
|
||||
localizer := c.Locals("localizer").(*i18n.Localizer)
|
||||
|
||||
next, _ := localizer.LocalizeMessage(&i18n.Message{ID: "next"})
|
||||
username, _ := localizer.LocalizeMessage(&i18n.Message{ID: "username"})
|
||||
password, _ := localizer.LocalizeMessage(&i18n.Message{ID: "password"})
|
||||
title, _ := localizer.LocalizeMessage(&i18n.Message{ID: "signinTitle"})
|
||||
caption, _ := localizer.LocalizeMessage(&i18n.Message{ID: "signinCaption"})
|
||||
|
||||
return c.Render("views/signin", fiber.Map{
|
||||
"i18n": fiber.Map{
|
||||
"next": next,
|
||||
"username": username,
|
||||
"password": password,
|
||||
"title": title,
|
||||
"caption": caption,
|
||||
},
|
||||
}, "views/layouts/auth")
|
||||
}
|
@ -4,5 +4,10 @@ import "github.com/gofiber/fiber/v2"
|
||||
|
||||
func MapUserInterface(A *fiber.App) {
|
||||
pages := A.Group("/").Name("Pages")
|
||||
|
||||
pages.Get("/sign-up", signupPage)
|
||||
pages.Get("/sign-in", signinPage)
|
||||
|
||||
pages.Post("/sign-up", signupAction)
|
||||
pages.Post("/sign-in", signinAction)
|
||||
}
|
||||
|
77
pkg/server/ui/signin.go
Normal file
77
pkg/server/ui/signin.go
Normal file
@ -0,0 +1,77 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.solsynth.dev/hydrogen/passport/pkg/services"
|
||||
"git.solsynth.dev/hydrogen/passport/pkg/utils"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||
"github.com/sujit-baniya/flash"
|
||||
)
|
||||
|
||||
func signinPage(c *fiber.Ctx) error {
|
||||
localizer := c.Locals("localizer").(*i18n.Localizer)
|
||||
|
||||
next, _ := localizer.LocalizeMessage(&i18n.Message{ID: "next"})
|
||||
username, _ := localizer.LocalizeMessage(&i18n.Message{ID: "username"})
|
||||
password, _ := localizer.LocalizeMessage(&i18n.Message{ID: "password"})
|
||||
signup, _ := localizer.LocalizeMessage(&i18n.Message{ID: "signupTitle"})
|
||||
title, _ := localizer.LocalizeMessage(&i18n.Message{ID: "signinTitle"})
|
||||
caption, _ := localizer.LocalizeMessage(&i18n.Message{ID: "signinCaption"})
|
||||
|
||||
return c.Render("views/signin", fiber.Map{
|
||||
"info": flash.Get(c)["message"],
|
||||
"i18n": fiber.Map{
|
||||
"next": next,
|
||||
"username": username,
|
||||
"password": password,
|
||||
"signup": signup,
|
||||
"title": title,
|
||||
"caption": caption,
|
||||
},
|
||||
}, "views/layouts/auth")
|
||||
}
|
||||
|
||||
func signinAction(c *fiber.Ctx) error {
|
||||
var data struct {
|
||||
Username string `form:"username" validate:"required"`
|
||||
Password string `form:"password" validate:"required"`
|
||||
}
|
||||
|
||||
if err := utils.BindAndValidate(c, &data); err != nil {
|
||||
return flash.WithInfo(c, fiber.Map{
|
||||
"message": err.Error(),
|
||||
}).Redirect("/sign-in")
|
||||
}
|
||||
|
||||
user, err := services.LookupAccount(data.Username)
|
||||
if err != nil {
|
||||
return flash.WithInfo(c, fiber.Map{
|
||||
"message": fmt.Sprintf("account was not found: %v", err.Error()),
|
||||
}).Redirect("/sign-in")
|
||||
}
|
||||
|
||||
ticket, err := services.NewTicket(user, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||
if err != nil {
|
||||
return flash.WithInfo(c, fiber.Map{
|
||||
"message": fmt.Sprintf("unable setup ticket: %v", err.Error()),
|
||||
}).Redirect("/sign-in")
|
||||
}
|
||||
|
||||
ticket, err = services.ActiveTicketWithPassword(ticket, data.Password)
|
||||
if err != nil {
|
||||
return flash.WithInfo(c, fiber.Map{
|
||||
"message": fmt.Sprintf("invalid password: %v", err.Error()),
|
||||
}).Redirect("/sign-in")
|
||||
}
|
||||
|
||||
if ticket.IsAvailable() != nil {
|
||||
return flash.WithInfo(c, fiber.Map{
|
||||
"message": "multi factor authenticate required",
|
||||
}).Redirect("/sign-in")
|
||||
} else {
|
||||
return flash.WithInfo(c, fiber.Map{
|
||||
"message": "done",
|
||||
}).Redirect("/sign-in")
|
||||
}
|
||||
}
|
86
pkg/server/ui/signup.go
Normal file
86
pkg/server/ui/signup.go
Normal file
@ -0,0 +1,86 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.solsynth.dev/hydrogen/passport/pkg/database"
|
||||
"git.solsynth.dev/hydrogen/passport/pkg/models"
|
||||
"git.solsynth.dev/hydrogen/passport/pkg/services"
|
||||
"git.solsynth.dev/hydrogen/passport/pkg/utils"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/sujit-baniya/flash"
|
||||
)
|
||||
|
||||
func signupPage(c *fiber.Ctx) error {
|
||||
localizer := c.Locals("localizer").(*i18n.Localizer)
|
||||
|
||||
next, _ := localizer.LocalizeMessage(&i18n.Message{ID: "next"})
|
||||
email, _ := localizer.LocalizeMessage(&i18n.Message{ID: "email"})
|
||||
nickname, _ := localizer.LocalizeMessage(&i18n.Message{ID: "nickname"})
|
||||
username, _ := localizer.LocalizeMessage(&i18n.Message{ID: "username"})
|
||||
password, _ := localizer.LocalizeMessage(&i18n.Message{ID: "password"})
|
||||
magicToken, _ := localizer.LocalizeMessage(&i18n.Message{ID: "magicToken"})
|
||||
signin, _ := localizer.LocalizeMessage(&i18n.Message{ID: "signinTitle"})
|
||||
title, _ := localizer.LocalizeMessage(&i18n.Message{ID: "signupTitle"})
|
||||
caption, _ := localizer.LocalizeMessage(&i18n.Message{ID: "signupCaption"})
|
||||
|
||||
return c.Render("views/signup", fiber.Map{
|
||||
"info": flash.Get(c)["message"],
|
||||
"use_magic_token": viper.GetBool("use_registration_magic_token"),
|
||||
"i18n": fiber.Map{
|
||||
"next": next,
|
||||
"email": email,
|
||||
"username": username,
|
||||
"nickname": nickname,
|
||||
"password": password,
|
||||
"magic_token": magicToken,
|
||||
"signin": signin,
|
||||
"title": title,
|
||||
"caption": caption,
|
||||
},
|
||||
}, "views/layouts/auth")
|
||||
}
|
||||
|
||||
func signupAction(c *fiber.Ctx) error {
|
||||
var data struct {
|
||||
Name string `form:"name" validate:"required,lowercase,alphanum,min=4,max=16"`
|
||||
Nick string `form:"nick" validate:"required,min=4,max=24"`
|
||||
Email string `form:"email" validate:"required,email"`
|
||||
Password string `form:"password" validate:"required,min=4,max=32"`
|
||||
MagicToken string `form:"magic_token"`
|
||||
}
|
||||
|
||||
if err := utils.BindAndValidate(c, &data); err != nil {
|
||||
return flash.WithInfo(c, fiber.Map{
|
||||
"message": err.Error(),
|
||||
}).Redirect("/sign-up")
|
||||
} else if viper.GetBool("use_registration_magic_token") && len(data.MagicToken) <= 0 {
|
||||
return flash.WithInfo(c, fiber.Map{
|
||||
"message": "magic token was required",
|
||||
}).Redirect("/sign-up")
|
||||
} else if viper.GetBool("use_registration_magic_token") {
|
||||
if tk, err := services.ValidateMagicToken(data.MagicToken, models.RegistrationMagicToken); err != nil {
|
||||
return flash.WithInfo(c, fiber.Map{
|
||||
"message": fmt.Sprintf("magic token was invalid: %v", err.Error()),
|
||||
}).Redirect("/sign-up")
|
||||
} else {
|
||||
database.C.Delete(&tk)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := services.CreateAccount(
|
||||
data.Name,
|
||||
data.Nick,
|
||||
data.Email,
|
||||
data.Password,
|
||||
); err != nil {
|
||||
return flash.WithInfo(c, fiber.Map{
|
||||
"message": err.Error(),
|
||||
}).Redirect("/sign-up")
|
||||
} else {
|
||||
return flash.WithInfo(c, fiber.Map{
|
||||
"message": "account has been created. now you can sign in!",
|
||||
}).Redirect("/sign-in")
|
||||
}
|
||||
}
|
@ -34,14 +34,15 @@ func NewTicket(user models.Account, ip, ua string) (models.AuthTicket, error) {
|
||||
}
|
||||
|
||||
ticket = models.AuthTicket{
|
||||
Claims: []string{"*"},
|
||||
Audiences: []string{"passport"},
|
||||
IpAddress: ip,
|
||||
UserAgent: ua,
|
||||
RequireMFA: DetectRisk(user, ip, ua),
|
||||
ExpiredAt: lo.ToPtr(time.Now().Add(2 * time.Hour)),
|
||||
AvailableAt: nil,
|
||||
AccountID: user.ID,
|
||||
Claims: []string{"*"},
|
||||
Audiences: []string{"passport"},
|
||||
IpAddress: ip,
|
||||
UserAgent: ua,
|
||||
RequireMFA: DetectRisk(user, ip, ua),
|
||||
RequireAuthenticate: true,
|
||||
ExpiredAt: lo.ToPtr(time.Now().Add(2 * time.Hour)),
|
||||
AvailableAt: nil,
|
||||
AccountID: user.ID,
|
||||
}
|
||||
|
||||
err := database.C.Save(&ticket).Error
|
||||
@ -81,7 +82,7 @@ func ActiveTicketWithPassword(ticket models.AuthTicket, password string) (models
|
||||
if ticket.AvailableAt != nil {
|
||||
return ticket, nil
|
||||
} else if !ticket.RequireAuthenticate {
|
||||
return ticket, fmt.Errorf("detected risk, multi factor authentication required")
|
||||
return ticket, nil
|
||||
}
|
||||
|
||||
if factor, err := GetPasswordFactor(ticket.AccountID); err != nil {
|
||||
|
@ -1,4 +1,4 @@
|
||||
package server
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/go-playground/validator/v10"
|
@ -5,8 +5,16 @@
|
||||
|
||||
<body>
|
||||
<div class="wrapper-container">
|
||||
<div class="wrapper-card">
|
||||
{{embed}}
|
||||
<div class="wrapper-middleware">
|
||||
{{if ne .info nil}}
|
||||
<div class="animate__animated animate__fadeInDown alert">
|
||||
<div class="content">{{.info}}</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div class="wrapper-card">
|
||||
{{embed}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
@ -20,9 +28,19 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.wrapper-card {
|
||||
.wrapper-middleware {
|
||||
width: 100%;
|
||||
max-width: 720px;
|
||||
min-width: 0;
|
||||
max-width: min(800px, 100dvw);
|
||||
|
||||
margin: 1rem;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.wrapper-card {
|
||||
transition: all .3s;
|
||||
height: auto;
|
||||
overflow: auto;
|
||||
@ -31,9 +49,25 @@
|
||||
justify-content: center;
|
||||
border-radius: 28px;
|
||||
padding: 56px;
|
||||
margin: 2rem;
|
||||
gap: 0 2rem;
|
||||
background-color: #ebf1fa;
|
||||
background-color: var(--md-sys-color-surface);
|
||||
color: var(--md-sys-color-on-surface)
|
||||
}
|
||||
|
||||
.alert {
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
padding: 16px;
|
||||
border-radius: 16px;
|
||||
background-color: var(--md-sys-color-secondary-container);
|
||||
color: var(--md-sys-color-on-secondary-container);
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.alert .content {
|
||||
flex-grow: 1;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.logo {
|
||||
|
@ -10,6 +10,11 @@
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"
|
||||
/>
|
||||
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
@ -28,10 +33,49 @@
|
||||
<title>Solarpass</title>
|
||||
|
||||
<style>
|
||||
:root, :host {
|
||||
--md-sys-color-background: #fbf8ff;
|
||||
--md-sys-color-on-background: #1b1b20;
|
||||
--md-sys-color-surface: #fbf8ff;
|
||||
--md-sys-color-surface-dim: #dcd9df;
|
||||
--md-sys-color-surface-bright: #fbf8ff;
|
||||
--md-sys-color-surface-container-lowest: #ffffff;
|
||||
--md-sys-color-surface-container-low: #f6f2f9;
|
||||
--md-sys-color-surface-container: #f0edf3;
|
||||
--md-sys-color-surface-container-high: #eae7ed;
|
||||
--md-sys-color-surface-container-highest: #e4e1e8;
|
||||
--md-sys-color-on-surface: #1b1b20;
|
||||
--md-sys-color-surface-variant: #e3e1ee;
|
||||
--md-sys-color-on-surface-variant: #464650;
|
||||
--md-sys-color-inverse-surface: #303035;
|
||||
--md-sys-color-inverse-on-surface: #f3eff6;
|
||||
--md-sys-color-outline: #777681;
|
||||
--md-sys-color-outline-variant: #c7c5d2;
|
||||
--md-sys-color-shadow: #000000;
|
||||
--md-sys-color-scrim: #000000;
|
||||
--md-sys-color-surface-tint: #53589d;
|
||||
--md-sys-color-primary: #373c7e;
|
||||
--md-sys-color-on-primary: #ffffff;
|
||||
--md-sys-color-primary-container: #5b60a5;
|
||||
--md-sys-color-on-primary-container: #ffffff;
|
||||
--md-sys-color-inverse-primary: #bec2ff;
|
||||
--md-sys-color-secondary: #5b5c79;
|
||||
--md-sys-color-on-secondary: #ffffff;
|
||||
--md-sys-color-secondary-container: #e2e1ff;
|
||||
--md-sys-color-on-secondary-container: #454662;
|
||||
--md-sys-color-tertiary: #662d5e;
|
||||
--md-sys-color-on-tertiary: #ffffff;
|
||||
--md-sys-color-tertiary-container: #8e5084;
|
||||
--md-sys-color-on-tertiary-container: #ffffff;
|
||||
--md-sys-color-error: #ba1a1a;
|
||||
--md-sys-color-on-error: #ffffff;
|
||||
--md-sys-color-error-container: #ffdad6;
|
||||
--md-sys-color-on-error-container: #410002;
|
||||
}
|
||||
|
||||
html, body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: Roboto Mono, monospace;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
@ -8,7 +8,7 @@
|
||||
<div class="right-part">
|
||||
<div class="responsive-title-gap"></div>
|
||||
|
||||
<form class="action-form">
|
||||
<form class="action-form" action="/sign-in" method="POST">
|
||||
<md-outlined-text-field
|
||||
class="block-field"
|
||||
name="username"
|
||||
@ -28,6 +28,7 @@
|
||||
</md-outlined-text-field>
|
||||
|
||||
<div class="action-form-buttons">
|
||||
<md-text-button type="button" href="/sign-up">{{.i18n.signup}}</md-text-button>
|
||||
<md-filled-button type="submit">{{.i18n.next}}</md-filled-button>
|
||||
</div>
|
||||
</form>
|
||||
|
75
pkg/views/signup.gohtml
Normal file
75
pkg/views/signup.gohtml
Normal file
@ -0,0 +1,75 @@
|
||||
<div class="left-part">
|
||||
<img class="logo" src="/favicon.png" width="64" height="64"/>
|
||||
|
||||
<h1 class="title">{{.i18n.title}}</h1>
|
||||
<p class="caption">{{.i18n.caption}}</p>
|
||||
</div>
|
||||
|
||||
<div class="right-part">
|
||||
<div class="responsive-title-gap"></div>
|
||||
|
||||
<form class="action-form" action="/sign-up" method="POST">
|
||||
<div class="columns-two">
|
||||
<md-outlined-text-field
|
||||
class="block-field"
|
||||
name="name"
|
||||
type="text"
|
||||
autocomplete="username"
|
||||
label={{.i18n.username}}
|
||||
>
|
||||
</md-outlined-text-field>
|
||||
|
||||
<md-outlined-text-field
|
||||
class="block-field"
|
||||
name="nick"
|
||||
type="text"
|
||||
autocomplete="nickname"
|
||||
label={{.i18n.nickname}}
|
||||
>
|
||||
</md-outlined-text-field>
|
||||
</div>
|
||||
|
||||
|
||||
<md-outlined-text-field
|
||||
class="block-field"
|
||||
name="email"
|
||||
type="email"
|
||||
autocomplete="email"
|
||||
label={{.i18n.email}}
|
||||
>
|
||||
</md-outlined-text-field>
|
||||
|
||||
<md-outlined-text-field
|
||||
class="block-field"
|
||||
name="password"
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
label={{.i18n.password}}
|
||||
>
|
||||
</md-outlined-text-field>
|
||||
|
||||
{{if eq .use_magic_token true}}
|
||||
<md-outlined-text-field
|
||||
class="block-field"
|
||||
name="magic_token"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
label={{.i18n.magic_token}}
|
||||
>
|
||||
</md-outlined-text-field>
|
||||
{{end}}
|
||||
|
||||
<div class="action-form-buttons">
|
||||
<md-text-button type="button" href="/sign-in">{{.i18n.signin}}</md-text-button>
|
||||
<md-filled-button type="submit">{{.i18n.next}}</md-filled-button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.columns-two {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user