event_sourcing_user_management_poc

PoC for user management in Event Sourcing using SQLite3

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
  42. 42
  43. 43
  44. 44
  45. 45
  46. 46
  47. 47
  48. 48
  49. 49
  50. 50
  51. 51
  52. 52
  53. 53
  54. 54
  55. 55
  56. 56
  57. 57
  58. 58
  59. 59
  60. 60
  61. 61
  62. 62
  63. 63
  64. 64
  65. 65
  66. 66
  67. 67
  68. 68
  69. 69
  70. 70
  71. 71
  72. 72
  73. 73
  74. 74
  75. 75
  76. 76
  77. 77
  78. 78
  79. 79
  80. 80
  81. 81
  82. 82
  83. 83
  84. 84
  85. 85
  86. 86
  87. 87
  88. 88
  89. 89
  90. 90
  91. 91
  92. 92
  93. 93
  94. 94
  95. 95
  96. 96
  97. 97
  98. 98
  99. 99
  100. 100
  101. 101
  102. 102
  103. 103
  104. 104
  105. 105
  106. 106
  107. 107
  108. 108
  109. 109
  110. 110
  111. 111
  112. 112
  113. 113
  114. 114
  115. 115
  116. 116
  117. 117
// Copyright 2025 Shota FUJI
//
// This source code is licensed under Zero-Clause BSD License.
// You can find a copy of the Zero-Clause BSD License at LICENSES/0BSD.txt
// You may also obtain a copy of the Zero-Clause BSD License at
// <https://opensource.org/license/0bsd>
//
// SPDX-License-Identifier: 0BSD

package main

import (
	"database/sql"
	_ "embed"
	"flag"
	"os"
	"strings"

	"github.com/charmbracelet/lipgloss"
	"github.com/charmbracelet/log"
	_ "modernc.org/sqlite"

	"pocka.jp/x/event_sourcing_user_management_poc/setups"
)

//go:embed init.sql
var initSQL string

var noVerbose = flag.Bool("noverbose", false, "Suppress debug logs")

var shouldCreateInitAdminCreationPassword = flag.Bool(
	"init-admin-creation-password", false, "Whether generate a password for initial admin user creation",
)

var shouldCreateAlice = flag.Bool(
	"create-alice", false, "Create an admin user \"Alice/Alice's password\"?",
)

// charmbracelet/log uses 256-color for default styles.
// In other words, they ignore common terminal emulator's palette and uses
// semi-hard-coded color. Unsafe defaults.
func createLogger() *log.Logger {
	styles := log.DefaultStyles()
	styles.Levels = map[log.Level]lipgloss.Style{
		log.DebugLevel: lipgloss.NewStyle().
			SetString(strings.ToUpper(log.DebugLevel.String())).
			Bold(true).
			Width(5).
			Foreground(lipgloss.Color("7")),
		log.InfoLevel: lipgloss.NewStyle().
			SetString(strings.ToUpper(log.InfoLevel.String())).
			Bold(true).
			Width(5).
			Foreground(lipgloss.Color("4")),
		log.WarnLevel: lipgloss.NewStyle().
			SetString(strings.ToUpper(log.WarnLevel.String())).
			Bold(true).
			Width(5).
			Foreground(lipgloss.Color("3")),
		log.ErrorLevel: lipgloss.NewStyle().
			SetString(strings.ToUpper(log.ErrorLevel.String())).
			Bold(true).
			Width(5).
			Foreground(lipgloss.Color("1")),
		log.FatalLevel: lipgloss.NewStyle().
			SetString(strings.ToUpper(log.FatalLevel.String())).
			Bold(true).
			Width(5).
			Foreground(lipgloss.Color("1")),
	}

	log.SetStyles(styles)
	logger := log.New(os.Stderr)
	logger.SetStyles(styles)

	return logger
}

func main() {
	logger := createLogger()

	flag.Parse()
	if !*noVerbose {
		logger.SetLevel(log.DebugLevel)
	}

	db, err := sql.Open("sqlite", ":memory:")
	if err != nil {
		logger.Fatalf("Opening in-memory database failed: %s\n", err)
	}

	if _, err := db.Exec(initSQL); err != nil {
		logger.Fatalf("Initialization SQL failed: %s\n", err)
	}

	if *shouldCreateInitAdminCreationPassword {
		logger.Debug("Inserting InitialAdminCreationPasswordCreated event...")

		password, err := setups.InitAdminCreationPassword(db)
		if err != nil {
			logger.Fatal(err)
		}

		logger.Infof("Use this password to create initial user: %s", password)
	}

	if *shouldCreateAlice {
		logger.Debug("Creating admin user Alice...")

		id, err := setups.CreateAlice(db)
		if err != nil {
			logger.Fatal(err)
		}

		logger.Infof("Created admin user Alice. ID=%s", id)
	}
}