mirror of
https://github.com/openziti/zrok.git
synced 2025-06-20 09:48:07 +02:00
added maintenance cleanup for expired password reset requests
This commit is contained in:
parent
a07c4a519c
commit
2bbf404bae
@ -69,6 +69,7 @@ type InfluxConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MaintenanceConfig struct {
|
type MaintenanceConfig struct {
|
||||||
|
Account *AccountMaintenanceConfig
|
||||||
Registration *RegistrationMaintenanceConfig
|
Registration *RegistrationMaintenanceConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +79,12 @@ type RegistrationMaintenanceConfig struct {
|
|||||||
BatchLimit int
|
BatchLimit int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccountMaintenanceConfig struct {
|
||||||
|
ExpirationTimeout time.Duration
|
||||||
|
CheckFrequency time.Duration
|
||||||
|
BatchLimit int
|
||||||
|
}
|
||||||
|
|
||||||
const Unlimited = -1
|
const Unlimited = -1
|
||||||
|
|
||||||
type LimitsConfig struct {
|
type LimitsConfig struct {
|
||||||
@ -95,6 +102,11 @@ func DefaultConfig() *Config {
|
|||||||
ServiceName: "metrics",
|
ServiceName: "metrics",
|
||||||
},
|
},
|
||||||
Maintenance: &MaintenanceConfig{
|
Maintenance: &MaintenanceConfig{
|
||||||
|
Account: &AccountMaintenanceConfig{
|
||||||
|
ExpirationTimeout: time.Minute * 15,
|
||||||
|
CheckFrequency: time.Minute * 15,
|
||||||
|
BatchLimit: 500,
|
||||||
|
},
|
||||||
Registration: &RegistrationMaintenanceConfig{
|
Registration: &RegistrationMaintenanceConfig{
|
||||||
ExpirationTimeout: time.Hour * 24,
|
ExpirationTimeout: time.Hour * 24,
|
||||||
CheckFrequency: time.Hour,
|
CheckFrequency: time.Hour,
|
||||||
|
@ -80,8 +80,13 @@ func Run(inCfg *Config) error {
|
|||||||
cancel()
|
cancel()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if cfg.Maintenance != nil && cfg.Maintenance.Registration != nil {
|
if cfg.Maintenance != nil {
|
||||||
go newMaintenanceAgent(ctx, cfg.Maintenance).run()
|
if cfg.Maintenance.Registration != nil {
|
||||||
|
go newRegistrationMaintenanceAgent(ctx, cfg.Maintenance.Registration).run()
|
||||||
|
}
|
||||||
|
if cfg.Maintenance.Account != nil {
|
||||||
|
go newAccountMaintenanceAgent(ctx, cfg.Maintenance.Account).run()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
server := rest_server_zrok.NewServer(api)
|
server := rest_server_zrok.NewServer(api)
|
||||||
|
@ -1,11 +1,192 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="utf-8">
|
||||||
<title>zrok forgot password</title>
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
</head>
|
<title>Welcome to zrok!</title>
|
||||||
|
<meta name="description" content="Please click to create your zrok account.">
|
||||||
|
<meta name="viewport" content="width=device-width">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Russo+One&display=swap" rel="stylesheet">
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 25;
|
||||||
|
font-family: 'JetBrains Mono', Consolas, 'Courier New', monospace;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #3b2693;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
tt,
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: 'JetBrains Mono', Consolas, 'Courier New', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
font-family: 'Russo One', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:link {
|
||||||
|
color: #00d7e4;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:visited {
|
||||||
|
color: #00d7e4;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover,
|
||||||
|
a:active {
|
||||||
|
color: #ff0100;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
line-height: 1.3em;
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-family: 'JetBrains Mono', Consolas, 'Courier New', monospace;
|
||||||
|
font-size: 1.5em;
|
||||||
|
line-height: 1.3em;
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.17em;
|
||||||
|
line-height: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: 0;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
p code,
|
||||||
|
li code {
|
||||||
|
font-size: .875em;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-title {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 5em;
|
||||||
|
line-height: 48px;
|
||||||
|
color: #ffffff;
|
||||||
|
margin: 0 .25em 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.claim {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.5em 0 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 62em;
|
||||||
|
margin: 2em auto;
|
||||||
|
max-width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta {
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
display: inline-block;
|
||||||
|
margin: .25em;
|
||||||
|
padding: 10px 16px;
|
||||||
|
font-size: 1.15em;
|
||||||
|
line-height: 1.33;
|
||||||
|
border-radius: 6px;
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: middle;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
color: #FFF;
|
||||||
|
background-color: #ff0100;
|
||||||
|
border-color: #ff0100;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.btn-primary:link,
|
||||||
|
a.btn-primary:visited {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.btn-primary:hover,
|
||||||
|
a.btn-primary:active {
|
||||||
|
background-color: #cf0100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background-color: #ddd;
|
||||||
|
border-color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.btn-secondary:link,
|
||||||
|
a.btn-secondary:visited {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.btn-secondary:hover,
|
||||||
|
a.btn-secondary:hover {
|
||||||
|
background-color: #ccc;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about {
|
||||||
|
margin: 1em auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about td {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about td:first-child {
|
||||||
|
width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 600px) {
|
||||||
|
img {
|
||||||
|
height: auto !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 400px) {
|
||||||
|
body {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 320px) {
|
||||||
|
body {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>We see you requested a forgot password request, {{ .EmailAddress }}!</h1>
|
<h1>We see you requested a forgot password request, {{ .EmailAddress }}!</h1>
|
||||||
<p>Please click this <a href="{{ .ForgotPasswordUrl }}">link</a> to change your <code>zrok</code> account password.</p>
|
<p>Please click this to change your <code>zrok</code> account password.</p>
|
||||||
|
<div><a class="btn btn-primary" href="{{ .ForgotPasswordUrl }}">Reset Passwrod</a></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -10,23 +10,23 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type maintenanceAgent struct {
|
type maintenanceRegistrationAgent struct {
|
||||||
*MaintenanceConfig
|
*RegistrationMaintenanceConfig
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMaintenanceAgent(ctx context.Context, cfg *MaintenanceConfig) *maintenanceAgent {
|
func newRegistrationMaintenanceAgent(ctx context.Context, cfg *RegistrationMaintenanceConfig) *maintenanceRegistrationAgent {
|
||||||
return &maintenanceAgent{
|
return &maintenanceRegistrationAgent{
|
||||||
MaintenanceConfig: cfg,
|
RegistrationMaintenanceConfig: cfg,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ma *maintenanceAgent) run() {
|
func (ma *maintenanceRegistrationAgent) run() {
|
||||||
logrus.Info("starting")
|
logrus.Infof("starting maintenance registration agent")
|
||||||
defer logrus.Info("stopping")
|
defer logrus.Info("stopping maintenance registration agent")
|
||||||
|
|
||||||
ticker := time.NewTicker(ma.Registration.CheckFrequency)
|
ticker := time.NewTicker(ma.CheckFrequency)
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ma.ctx.Done():
|
case <-ma.ctx.Done():
|
||||||
@ -44,15 +44,15 @@ func (ma *maintenanceAgent) run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ma *maintenanceAgent) deleteExpiredAccountRequests() error {
|
func (ma *maintenanceRegistrationAgent) deleteExpiredAccountRequests() error {
|
||||||
tx, err := str.Begin()
|
tx, err := str.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer func() { _ = tx.Rollback() }()
|
defer func() { _ = tx.Rollback() }()
|
||||||
|
|
||||||
timeout := time.Now().UTC().Add(-ma.Registration.ExpirationTimeout)
|
timeout := time.Now().UTC().Add(-ma.ExpirationTimeout)
|
||||||
accountRequests, err := str.FindExpiredAccountRequests(timeout, ma.Registration.BatchLimit, tx)
|
accountRequests, err := str.FindExpiredAccountRequests(timeout, ma.BatchLimit, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error finding expire account requests before %v", timeout)
|
return errors.Wrapf(err, "error finding expire account requests before %v", timeout)
|
||||||
}
|
}
|
||||||
@ -76,3 +76,68 @@ func (ma *maintenanceAgent) deleteExpiredAccountRequests() error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type maintenanceAccountAgent struct {
|
||||||
|
*AccountMaintenanceConfig
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAccountMaintenanceAgent(ctx context.Context, cfg *AccountMaintenanceConfig) *maintenanceAccountAgent {
|
||||||
|
return &maintenanceAccountAgent{
|
||||||
|
AccountMaintenanceConfig: cfg,
|
||||||
|
ctx: ctx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ma *maintenanceAccountAgent) run() {
|
||||||
|
logrus.Infof("starting maintenance account agent")
|
||||||
|
defer logrus.Info("stopping maintenance account agent")
|
||||||
|
|
||||||
|
ticker := time.NewTicker(ma.CheckFrequency)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ma.ctx.Done():
|
||||||
|
{
|
||||||
|
ticker.Stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-ticker.C:
|
||||||
|
{
|
||||||
|
if err := ma.deleteExpiredForgetPasswordRequests(); err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (ma *maintenanceAccountAgent) deleteExpiredForgetPasswordRequests() error {
|
||||||
|
tx, err := str.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() { _ = tx.Rollback() }()
|
||||||
|
|
||||||
|
timeout := time.Now().UTC().Add(-ma.ExpirationTimeout)
|
||||||
|
passwordResetRequests, err := str.FindExpiredPasswordResetRequests(timeout, ma.BatchLimit, tx)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error finding expired password reset requests before %v", timeout)
|
||||||
|
}
|
||||||
|
if len(passwordResetRequests) > 0 {
|
||||||
|
logrus.Infof("found %d expired password reset requests to remove", len(passwordResetRequests))
|
||||||
|
acctStrings := make([]string, len(passwordResetRequests))
|
||||||
|
ids := make([]int, len(passwordResetRequests))
|
||||||
|
for i, acct := range passwordResetRequests {
|
||||||
|
ids[i] = acct.Id
|
||||||
|
acctStrings[i] = fmt.Sprintf("{id:%d}", acct.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Infof("deleting expired password reset requests: %v", strings.Join(acctStrings, ","))
|
||||||
|
if err := str.DeleteMultiplePasswordResetRequests(ids, tx); err != nil {
|
||||||
|
return errors.Wrapf(err, "error deleting expired password reset requests before %v", timeout)
|
||||||
|
}
|
||||||
|
if err := tx.Commit(); err != nil {
|
||||||
|
return errors.Wrapf(err, "error committing expired password reset requests deletion")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
package store
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
@ -31,6 +35,33 @@ func (self *Store) FindPasswordResetRequestWithToken(token string, tx *sqlx.Tx)
|
|||||||
return prr, nil
|
return prr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Store) FindExpiredPasswordResetRequests(before time.Time, limit int, tx *sqlx.Tx) ([]*PasswordResetRequest, error) {
|
||||||
|
var sql string
|
||||||
|
switch self.cfg.Type {
|
||||||
|
case "postgres":
|
||||||
|
sql = "select * from password_reset_requests where created_at < $1 limit %d for update"
|
||||||
|
|
||||||
|
case "sqlite3":
|
||||||
|
sql = "select * from password_reset_requests where created_at < $1 limit %d"
|
||||||
|
default:
|
||||||
|
return nil, errors.Errorf("unknown database type '%v'", self.cfg.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := tx.Queryx(fmt.Sprintf(sql, limit), before)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "error selecting expired password_reset_requests")
|
||||||
|
}
|
||||||
|
var prrs []*PasswordResetRequest
|
||||||
|
for rows.Next() {
|
||||||
|
prr := &PasswordResetRequest{}
|
||||||
|
if err := rows.StructScan(prr); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "error scanning password_reset_request")
|
||||||
|
}
|
||||||
|
prrs = append(prrs, prr)
|
||||||
|
}
|
||||||
|
return prrs, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (self *Store) DeletePasswordResetRequest(id int, tx *sqlx.Tx) error {
|
func (self *Store) DeletePasswordResetRequest(id int, tx *sqlx.Tx) error {
|
||||||
stmt, err := tx.Prepare("delete from password_reset_requests where id = $1")
|
stmt, err := tx.Prepare("delete from password_reset_requests where id = $1")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -42,3 +73,27 @@ func (self *Store) DeletePasswordResetRequest(id int, tx *sqlx.Tx) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Store) DeleteMultiplePasswordResetRequests(ids []int, tx *sqlx.Tx) error {
|
||||||
|
if len(ids) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
anyIds := make([]any, len(ids))
|
||||||
|
indexes := make([]string, len(ids))
|
||||||
|
|
||||||
|
for i, id := range ids {
|
||||||
|
anyIds[i] = id
|
||||||
|
indexes[i] = fmt.Sprintf("$%d", i+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := tx.Prepare(fmt.Sprintf("delete from password_reset_requests where id in (%s)", strings.Join(indexes, ",")))
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error preparing password_reset_requests delete multiple statement")
|
||||||
|
}
|
||||||
|
_, err = stmt.Exec(anyIds...)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error executing password_reset_requests delete multiple statement")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
-- +migrate up
|
-- +migrate Up
|
||||||
|
|
||||||
--
|
--
|
||||||
-- password_reset_requests
|
-- password_reset_requests
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {useState} from "react";
|
import {useState} from "react";
|
||||||
import * as account from '../../api/account';
|
import * as account from '../../api/account';
|
||||||
import {Button, Container, Form, Row} from "react-bootstrap";
|
import {Button, Container, Form, Row} from "react-bootstrap";
|
||||||
import { Navigate } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
const ResetPassword = (props) => {
|
const ResetPassword = (props) => {
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
@ -30,11 +30,10 @@ const ResetPassword = (props) => {
|
|||||||
setMessage(undefined);
|
setMessage(undefined);
|
||||||
setComplete(true);
|
setComplete(true);
|
||||||
} else {
|
} else {
|
||||||
setMessage(errorMessage)
|
setMessage(errorMessage);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(resp => {
|
.catch(resp => {
|
||||||
console.log("reset password failed", resp);
|
|
||||||
setMessage(errorMessage);
|
setMessage(errorMessage);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -83,7 +82,24 @@ const ResetPassword = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Navigate to="/" />
|
<Container fluid>
|
||||||
|
<Row>
|
||||||
|
<img alt="ziggy" src={"/ziggy.svg"} width={200}/>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<h1>Password Reset</h1>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
Password reset successful! You can now return to the login page and login.
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<div>
|
||||||
|
<Link to="/" className="">
|
||||||
|
Login
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</Row>
|
||||||
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import * as account from '../../api/account';
|
import * as account from '../../api/account';
|
||||||
import { Button, Container, Form, Row } from "react-bootstrap";
|
import { Button, Container, Form, Row } from "react-bootstrap";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
const SendRequest = (props) => {
|
const SendRequest = (props) => {
|
||||||
const [email, setEmail] = useState('');
|
const [email, setEmail] = useState('');
|
||||||
const [message, setMessage] = useState();
|
|
||||||
const [complete, setComplete] = useState(false);
|
const [complete, setComplete] = useState(false);
|
||||||
|
|
||||||
|
|
||||||
const errorMessage = <h2 className={"errorMessage"}>Forgot Password Failed!</h2>;
|
|
||||||
|
|
||||||
const handleSubmit = async e => {
|
const handleSubmit = async e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
console.log(email);
|
console.log(email);
|
||||||
@ -17,16 +14,13 @@ const SendRequest = (props) => {
|
|||||||
account.forgotPassword({ body: { "email": email } })
|
account.forgotPassword({ body: { "email": email } })
|
||||||
.then(resp => {
|
.then(resp => {
|
||||||
if (!resp.error) {
|
if (!resp.error) {
|
||||||
console.log("Make landing page to expect and email or something similar")
|
|
||||||
setComplete(true)
|
setComplete(true)
|
||||||
} else {
|
} else {
|
||||||
console.log('forgot password failed')
|
setComplete(true)
|
||||||
setMessage(errorMessage);
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((resp) => {
|
.catch((resp) => {
|
||||||
console.log('forgot password failed', resp)
|
setComplete(true)
|
||||||
setMessage(errorMessage)
|
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -50,7 +44,7 @@ const SendRequest = (props) => {
|
|||||||
<Form.Control
|
<Form.Control
|
||||||
type={"email"}
|
type={"email"}
|
||||||
placeholder={"Email Address"}
|
placeholder={"Email Address"}
|
||||||
onChange={t => { setMessage(null); setEmail(t.target.value); }}
|
onChange={t => { setEmail(t.target.value); }}
|
||||||
value={email}
|
value={email}
|
||||||
/>
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
@ -58,16 +52,30 @@ const SendRequest = (props) => {
|
|||||||
<Button variant={"light"} type={"submit"}>Forgot Password</Button>
|
<Button variant={"light"} type={"submit"}>Forgot Password</Button>
|
||||||
</Form>
|
</Form>
|
||||||
</Row>
|
</Row>
|
||||||
<Row>
|
|
||||||
{message}
|
|
||||||
</Row>
|
|
||||||
</Container>
|
</Container>
|
||||||
</Row>
|
</Row>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div>Make landing page to expect an email or something similar</div>
|
<Container fluid>
|
||||||
|
<Row>
|
||||||
|
<img alt="ziggy" src={"/ziggy.svg"} width={200}/>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<h1>Reset Password</h1>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
We will get back to you shortly with a link to reset your password!
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<div>
|
||||||
|
<Link to="/" className="">
|
||||||
|
Login
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</Row>
|
||||||
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,13 +66,13 @@ const Login = (props) => {
|
|||||||
/>
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
|
|
||||||
<div className="text-right">
|
<Button variant={"light"} type={"submit"}>Log In</Button>
|
||||||
|
|
||||||
|
<div>
|
||||||
<Link to="/forgotpassword" className="">
|
<Link to="/forgotpassword" className="">
|
||||||
Forgot Password?
|
Forgot Password?
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button variant={"light"} type={"submit"}>Log In</Button>
|
|
||||||
</Form>
|
</Form>
|
||||||
</Row>
|
</Row>
|
||||||
<Row>
|
<Row>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user