Compare commits
5 Commits
4dfb28ad3d
...
4ec1d8c5a2
Author | SHA1 | Date | |
---|---|---|---|
4ec1d8c5a2 | |||
011b3a8c0b | |||
b37dcf89fd | |||
aab0ef380a | |||
846282db74 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,3 +5,4 @@ docker/docker-compose.yml
|
||||
cred.txt
|
||||
dist/
|
||||
.air.toml
|
||||
tmp/
|
||||
|
@ -230,3 +230,39 @@ func (app *application) SaveOrder(order models.Order) (int, error) {
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (app *application) CreateAuthToken(w http.ResponseWriter, r *http.Request) {
|
||||
var userInput struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
err := app.readJSON(w, r, &userInput)
|
||||
if err != nil {
|
||||
app.badRequest(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
// get the user from the db by email, send error if invalid email
|
||||
user, err := app.DB.GetUserByEmail(userInput.Email)
|
||||
if err != nil {
|
||||
app.invalidCredentials(w)
|
||||
return
|
||||
}
|
||||
|
||||
// validate the password, send error if invalid password
|
||||
|
||||
// generate the token
|
||||
|
||||
// send response
|
||||
|
||||
var payload struct {
|
||||
Error bool `json:"error"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
payload.Error = false
|
||||
payload.Message = "Success!"
|
||||
|
||||
_ = app.writeJSON(w, http.StatusOK, payload)
|
||||
}
|
||||
|
85
cmd/api/helpers.go
Normal file
85
cmd/api/helpers.go
Normal file
@ -0,0 +1,85 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (app *application) readJSON(w http.ResponseWriter, r *http.Request, data interface{}) error {
|
||||
maxBytes := 1048576
|
||||
|
||||
r.Body = http.MaxBytesReader(w, r.Body, int64(maxBytes))
|
||||
|
||||
dec := json.NewDecoder(r.Body)
|
||||
err := dec.Decode(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Make sure there is only one entry.
|
||||
err = dec.Decode(&struct{}{})
|
||||
if err != io.EOF {
|
||||
return errors.New("body must only have a single JSON value")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeJSON writes arbitrary data out as JSON
|
||||
func (app *application) writeJSON(
|
||||
w http.ResponseWriter,
|
||||
status int, data interface{},
|
||||
headers ...http.Header,
|
||||
) error {
|
||||
out, err := json.MarshalIndent(data, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(headers) > 0 {
|
||||
for k, v := range headers[0] {
|
||||
w.Header()[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(status)
|
||||
w.Write(out)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (app *application) badRequest(w http.ResponseWriter, r *http.Request, err error) error {
|
||||
var payload struct {
|
||||
Error bool `json:"error"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
payload.Error = true
|
||||
payload.Message = err.Error()
|
||||
|
||||
out, err := json.MarshalIndent(payload, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(out)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (app *application) invalidCredentials(w http.ResponseWriter) error {
|
||||
var payload struct {
|
||||
Error bool `json:"error"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
payload.Error = true
|
||||
payload.Message = "invalid authentication credentials"
|
||||
|
||||
err := app.writeJSON(w, http.StatusUnauthorized, payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@ -21,5 +21,8 @@ func (app *application) routes() http.Handler {
|
||||
mux.Post("/api/payment-intent", app.GetPaymentIntent)
|
||||
mux.Get("/api/widget/{id}", app.GetWidgetByID)
|
||||
mux.Post("/api/create-customer-and-subscribe-to-plan", app.CreateCustomerAndSubscribeToPlan)
|
||||
|
||||
mux.Post("/api/authenticate", app.CreateAuthToken)
|
||||
|
||||
return mux
|
||||
}
|
||||
|
@ -283,3 +283,9 @@ func (app *application) BronzePlanReceipt(w http.ResponseWriter, r *http.Request
|
||||
app.errorLog.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (app *application) LoginPage(w http.ResponseWriter, r *http.Request) {
|
||||
if err := app.renderTemplate(w, r, "login", &templateData{}); err != nil {
|
||||
app.errorLog.Println(err)
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,9 @@ func (app *application) routes() http.Handler {
|
||||
mux.Get("/plans/bronze", app.BronzePlan)
|
||||
mux.Get("/receipt/bronze", app.BronzePlanReceipt)
|
||||
|
||||
// auth routes
|
||||
mux.Get("/login", app.LoginPage)
|
||||
|
||||
fileServer := http.FileServer(http.Dir("./static"))
|
||||
mux.Handle("/static/*", http.StripPrefix("/static", fileServer))
|
||||
|
||||
|
@ -51,6 +51,11 @@
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item">
|
||||
<a href="/login" class="nav-link">Login</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
44
cmd/web/templates/login.page.gohtml
Normal file
44
cmd/web/templates/login.page.gohtml
Normal file
@ -0,0 +1,44 @@
|
||||
{{ template "base" . }}
|
||||
{{ define "title" }}
|
||||
Login
|
||||
{{ end }}
|
||||
{{ define "content" }}
|
||||
<form action=""
|
||||
method="post"
|
||||
name="login-form"
|
||||
id="login-form"
|
||||
class="d-blick needs-validation"
|
||||
autocomplete="off"
|
||||
novalidate="">
|
||||
<h2 class="mt-2 mb-3 text-center">Login</h2>
|
||||
<hr>
|
||||
<div class="mb-3">
|
||||
<label for="-email" class="form-label">Email</label>
|
||||
<input type="text"
|
||||
id="email"
|
||||
name="email"
|
||||
autocomplete="email-new"
|
||||
required=""
|
||||
class="form-control">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Password</label>
|
||||
<input type="password"
|
||||
id="password"
|
||||
name="password"
|
||||
autocomplete="password-new"
|
||||
required=""
|
||||
class="form-control">
|
||||
</div>
|
||||
<hr>
|
||||
<a href="javascript:void(0)" id="login-btn" class="btn btn-primary">Login</a>
|
||||
</form>
|
||||
{{ end }}
|
||||
{{define "js"}}
|
||||
<script type="module">
|
||||
import {val} from "/static/js/login.js"
|
||||
document.getElementById("login-btn").addEventListener("click", () => {
|
||||
val({{.API}});
|
||||
})
|
||||
</script>
|
||||
{{end}}
|
@ -3,6 +3,7 @@ package models
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -223,3 +224,32 @@ func (m *DBModel) InsertCustomer(customer Customer) (int, error) {
|
||||
}
|
||||
return int(id), nil
|
||||
}
|
||||
|
||||
// GetUserByEmail gets a user by email address
|
||||
func (m *DBModel) GetUserByEmail(email string) (User, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
email = strings.ToLower(email)
|
||||
var u User
|
||||
|
||||
query := `SELECT id, first_name, last_name, email, password, created_at, updated_at
|
||||
FROM users
|
||||
WHERE email = ?`
|
||||
|
||||
row := m.DB.QueryRowContext(ctx, query, email)
|
||||
|
||||
err := row.Scan(
|
||||
&u.ID,
|
||||
&u.FirstName,
|
||||
&u.LastName,
|
||||
&u.Email,
|
||||
&u.CreatedAt,
|
||||
&u.UpdatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
return User{}, err
|
||||
}
|
||||
|
||||
return u, nil
|
||||
}
|
||||
|
33
static/js/login.js
Normal file
33
static/js/login.js
Normal file
@ -0,0 +1,33 @@
|
||||
export function val(api) {
|
||||
let form = document.getElementById("login-form");
|
||||
|
||||
if (form.checkValidity() === false) {
|
||||
this.event.preventDefault();
|
||||
this.event.stopPropagation();
|
||||
form.classList.add("was-validated");
|
||||
return;
|
||||
}
|
||||
form.classList.add("was-validated");
|
||||
|
||||
let payload = {
|
||||
email: document.getElementById("email").value,
|
||||
password: document.getElementById("password").value,
|
||||
};
|
||||
|
||||
const requestOptions = {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
};
|
||||
|
||||
fetch(api + "/api/authenticate", requestOptions)
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
console.log(response)
|
||||
});
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user