- /pedir/: calendario de fechas pedidas (negro) y confirmadas (verde), findes tranqui y con ruido, solo findes por defecto con opción de ver la semana entera. Se pincha un día libre y, sin sesión, se pide entrar y se vuelve al formulario con la fecha puesta. - Gestor en Go (gestor/): cuentas por colectivo con argon2id, sesiones y CSRF, cupo y ventana de la asamblea, caducidades, registro append-only. - La cuenta de la asamblea confirma, rechaza, cambia turnos de finde y en «Todas las fechas» quita o modifica cualquier evento. - Orden «cuenta» y «clave» con -clave y -definitiva para fijar contraseñas desde el servidor; ninguna contraseña va en el repositorio. - Botón «Pedir evento» arriba de la barra lateral en todas las páginas. - Calendario semanal en móvil: lista de días con nombre y hora de cada cita. - Fuentes servidas desde fuentes/: fuera Google Fonts en todas las páginas.
94 lines
2.6 KiB
Go
94 lines
2.6 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
type Config struct {
|
|
// Red
|
|
Escucha string `json:"escucha"` // siempre local: nginx hace de puerta
|
|
BaseURL string `json:"base_url"` // https://www.laenre.net, para comprobar el Origen
|
|
|
|
// Rutas
|
|
DirDatos string `json:"dir_datos"` // registro.jsonl
|
|
DirPlantillas string `json:"dir_plantillas"` // plantillas de /pedir/
|
|
DirEstatico string `json:"dir_estatico"` // raiz de la web, solo en modo local
|
|
LogAuth string `json:"log_auth"` // el registro que lee fail2ban
|
|
|
|
// Comportamiento
|
|
CookieSegura bool `json:"cookie_segura"` // false solo para probar sin TLS
|
|
Local bool `json:"local"` // sirve tambien la web estatica
|
|
MesesVista int `json:"meses_vista"` // meses que pinta /pedir/
|
|
|
|
Reglas Reglas `json:"reglas"`
|
|
}
|
|
|
|
func ConfigPorDefecto() Config {
|
|
return Config{
|
|
Escucha: "127.0.0.1:8090",
|
|
BaseURL: "https://www.laenre.net",
|
|
DirDatos: "/var/lib/enrecal",
|
|
DirPlantillas: "/var/www/laenre/gestor/plantillas",
|
|
DirEstatico: "/var/www/laenre",
|
|
LogAuth: "/var/log/enrecal/auth.log",
|
|
CookieSegura: true,
|
|
MesesVista: 4,
|
|
Reglas: ReglasPorDefecto(),
|
|
}
|
|
}
|
|
|
|
func CargarConfig(ruta string) (Config, error) {
|
|
c := ConfigPorDefecto()
|
|
if ruta == "" {
|
|
return c, nil
|
|
}
|
|
b, err := os.ReadFile(ruta)
|
|
if err != nil {
|
|
return c, err
|
|
}
|
|
dec := json.NewDecoder(bytes.NewReader(b))
|
|
dec.DisallowUnknownFields()
|
|
if err := dec.Decode(&c); err != nil {
|
|
return c, fmt.Errorf("%s: %w", ruta, err)
|
|
}
|
|
return c, c.Validar()
|
|
}
|
|
|
|
func (c Config) Validar() error {
|
|
if c.Reglas.CupoDias < 1 {
|
|
return fmt.Errorf("cupo_dias tiene que ser 1 o mas")
|
|
}
|
|
if c.Reglas.MesesVentana < 1 {
|
|
return fmt.Errorf("meses_ventana tiene que ser 1 o mas")
|
|
}
|
|
if c.Reglas.DiasMinimoConfirmar < 0 {
|
|
return fmt.Errorf("dias_minimo_confirmar no puede ser negativo")
|
|
}
|
|
if c.Reglas.DiasMaximoPendiente < 1 {
|
|
return fmt.Errorf("dias_maximo_pendiente tiene que ser 1 o mas")
|
|
}
|
|
if len(c.Reglas.DiasReservables) == 0 {
|
|
return fmt.Errorf("dias_reservables no puede estar vacio")
|
|
}
|
|
if _, err := c.Reglas.AnclaFinde.Time(); err != nil {
|
|
return fmt.Errorf("ancla_finde: %w", err)
|
|
}
|
|
if c.Reglas.TurnoAncla != Tranqui && c.Reglas.TurnoAncla != Ruido {
|
|
return fmt.Errorf("turno_ancla solo puede ser %q o %q", Tranqui, Ruido)
|
|
}
|
|
if c.MesesVista < 1 {
|
|
return fmt.Errorf("meses_vista tiene que ser 1 o mas")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
const (
|
|
inactividadSesion = 8 * time.Hour
|
|
maxIntentos = 5
|
|
ventanaIntentos = 10 * time.Minute
|
|
esperaBloqueo = 15 * time.Minute
|
|
)
|