Slight refactor
parent
a6c06dc831
commit
4b53126d6a
@ -0,0 +1,144 @@
|
|||||||
|
import { render } from 'preact'
|
||||||
|
import { useState, useEffect } from 'preact/hooks'
|
||||||
|
|
||||||
|
const InputNumero = ({ name, id, value, setValue }) => (
|
||||||
|
<div class="compound-input">
|
||||||
|
<input
|
||||||
|
class="grow"
|
||||||
|
type="number"
|
||||||
|
name={name}
|
||||||
|
id={id}
|
||||||
|
value={value}
|
||||||
|
onInput={e => setValue(e.target.value)}
|
||||||
|
/>
|
||||||
|
<button type="button" class="square" onClick={() => setValue(value => value + 1)}>
|
||||||
|
<span class="material-symbols-outlined">add</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="square" onClick={() => setValue(value => value - 1)}>
|
||||||
|
<span class="material-symbols-outlined">remove</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
const App = () => {
|
||||||
|
const [user, setUser] = useState(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch('/api/user')
|
||||||
|
.then(res => {
|
||||||
|
if (res.ok) {
|
||||||
|
return res.json()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(user => setUser(user))
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const [numeroGiocatori, setNumeroGiocatori] = useState(10)
|
||||||
|
|
||||||
|
const [numLupi, setNumLupi] = useState(2)
|
||||||
|
const [numFattucchiere, setNumFattucchiere] = useState(1)
|
||||||
|
const [numGuardie, setNumGuardie] = useState(1)
|
||||||
|
const [numCacciatori, setNumCacciatori] = useState(1)
|
||||||
|
const [numMedium, setNumMedium] = useState(1)
|
||||||
|
const [numVeggenti, setNumVeggenti] = useState(1)
|
||||||
|
|
||||||
|
const [tooManyRuoli, setTooManyRuoli] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const totalNumGiocatoriConRuolo =
|
||||||
|
numLupi + numFattucchiere + numGuardie + numCacciatori + numMedium + numVeggenti
|
||||||
|
|
||||||
|
console.log(totalNumGiocatoriConRuolo)
|
||||||
|
|
||||||
|
setTooManyRuoli(numeroGiocatori < totalNumGiocatoriConRuolo)
|
||||||
|
}, [numLupi, numFattucchiere, numGuardie, numCacciatori, numMedium, numVeggenti])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<header>
|
||||||
|
<h1>
|
||||||
|
<a href="/">Lupus Lite</a>
|
||||||
|
</h1>
|
||||||
|
<h2>Crea Partita</h2>
|
||||||
|
{user && (
|
||||||
|
<div>
|
||||||
|
(Accesso eseguito come <b>{user.username}</b>)
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</header>
|
||||||
|
<hr />
|
||||||
|
<form action="/api/crea-partita" method="post">
|
||||||
|
<h2 className="fill-row">Nuova Partita</h2>
|
||||||
|
<p class="fill-row">
|
||||||
|
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Esse ipsam quisquam
|
||||||
|
laborum nemo at dignissimos excepturi sapiente incidunt enim fuga?
|
||||||
|
</p>
|
||||||
|
<div class="compact">
|
||||||
|
<label for="numero-giocatori">Numero Giocatori</label>
|
||||||
|
<InputNumero
|
||||||
|
name="numero-giocatori"
|
||||||
|
id="numero-giocatori"
|
||||||
|
value={numeroGiocatori}
|
||||||
|
setValue={setNumeroGiocatori}
|
||||||
|
/>
|
||||||
|
<label for="numero-lupi">Numero Lupi</label>
|
||||||
|
<InputNumero
|
||||||
|
name="numero-lupi"
|
||||||
|
id="numero-lupi"
|
||||||
|
value={numLupi}
|
||||||
|
setValue={setNumLupi}
|
||||||
|
/>
|
||||||
|
<label for="numero-fattucchiere">Numero Fattucchiere</label>
|
||||||
|
<InputNumero
|
||||||
|
name="numero-fattucchiere"
|
||||||
|
id="numero-fattucchiere"
|
||||||
|
value={numFattucchiere}
|
||||||
|
setValue={setNumFattucchiere}
|
||||||
|
/>
|
||||||
|
<label for="numero-lupi">Numero Guardie</label>
|
||||||
|
<InputNumero
|
||||||
|
name="numero-guardie"
|
||||||
|
id="numero-guardie"
|
||||||
|
value={numGuardie}
|
||||||
|
setValue={setNumGuardie}
|
||||||
|
/>
|
||||||
|
<label for="numero-lupi">Numero Cacciatori</label>
|
||||||
|
<InputNumero
|
||||||
|
name="numero-cacciatori"
|
||||||
|
id="numero-cacciatori"
|
||||||
|
value={numCacciatori}
|
||||||
|
setValue={setNumCacciatori}
|
||||||
|
/>
|
||||||
|
<label for="numero-lupi">Numero Medium</label>
|
||||||
|
<InputNumero
|
||||||
|
name="numero-medium"
|
||||||
|
id="numero-medium"
|
||||||
|
value={numMedium}
|
||||||
|
setValue={setNumMedium}
|
||||||
|
/>
|
||||||
|
<label for="numero-lupi">Numero Veggenti</label>
|
||||||
|
<InputNumero
|
||||||
|
name="numero-veggenti"
|
||||||
|
id="numero-veggenti"
|
||||||
|
value={numVeggenti}
|
||||||
|
setValue={setNumVeggenti}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{tooManyRuoli && (
|
||||||
|
<div class="info">
|
||||||
|
<div class="icon">
|
||||||
|
<span class="material-symbols-outlined">error</span>
|
||||||
|
</div>
|
||||||
|
<div class="description">
|
||||||
|
Attenzione il numero di giocatori con ruolo è più alto del numero di
|
||||||
|
giocatori totali.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<button class="fill-row">Crea Partita</button>
|
||||||
|
</form>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
render(<App />, document.querySelector('main'))
|
@ -0,0 +1,17 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aziis98/lupus-lite/database"
|
||||||
|
"github.com/aziis98/lupus-lite/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthService interface {
|
||||||
|
Register(username, password string) error
|
||||||
|
Login(username, password string) (string, error)
|
||||||
|
UserForSession(token string) (string, error)
|
||||||
|
GetUser(username string) (model.User, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInMemoryAuthService(db database.Database) AuthService {
|
||||||
|
return &memAuth{db, map[string]string{}}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/aziis98/lupus-lite/database"
|
||||||
|
"github.com/aziis98/lupus-lite/model"
|
||||||
|
"github.com/aziis98/lupus-lite/util"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type memAuth struct {
|
||||||
|
db database.Database
|
||||||
|
|
||||||
|
sessions map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *memAuth) Register(username, password string) error {
|
||||||
|
passwordBCrypt, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return auth.db.CreateUser(model.User{
|
||||||
|
Username: username,
|
||||||
|
PasswordBCrypt: passwordBCrypt,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *memAuth) Login(username, password string) (string, error) {
|
||||||
|
user, err := auth.db.GetUser(username)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := bcrypt.CompareHashAndPassword(user.PasswordBCrypt, []byte(password)); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
token := util.GenerateRandomString(16)
|
||||||
|
auth.sessions[token] = username
|
||||||
|
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *memAuth) UserForSession(token string) (string, error) {
|
||||||
|
username, ok := auth.sessions[token]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf(`invalid session token`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return username, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *memAuth) GetUser(username string) (model.User, error) {
|
||||||
|
return auth.db.GetUser(username)
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/aziis98/lupus-lite/model"
|
||||||
|
"github.com/aziis98/lupus-lite/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Database interface {
|
||||||
|
CreateUser(user model.User) error
|
||||||
|
GetUsers() ([]model.User, error)
|
||||||
|
GetUser(username string) (model.User, error)
|
||||||
|
|
||||||
|
CreatePartita(partitaConfig model.PartitaConfig) (model.Partita, error)
|
||||||
|
GetPartita(uid string) (model.Partita, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type memDB struct {
|
||||||
|
Users map[string]model.User `json:"users"`
|
||||||
|
Partite map[string]model.Partita `json:"partite"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInMemoryDB() (Database, error) {
|
||||||
|
var db memDB
|
||||||
|
|
||||||
|
exampleFile, err := os.Open("example-db.local.json")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.NewDecoder(exampleFile).Decode(&db); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &db, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *memDB) GetUsers() ([]model.User, error) {
|
||||||
|
users := make([]model.User, 0, len(db.Users))
|
||||||
|
for _, u := range db.Users {
|
||||||
|
users = append(users, u)
|
||||||
|
}
|
||||||
|
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *memDB) GetUser(username string) (model.User, error) {
|
||||||
|
user, ok := db.Users[username]
|
||||||
|
if !ok {
|
||||||
|
return model.User{}, fmt.Errorf(`no user with username %q`, username)
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *memDB) CreateUser(user model.User) error {
|
||||||
|
if _, ok := db.Users[user.Username]; ok {
|
||||||
|
return fmt.Errorf(`user with username %q already exists`, user.Username)
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Users[user.Username] = user
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *memDB) CreatePartita(partitaConfig model.PartitaConfig) (model.Partita, error) {
|
||||||
|
uid := util.GenerateRandomString(12)
|
||||||
|
partita := model.Partita{
|
||||||
|
Uid: uid,
|
||||||
|
Config: partitaConfig,
|
||||||
|
Players: []string{},
|
||||||
|
Iniziata: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Partite[uid] = partita
|
||||||
|
|
||||||
|
return partita, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *memDB) GetPartita(uid string) (model.Partita, error) {
|
||||||
|
partita, ok := db.Partite[uid]
|
||||||
|
if !ok {
|
||||||
|
return model.Partita{}, fmt.Errorf(`nessuna partita con uid %q`, uid)
|
||||||
|
}
|
||||||
|
|
||||||
|
return partita, nil
|
||||||
|
}
|
@ -1,100 +0,0 @@
|
|||||||
import { render } from 'preact'
|
|
||||||
import { useState, useEffect } from 'preact/hooks'
|
|
||||||
|
|
||||||
const InputNumero = ({ name, id, value, setValue }) => (
|
|
||||||
<div class="compound-input">
|
|
||||||
<input
|
|
||||||
class="grow"
|
|
||||||
type="number"
|
|
||||||
name={name}
|
|
||||||
id={id}
|
|
||||||
value={value}
|
|
||||||
onInput={e => setValue(e.target.value)}
|
|
||||||
/>
|
|
||||||
<button type="button" class="square" onClick={() => setValue(value => value + 1)}>
|
|
||||||
<span class="material-symbols-outlined">add</span>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="square" onClick={() => setValue(value => value - 1)}>
|
|
||||||
<span class="material-symbols-outlined">remove</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
const App = () => {
|
|
||||||
const [user, setUser] = useState(null)
|
|
||||||
useEffect(() => {
|
|
||||||
fetch('/api/user')
|
|
||||||
.then(res => {
|
|
||||||
if (res.ok) {
|
|
||||||
return res.json()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(user => setUser(user))
|
|
||||||
})
|
|
||||||
|
|
||||||
const [numeroGiocatori, setNumeroGiocatori] = useState(10)
|
|
||||||
const [numLupi, setNumLupi] = useState(2)
|
|
||||||
const [numFattucchiere, setNumFattucchiere] = useState(1)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<header>
|
|
||||||
<h1>
|
|
||||||
<a href="/">Lupus Lite</a>
|
|
||||||
</h1>
|
|
||||||
<h2>Crea Partita</h2>
|
|
||||||
{user && (
|
|
||||||
<div>
|
|
||||||
(Accesso eseguito come <b>{user.username}</b>)
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</header>
|
|
||||||
<hr />
|
|
||||||
<form action="/api/crea-partita" method="post">
|
|
||||||
<h2 className="fill-row">Nuova Partita</h2>
|
|
||||||
<p class="fill-row">
|
|
||||||
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Esse ipsam quisquam
|
|
||||||
laborum nemo at dignissimos excepturi sapiente incidunt enim fuga?
|
|
||||||
</p>
|
|
||||||
<label for="numero-giocatori">Numero Giocatori</label>
|
|
||||||
<InputNumero
|
|
||||||
name="numero-giocatori"
|
|
||||||
id="numero-giocatori"
|
|
||||||
value={numeroGiocatori}
|
|
||||||
setValue={setNumeroGiocatori}
|
|
||||||
/>
|
|
||||||
<label for="numero-lupi">Numero Lupi</label>
|
|
||||||
<InputNumero
|
|
||||||
name="numero-lupi"
|
|
||||||
id="numero-lupi"
|
|
||||||
value={numLupi}
|
|
||||||
setValue={setNumLupi}
|
|
||||||
/>
|
|
||||||
<label for="numero-fattucchiere">Numero Fattucchiere</label>
|
|
||||||
<InputNumero
|
|
||||||
name="numero-fattucchiere"
|
|
||||||
id="numero-fattucchiere"
|
|
||||||
value={numFattucchiere}
|
|
||||||
setValue={setNumFattucchiere}
|
|
||||||
/>
|
|
||||||
<label for="numero-lupi">Numero Lupi</label>
|
|
||||||
<InputNumero
|
|
||||||
name="numero-lupi"
|
|
||||||
id="numero-lupi"
|
|
||||||
value={numLupi}
|
|
||||||
setValue={setNumLupi}
|
|
||||||
/>
|
|
||||||
<label for="numero-lupi">Numero Lupi</label>
|
|
||||||
<InputNumero
|
|
||||||
name="numero-lupi"
|
|
||||||
id="numero-lupi"
|
|
||||||
value={numLupi}
|
|
||||||
setValue={setNumLupi}
|
|
||||||
/>
|
|
||||||
<button class="fill-row">Crea Partita</button>
|
|
||||||
</form>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
render(<App />, document.querySelector('main'))
|
|
@ -0,0 +1,103 @@
|
|||||||
|
package lupus
|
||||||
|
|
||||||
|
// StatoPartita rappresenta lo stato della partita
|
||||||
|
type StatoPartita struct {
|
||||||
|
Uid string `json:"uid"` // Uid corrispondente come in "Partita"
|
||||||
|
|
||||||
|
Players []Player `json:"players"` // Players è una mappa da username a giocatore
|
||||||
|
Time uint `json:"time"` // Time indica la fase corrente del gioco (la parità indica notte/giorno e si inizia da "Notte 0")
|
||||||
|
Actions []Action `json:"actions"` // PhaseActions indica quali azioni sono state fatte in una certa fase
|
||||||
|
}
|
||||||
|
|
||||||
|
type Player struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
Ruolo Ruolo `json:"ruolo"`
|
||||||
|
Vivo bool `json:"vivo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Ruolo struct {
|
||||||
|
Uid string `json:"uid"`
|
||||||
|
Nome string `json:"nome"`
|
||||||
|
Fazione string `json:"fazione"`
|
||||||
|
Aura string `json:"aura"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Action struct {
|
||||||
|
Uid string `json:"uid"`
|
||||||
|
Time uint `json:"time"` // Time indica in quale fase è stata compiuta l'azione
|
||||||
|
Player string `json:"player"`
|
||||||
|
TargetPlayer string `json:"targetPlayer"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
AuraBianca = "bianca"
|
||||||
|
AuraNera = "nera"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
FazioneBuoni = "buoni"
|
||||||
|
FazioneCattivi = "cattivi"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Contadino = Ruolo{
|
||||||
|
Uid: "contadino",
|
||||||
|
Nome: "Contadino",
|
||||||
|
Fazione: FazioneBuoni,
|
||||||
|
Aura: AuraBianca,
|
||||||
|
}
|
||||||
|
Lupo = Ruolo{
|
||||||
|
Uid: "lupo",
|
||||||
|
Nome: "Lupo",
|
||||||
|
Fazione: FazioneCattivi,
|
||||||
|
Aura: AuraNera,
|
||||||
|
}
|
||||||
|
Fattucchiera = Ruolo{
|
||||||
|
Uid: "fattucchiera",
|
||||||
|
Nome: "Fattucchiera",
|
||||||
|
Fazione: FazioneCattivi,
|
||||||
|
Aura: AuraNera,
|
||||||
|
}
|
||||||
|
Indemoniato = Ruolo{
|
||||||
|
Uid: "indemoniato",
|
||||||
|
Nome: "Indemoniato",
|
||||||
|
Fazione: FazioneCattivi,
|
||||||
|
Aura: AuraBianca,
|
||||||
|
}
|
||||||
|
Guardia = Ruolo{
|
||||||
|
Uid: "guardia",
|
||||||
|
Nome: "Guardia",
|
||||||
|
Fazione: FazioneBuoni,
|
||||||
|
Aura: AuraBianca,
|
||||||
|
}
|
||||||
|
Cacciatore = Ruolo{
|
||||||
|
Uid: "cacciatore",
|
||||||
|
Nome: "Cacciatore",
|
||||||
|
Fazione: FazioneBuoni,
|
||||||
|
Aura: AuraBianca,
|
||||||
|
}
|
||||||
|
Medium = Ruolo{
|
||||||
|
Uid: "medium",
|
||||||
|
Nome: "Medium",
|
||||||
|
Fazione: FazioneBuoni,
|
||||||
|
Aura: AuraBianca,
|
||||||
|
}
|
||||||
|
Veggente = Ruolo{
|
||||||
|
Uid: "veggente",
|
||||||
|
Nome: "Veggente",
|
||||||
|
Fazione: FazioneBuoni,
|
||||||
|
Aura: AuraBianca,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ruoli è una lista di ruoli comuni, l'ordine è puramente casuale
|
||||||
|
var Ruoli = []Ruolo{
|
||||||
|
Contadino,
|
||||||
|
Lupo,
|
||||||
|
Fattucchiera,
|
||||||
|
Indemoniato,
|
||||||
|
Guardia,
|
||||||
|
Cacciatore,
|
||||||
|
Medium,
|
||||||
|
Veggente,
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
// PublicUser is the "public" version of the "User" struct, this excludes private information
|
||||||
|
type PublicUser struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// User represents a user in the database
|
||||||
|
type User struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
PasswordBCrypt []byte `json:"passwordBCrypt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u User) PublicUser() PublicUser {
|
||||||
|
return PublicUser{
|
||||||
|
Username: u.Username,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PartitaConfig struct {
|
||||||
|
// NumeroGiocatori indica il numero totale di giocatori (per capire quanto tutti sono entrati nella partita)
|
||||||
|
NumeroGiocatori int
|
||||||
|
|
||||||
|
// NumeroPerRuolo indica quanti giocatori di un certo ruolo ci dovrebbero essere
|
||||||
|
NumeroPerRuolo map[string]int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Partita struct {
|
||||||
|
Uid string // Uid è il codice identificativo della partita.
|
||||||
|
Config PartitaConfig // Config è la configurazione iniziale della partita.
|
||||||
|
Players []string // Players è la lista di giocatori che sono entrati in questa partita.
|
||||||
|
Iniziata bool // Iniziata indica se la partita è iniziata, quando lo è c'è una tabella corrispondente StatoPartita con le informazioni sui vari giocatori e sullo stato attuale del gioco.
|
||||||
|
}
|
@ -0,0 +1,100 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aziis98/lupus-lite/auth"
|
||||||
|
"github.com/aziis98/lupus-lite/database"
|
||||||
|
"github.com/aziis98/lupus-lite/util"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Api(api fiber.Router) {
|
||||||
|
db, err := database.NewInMemoryDB()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
auth := auth.NewInMemoryAuthService(db)
|
||||||
|
requireLogged := RequireLoggedMiddleware(auth)
|
||||||
|
|
||||||
|
api.Get("/status", func(c *fiber.Ctx) error {
|
||||||
|
s, err := json.MarshalIndent(db, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println(string(s))
|
||||||
|
|
||||||
|
return c.SendString("ok")
|
||||||
|
})
|
||||||
|
|
||||||
|
api.Post("/login", func(c *fiber.Ctx) error {
|
||||||
|
var loginForm struct {
|
||||||
|
Username string `form:"username"`
|
||||||
|
Password string `form:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.BodyParser(&loginForm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := auth.Login(loginForm.Username, loginForm.Password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Cookie(&fiber.Cookie{
|
||||||
|
Name: "sid",
|
||||||
|
Value: token,
|
||||||
|
Path: "/",
|
||||||
|
Expires: time.Now().Add(3 * 24 * time.Hour),
|
||||||
|
})
|
||||||
|
|
||||||
|
return c.Redirect("/")
|
||||||
|
})
|
||||||
|
|
||||||
|
api.Post("/logout", func(c *fiber.Ctx) error {
|
||||||
|
c.Cookie(&fiber.Cookie{
|
||||||
|
Name: "sid",
|
||||||
|
Value: "",
|
||||||
|
Path: "/",
|
||||||
|
Expires: time.Now(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return c.SendString("ok")
|
||||||
|
})
|
||||||
|
|
||||||
|
api.Post("/register", func(c *fiber.Ctx) error {
|
||||||
|
var loginForm struct {
|
||||||
|
Username string `form:"username"`
|
||||||
|
Password string `form:"password"`
|
||||||
|
Password2 string `form:"password2"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.BodyParser(&loginForm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := util.ValidateUsername(loginForm.Username); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := util.ValidatePasswords(loginForm.Password, loginForm.Password2); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := auth.Register(loginForm.Username, loginForm.Password); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Redirect("/login")
|
||||||
|
})
|
||||||
|
|
||||||
|
api.Get("/user", requireLogged, func(c *fiber.Ctx) error {
|
||||||
|
return c.JSON(requestUser(c).PublicUser())
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/aziis98/lupus-lite/auth"
|
||||||
|
"github.com/aziis98/lupus-lite/model"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const UserKey = "github.com/aziis98/lupus-lite/user"
|
||||||
|
|
||||||
|
func requestUser(c *fiber.Ctx) *model.User {
|
||||||
|
return c.Locals(UserKey).(*model.User)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RequireLoggedMiddleware(auth auth.AuthService) fiber.Handler {
|
||||||
|
return func(c *fiber.Ctx) error {
|
||||||
|
token := c.Cookies("sid")
|
||||||
|
if token == "" {
|
||||||
|
return fmt.Errorf(`request has no session token`)
|
||||||
|
}
|
||||||
|
|
||||||
|
username, err := auth.UserForSession(token)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := auth.GetUser(username)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Locals(UserKey, &user)
|
||||||
|
|
||||||
|
return c.Next()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
const alphabet = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateRandomString(n int) string {
|
||||||
|
b := make([]byte, n)
|
||||||
|
|
||||||
|
for i := range b {
|
||||||
|
b[i] = alphabet[rand.Intn(len(alphabet))]
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsWhitespaceFree(s string) bool {
|
||||||
|
for _, r := range s {
|
||||||
|
if unicode.IsSpace(r) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
Loading…
Reference in New Issue