chore: remove create-user tooling
This commit is contained in:
@@ -1,146 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"errors"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
sqlite3 "github.com/mattn/go-sqlite3"
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
|
||||||
)
|
|
||||||
|
|
||||||
const bcryptCost = 12
|
|
||||||
|
|
||||||
type config struct {
|
|
||||||
email string
|
|
||||||
password string
|
|
||||||
passwordStdin bool
|
|
||||||
dbFile string
|
|
||||||
}
|
|
||||||
|
|
||||||
func trimTrailingNewline(v string) string {
|
|
||||||
v = strings.TrimSuffix(v, "\n")
|
|
||||||
v = strings.TrimSuffix(v, "\r")
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseConfig() (config, error) {
|
|
||||||
var cfg config
|
|
||||||
|
|
||||||
flag.StringVar(&cfg.email, "email", "", "User email/username")
|
|
||||||
flag.StringVar(&cfg.password, "password", "", "User password")
|
|
||||||
flag.BoolVar(&cfg.passwordStdin, "password-stdin", false, "Read password from stdin")
|
|
||||||
flag.StringVar(&cfg.dbFile, "db", "", "SQLite database file path")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
args := flag.Args()
|
|
||||||
if len(args) > 2 {
|
|
||||||
return cfg, fmt.Errorf("too many arguments")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.email == "" && len(args) >= 1 {
|
|
||||||
cfg.email = args[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.password == "" && len(args) == 2 {
|
|
||||||
cfg.password = args[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.password != "" && cfg.passwordStdin {
|
|
||||||
return cfg, fmt.Errorf("use either --password or --password-stdin")
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.email = strings.TrimSpace(cfg.email)
|
|
||||||
if cfg.email == "" {
|
|
||||||
return cfg, fmt.Errorf("email is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.passwordStdin {
|
|
||||||
passwordBytes, err := io.ReadAll(os.Stdin)
|
|
||||||
if err != nil {
|
|
||||||
return cfg, fmt.Errorf("read password from stdin: %w", err)
|
|
||||||
}
|
|
||||||
cfg.password = trimTrailingNewline(string(passwordBytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.password == "" {
|
|
||||||
return cfg, fmt.Errorf("password is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.dbFile == "" {
|
|
||||||
cfg.dbFile = os.Getenv("DATABASE_FILE")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.dbFile == "" {
|
|
||||||
cfg.dbFile = "mal.db"
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := os.Stat(cfg.dbFile); err != nil {
|
|
||||||
if errors.Is(err, os.ErrNotExist) {
|
|
||||||
return cfg, fmt.Errorf("database file not found: %s", cfg.dbFile)
|
|
||||||
}
|
|
||||||
return cfg, fmt.Errorf("inspect database file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return cfg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func run() error {
|
|
||||||
cfg, err := parseConfig()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
hash, err := bcrypt.GenerateFromPassword([]byte(cfg.password), bcryptCost)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("hash password: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
db, err := sql.Open("sqlite3", fmt.Sprintf("file:%s?_foreign_keys=on", cfg.dbFile))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("open database: %w", err)
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
_, err = db.Exec(
|
|
||||||
"INSERT INTO user (id, username, password_hash) VALUES (?, ?, ?)",
|
|
||||||
uuid.NewString(),
|
|
||||||
cfg.email,
|
|
||||||
string(hash),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
var sqliteErr sqlite3.Error
|
|
||||||
if errors.As(err, &sqliteErr) && sqliteErr.ExtendedCode == sqlite3.ErrConstraintUnique {
|
|
||||||
return fmt.Errorf("user already exists: %s", cfg.email)
|
|
||||||
}
|
|
||||||
return fmt.Errorf("insert user: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("created user: %s\n", cfg.email)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Usage = func() {
|
|
||||||
fmt.Fprintf(os.Stderr, "Usage: %s [--db path] [--email email] [--password value | --password-stdin] [email] [password]\n", os.Args[0])
|
|
||||||
fmt.Fprintln(os.Stderr)
|
|
||||||
fmt.Fprintln(os.Stderr, "Examples:")
|
|
||||||
fmt.Fprintf(os.Stderr, " %s --db /app/data/mal.db --email admin@example.com --password 'strong-pass'\n", os.Args[0])
|
|
||||||
fmt.Fprintf(os.Stderr, " printf 'strong-pass' | %s --email admin@example.com --password-stdin\n", os.Args[0])
|
|
||||||
fmt.Fprintln(os.Stderr)
|
|
||||||
fmt.Fprintln(os.Stderr, "Defaults:")
|
|
||||||
fmt.Fprintln(os.Stderr, " --db uses DATABASE_FILE, then mal.db")
|
|
||||||
flag.PrintDefaults()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := run(); err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
|
||||||
flag.Usage()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
|
|
||||||
repo_root="$(cd -- "${script_dir}/.." >/dev/null 2>&1 && pwd)"
|
|
||||||
|
|
||||||
email="${1:-}"
|
|
||||||
password="${2:-}"
|
|
||||||
|
|
||||||
if [[ -z "${email}" ]]; then
|
|
||||||
read -r -p 'Email: ' email
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -z "${password}" ]]; then
|
|
||||||
read -r -s -p 'Password: ' password
|
|
||||||
printf '\n'
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -z "${email}" || -z "${password}" ]]; then
|
|
||||||
printf 'Email and password are required.\n' >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
(
|
|
||||||
cd "${repo_root}"
|
|
||||||
printf '%s' "${password}" | go run ./cmd/create-user --email "${email}" --password-stdin
|
|
||||||
)
|
|
||||||
Reference in New Issue
Block a user