puerta/internal/user/ttl.go
Roberto Hidalgo 038e09b202 lol, 64bit integer overflows are fun to debug
specially when running a multi-arch cluster and HA with poor observability. a `time.Duration` was being casted to an `int` (because I clearly have no clue of what I'm doing). All is well on 64-bit processors, but 32-bit processors choke on the very long micro-second precision int, overflow, and turn `7d`, around `604800` seconds into `-114753536` seconds, which the browser happily (and correctly) turns into a cookie sent to /dev/null. lol.
2023-02-11 12:01:16 -06:00

109 lines
2.0 KiB
Go

// SPDX-License-Identifier: Apache-2.0
// Copyright © 2022 Roberto Hidalgo <nidito@un.rob.mx>
package user
import (
"database/sql"
"encoding/json"
"fmt"
"strconv"
"time"
"github.com/upper/db/v4"
)
var DefaultTTL = TTL{src: "30d", duration: time.Hour * 24 * 30}
type TTL struct {
src string
duration time.Duration
}
func (ttl *TTL) Parse() (err error) {
if ttl.src == "" {
return fmt.Errorf("could not parse empty ttl")
}
suffix := ttl.src[len(ttl.src)-1]
toParse := ttl.src
if suffix == 'd' || suffix == 'w' || suffix == 'M' {
multiplier := 1
switch suffix {
case 'd':
multiplier = 24
case 'w':
multiplier = 24 * 7
case 'M':
multiplier = 24 * 7 * 30
default:
err = fmt.Errorf("unknown suffix for time duration %s", string(suffix))
return
}
toParse = toParse[0 : len(toParse)-1]
var days int
days, err = strconv.Atoi(toParse)
if err != nil {
return
}
toParse = fmt.Sprintf("%dh", days*multiplier)
}
ttl.duration, err = time.ParseDuration(toParse)
return
}
func (ttl *TTL) Scan(value any) error {
if value == nil {
return nil
}
var src string
var ok bool
if src, ok = value.(string); !ok {
if err := json.Unmarshal(value.([]byte), &src); err != nil {
return fmt.Errorf("could not decode ttl as json %s: %s", value, err)
}
}
if src == "" {
return nil
}
ttl.src = src
if err := ttl.Parse(); err != nil {
return err
}
return nil
}
func (ttl *TTL) UnmarshalJSON(value []byte) error {
if err := json.Unmarshal(value, &ttl.src); err != nil {
return err
}
return ttl.Parse()
}
func (ttl *TTL) MarshalJSON() ([]byte, error) {
return json.Marshal(ttl.src)
}
func (ttl *TTL) MarshalDB() (any, error) {
return json.Marshal(ttl.src)
}
func (ttl *TTL) FromNow() time.Time {
return time.Now().Add(ttl.duration)
}
func (ttl *TTL) Seconds() int {
return int(ttl.duration.Seconds())
}
var _ sql.Scanner = &TTL{}
var _ db.Marshaler = &TTL{}
var _ json.Marshaler = &TTL{}
var _ json.Unmarshaler = &TTL{}