init - add project files
This commit is contained in:
167
cmd/metagen/css_preprocessor.go
Normal file
167
cmd/metagen/css_preprocessor.go
Normal file
@@ -0,0 +1,167 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"maxwarden/basic"
|
||||
"maxwarden/security"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var dedupMap = make(map[string]bool)
|
||||
|
||||
// Walk the FS tree and search for `.go` files containing calls to `InlineStyle()`
|
||||
// Collect the inputs to each call (they must be string literals), expand shorthand macros, and
|
||||
func generateInlineStyles() {
|
||||
fmt.Printf("Compiling Inline Styles")
|
||||
|
||||
inlineStyleRegex := regexp.MustCompile("InlineStyle\\((((?:'[^']*')|(?:\"[^\"]*\")|(`(?:[^`]|[\r\n])*?`)))\\)")
|
||||
|
||||
// these are the directories that get scanned
|
||||
var dirs = [2]string{"handlers", "ui"}
|
||||
|
||||
var matches []string
|
||||
|
||||
for _, dir := range dirs {
|
||||
err := filepath.Walk(dir, func(pathStr string, info os.FileInfo, err error) error {
|
||||
if strings.HasSuffix(info.Name(), ".go") {
|
||||
pathStr = filepath.ToSlash(pathStr)
|
||||
handleErr(err)
|
||||
|
||||
file, err := os.Open(pathStr)
|
||||
handleErr(err)
|
||||
|
||||
defer file.Close()
|
||||
|
||||
content, contentErr := os.ReadFile(pathStr)
|
||||
if contentErr != nil {
|
||||
return contentErr
|
||||
}
|
||||
|
||||
s := string(content)
|
||||
|
||||
// only pre-process page & component files
|
||||
inlineStyleRegex.ReplaceAllStringFunc(s, func(match string) string {
|
||||
submatch := inlineStyleRegex.FindStringSubmatch(match)
|
||||
|
||||
if len(submatch) > 1 {
|
||||
submatch[1] = strings.TrimPrefix(submatch[1], "\"")
|
||||
submatch[1] = strings.TrimPrefix(submatch[1], "'")
|
||||
submatch[1] = strings.TrimPrefix(submatch[1], "`")
|
||||
submatch[1] = strings.TrimSuffix(submatch[1], "\"")
|
||||
submatch[1] = strings.TrimSuffix(submatch[1], "'")
|
||||
submatch[1] = strings.TrimSuffix(submatch[1], "`")
|
||||
submatch[1] = strings.ReplaceAll(submatch[1], "\n", " ")
|
||||
submatch[1] = strings.ReplaceAll(submatch[1], "\t", "")
|
||||
|
||||
rawInput := submatch[1] // this is the input before we process it
|
||||
|
||||
// Skip duplicates
|
||||
_, found := dedupMap[rawInput]
|
||||
if !found {
|
||||
cssHash, _ := security.HighwayHash58(rawInput)
|
||||
cssHash = basic.GetFirstNChars(cssHash, 8)
|
||||
|
||||
// Expand custom css macros (see comments below for details)
|
||||
submatch[1] = expandMe(submatch[1], cssHash)
|
||||
submatch[1] = expandMedia(submatch[1])
|
||||
submatch[1] = expandColor(submatch[1])
|
||||
submatch[1] = expandSpacing(submatch[1])
|
||||
|
||||
matches = append(matches, submatch[1])
|
||||
dedupMap[rawInput] = true
|
||||
}
|
||||
}
|
||||
|
||||
// We aren't actually replacing anything in the src file, we just needed to iterate over the regex matches
|
||||
return ""
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
handleErr(err)
|
||||
}
|
||||
|
||||
outputCSS := "/* " + METAGEN_AUTO_COMMENT + " */\n"
|
||||
outputCSS += strings.Join(matches, "")
|
||||
os.WriteFile("./wwwroot/css/style.metagen.css", []byte(outputCSS), 0664)
|
||||
|
||||
printStatus(true)
|
||||
}
|
||||
|
||||
// Expand spacing macro
|
||||
// Ex:
|
||||
//
|
||||
// padding: $5;
|
||||
//
|
||||
// => padding: calc(var(--spacing) * 5);
|
||||
func expandSpacing(input string) string {
|
||||
re := regexp.MustCompile(`\$([0-9]+(?:\.[0-9]+)?)`)
|
||||
|
||||
transformed := re.ReplaceAllStringFunc(input, func(match string) string {
|
||||
if len(re.FindStringSubmatch(match)) >= 2 {
|
||||
number := re.FindStringSubmatch(match)[1]
|
||||
return fmt.Sprintf("calc(var(--spacing) * %s)", number)
|
||||
}
|
||||
|
||||
return ""
|
||||
})
|
||||
|
||||
return transformed
|
||||
}
|
||||
|
||||
// Expand color macros:
|
||||
func expandColor(input string) string {
|
||||
re := regexp.MustCompile(`\$color\((.*?)(?:\/(0*(?:[1-9][0-9]?|100)))?\)`)
|
||||
|
||||
transformed := re.ReplaceAllStringFunc(input, func(match string) string {
|
||||
if len(re.FindStringSubmatch(match)) == 3 {
|
||||
if re.FindStringSubmatch(match)[2] == "" {
|
||||
return fmt.Sprintf("var(--color-%s)", re.FindStringSubmatch(match)[1])
|
||||
} else {
|
||||
return fmt.Sprintf("oklch(from var(--color-%s) l c h / %s%%)", re.FindStringSubmatch(match)[1], re.FindStringSubmatch(match)[2])
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
})
|
||||
|
||||
return transformed
|
||||
}
|
||||
|
||||
// Expand shorthand media queries.
|
||||
// Ex:
|
||||
//
|
||||
// media md { ... }
|
||||
//
|
||||
// => media screen and (min-width: 768px) { ... }
|
||||
func expandMedia(input string) string {
|
||||
input = strings.ReplaceAll(input, "$dark", "(prefers-color-scheme: dark)")
|
||||
input = strings.ReplaceAll(input, "$light", "(prefers-color-scheme: light)")
|
||||
input = strings.ReplaceAll(input, "$xs-", "screen and (max-width: 639px)")
|
||||
input = strings.ReplaceAll(input, "$sm-", "screen and (max-width: 767px)")
|
||||
input = strings.ReplaceAll(input, "$md-", "screen and (max-width: 1023px)")
|
||||
input = strings.ReplaceAll(input, "$lg-", "screen and (max-width: 1279px)")
|
||||
input = strings.ReplaceAll(input, "$xl-", "screen and (max-width: 1535px)")
|
||||
input = strings.ReplaceAll(input, "$sm", "screen and (min-width: 640px)")
|
||||
input = strings.ReplaceAll(input, "$md", "screen and (min-width: 768px)")
|
||||
input = strings.ReplaceAll(input, "$lg", "screen and (min-width: 1024px)")
|
||||
input = strings.ReplaceAll(input, "$xl", "screen and (min-width: 1280px)")
|
||||
input = strings.ReplaceAll(input, "$xx", "screen and (min-width: 1536px)")
|
||||
|
||||
return input
|
||||
}
|
||||
|
||||
// Expand "$me" macro with inline style attribute
|
||||
// Ex:
|
||||
//
|
||||
// $me { ... }
|
||||
//
|
||||
// => [__inlinecss_{REPLACEMENT_ID}] { ... }
|
||||
func expandMe(input string, replacementId string) string {
|
||||
return strings.ReplaceAll(input, "$me", fmt.Sprintf("[__inlinecss_%s]", replacementId))
|
||||
}
|
||||
52
cmd/metagen/generate_db_models.go
Normal file
52
cmd/metagen/generate_db_models.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func compileJet() {
|
||||
fmt.Printf("Compiling Jet generator")
|
||||
|
||||
os.Setenv("CGO_ENABLED", "1")
|
||||
|
||||
cmd := exec.Command("go", "build", "./cmd/jet")
|
||||
cmd.Dir = "./tools/jet-2.12.0"
|
||||
handleCmdOutput(cmd.CombinedOutput())
|
||||
|
||||
}
|
||||
|
||||
func generateJetModels() {
|
||||
bin := ""
|
||||
jetdir := ".jet"
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
bin = "./tools/jet-2.12.0/jet.exe"
|
||||
} else {
|
||||
bin = "./tools/jet-2.12.0/jet"
|
||||
}
|
||||
|
||||
os.RemoveAll(jetdir)
|
||||
|
||||
// compile bin if not exists
|
||||
if _, err := os.Stat(bin); err != nil {
|
||||
compileJet()
|
||||
}
|
||||
|
||||
fmt.Printf("Generating SQL models (jet)")
|
||||
|
||||
if _, err := os.Stat("passwords.db"); err != nil {
|
||||
printStatus(false)
|
||||
fmt.Println("\n" + err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
databaseType := "sqlite"
|
||||
|
||||
cmd := exec.Command(bin, "-source="+databaseType, "-dsn=file:passwords.db", "-schema=maxwarden", "-path="+jetdir)
|
||||
|
||||
handleCmdOutput(cmd.CombinedOutput())
|
||||
printStatus(true)
|
||||
}
|
||||
29
cmd/metagen/generate_debug_info.go
Normal file
29
cmd/metagen/generate_debug_info.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// set debug constant inside the "config" package
|
||||
func generateDebugConfig() {
|
||||
fmt.Printf("Generating DEBUG/RELEASE config")
|
||||
|
||||
code := METAGEN_AUTO_COMMENT + "\npackage config\n\nconst (\n"
|
||||
|
||||
if envtype == ENVIRONMENT_DEV {
|
||||
code += " DEBUG = true"
|
||||
} else {
|
||||
code += " DEBUG = false"
|
||||
}
|
||||
|
||||
code += "\n)\n"
|
||||
|
||||
// open file and write code to it
|
||||
in := []byte(code)
|
||||
|
||||
err := os.WriteFile("./config/debug.metagen.go", in, 0644)
|
||||
handleErr(err)
|
||||
|
||||
printStatus(true)
|
||||
}
|
||||
111
cmd/metagen/main.go
Normal file
111
cmd/metagen/main.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
var envtype int // set by user with cli flag
|
||||
|
||||
const (
|
||||
ENVIRONMENT_DEV = iota
|
||||
ENVIRONMENT_STAGING = iota
|
||||
ENVIRONMENT_PRODUCTION = iota
|
||||
)
|
||||
|
||||
const METAGEN_AUTO_COMMENT = "// @Metagen -- THIS FILE WAS AUTOGENERATED OR PREPROCESSED BY METAGEN - DO NOT EDIT BY HAND"
|
||||
|
||||
// metagen - code generator application
|
||||
//
|
||||
// Generates code for other applications, such as the server application
|
||||
func main() {
|
||||
env := flag.String("env", "dev", "The environment to run in: dev, staging, or production")
|
||||
|
||||
// Parse command-line flags
|
||||
flag.Parse()
|
||||
|
||||
switch *env {
|
||||
case "dev":
|
||||
envtype = ENVIRONMENT_DEV
|
||||
case "staging":
|
||||
envtype = ENVIRONMENT_STAGING
|
||||
case "production":
|
||||
envtype = ENVIRONMENT_PRODUCTION
|
||||
default:
|
||||
fmt.Printf("Invalid environment specified: %s\n", *env)
|
||||
fmt.Println("Allowed values are: dev, staging, or production")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
args := flag.Args()
|
||||
|
||||
for _, arg := range args {
|
||||
switch arg {
|
||||
case "build-all":
|
||||
preBuild()
|
||||
build()
|
||||
goto End
|
||||
case "build":
|
||||
preBuild()
|
||||
goto End
|
||||
case "migrate":
|
||||
migrations(args)
|
||||
goto End
|
||||
default:
|
||||
helpmsg()
|
||||
goto End
|
||||
}
|
||||
}
|
||||
|
||||
End:
|
||||
|
||||
if len(args) == 0 {
|
||||
helpmsg()
|
||||
}
|
||||
}
|
||||
|
||||
func helpmsg() {
|
||||
fmt.Println("Usage: metagen [options...]")
|
||||
fmt.Println("build :: Build dependencies, generate code, then build final executables.")
|
||||
fmt.Println("migrate [up, down, goto {V}, create {migration name}] :: Deploy and create SQL migrations.")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func preBuild() {
|
||||
if envtype == ENVIRONMENT_DEV {
|
||||
fmt.Println("[DEBUG ENVIRONMENT]")
|
||||
} else if envtype == ENVIRONMENT_STAGING || envtype == ENVIRONMENT_PRODUCTION {
|
||||
fmt.Println("[RELEASE ENVIRONMENT]")
|
||||
}
|
||||
|
||||
// db creation
|
||||
maybeCreateSqliteDb()
|
||||
|
||||
// code generation
|
||||
generateInlineStyles()
|
||||
generateDebugConfig()
|
||||
generateJetModels()
|
||||
}
|
||||
|
||||
func build() {
|
||||
compileServer()
|
||||
}
|
||||
|
||||
func compileServer() {
|
||||
var out []byte
|
||||
var err error
|
||||
|
||||
fmt.Printf("Compiling Server Binary")
|
||||
|
||||
if envtype == ENVIRONMENT_DEV {
|
||||
// include extra flags for the GC
|
||||
out, err = exec.Command("go", "build", "-gcflags=all=-N -l", "./cmd/server").CombinedOutput()
|
||||
} else {
|
||||
out, err = exec.Command("go", "build", "./cmd/server").CombinedOutput()
|
||||
}
|
||||
|
||||
handleCmdOutput(out, err)
|
||||
printStatus(true)
|
||||
}
|
||||
242
cmd/metagen/migrations.go
Normal file
242
cmd/metagen/migrations.go
Normal file
@@ -0,0 +1,242 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang-migrate/migrate/v4"
|
||||
_ "github.com/golang-migrate/migrate/v4/database/sqlite3"
|
||||
_ "github.com/golang-migrate/migrate/v4/source/file"
|
||||
)
|
||||
|
||||
// create
|
||||
func maybeCreateSqliteDb() {
|
||||
if _, err := os.Stat("./passwords.db"); err != nil {
|
||||
fmt.Printf("Creating new sqlite database")
|
||||
err := os.WriteFile("./passwords.db", nil, 0755)
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating Sqlite database.")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
m, err := migrate.New(
|
||||
"file://./migrations",
|
||||
"sqlite3://passwords.db",
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
printStatus(false)
|
||||
fmt.Println(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
mErr := m.Up()
|
||||
if mErr != nil {
|
||||
printStatus(false)
|
||||
fmt.Println(mErr.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
printStatus(true)
|
||||
}
|
||||
}
|
||||
|
||||
// handle the running and creation of migrations
|
||||
func migrations(args []string) {
|
||||
if len(args) < 2 {
|
||||
fmt.Println("Usage: metagen migrate [up, down, goto {V}, create {migration name}]")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
maybeCreateSqliteDb()
|
||||
|
||||
m, err := migrate.New(
|
||||
"file://./migrations",
|
||||
"sqlite3://passwords.db",
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
migrateNum := 0
|
||||
|
||||
if len(args) >= 3 && args[1] != "create" {
|
||||
var parseErr error
|
||||
migrateNum, parseErr = strconv.Atoi(args[2])
|
||||
if parseErr != nil {
|
||||
fmt.Println("Please provide a valid migration number.")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
m.PrefetchMigrations = migrate.DefaultPrefetchMigrations
|
||||
|
||||
switch args[1] {
|
||||
case "up":
|
||||
err := m.Up()
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
case "down":
|
||||
err := m.Down()
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
case "goto":
|
||||
err := m.Migrate(uint(migrateNum))
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
case "create":
|
||||
if len(args) < 3 {
|
||||
fmt.Println("Please provide a name for the new migration.")
|
||||
os.Exit(1)
|
||||
}
|
||||
createCmd("./migrations", time.Now(), defaultTimeFormat, args[2], "sql", true, 7, true)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
defaultTimeFormat = "20060102150405"
|
||||
defaultTimezone = "UTC"
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidSequenceWidth = errors.New("Digits must be positive")
|
||||
errIncompatibleSeqAndFormat = errors.New("The seq and format options are mutually exclusive")
|
||||
errInvalidTimeFormat = errors.New("Time format may not be empty")
|
||||
)
|
||||
|
||||
func createFile(filename string) error {
|
||||
// create exclusive (fails if file already exists)
|
||||
// os.Create() specifies 0666 as the FileMode, so we're doing the same
|
||||
f, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return f.Close()
|
||||
}
|
||||
|
||||
func nextSeqVersion(matches []string, seqDigits int) (string, error) {
|
||||
if seqDigits <= 0 {
|
||||
return "", errInvalidSequenceWidth
|
||||
}
|
||||
|
||||
nextSeq := uint64(1)
|
||||
|
||||
if len(matches) > 0 {
|
||||
filename := matches[len(matches)-1]
|
||||
matchSeqStr := filepath.Base(filename)
|
||||
idx := strings.Index(matchSeqStr, "_")
|
||||
|
||||
if idx < 1 { // Using 1 instead of 0 since there should be at least 1 digit
|
||||
return "", fmt.Errorf("Malformed migration filename: %s", filename)
|
||||
}
|
||||
|
||||
var err error
|
||||
matchSeqStr = matchSeqStr[0:idx]
|
||||
nextSeq, err = strconv.ParseUint(matchSeqStr, 10, 64)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
nextSeq++
|
||||
}
|
||||
|
||||
version := fmt.Sprintf("%0[2]*[1]d", nextSeq, seqDigits)
|
||||
|
||||
if len(version) > seqDigits {
|
||||
return "", fmt.Errorf("Next sequence number %s too large. At most %d digits are allowed", version, seqDigits)
|
||||
}
|
||||
|
||||
return version, nil
|
||||
}
|
||||
|
||||
func timeVersion(startTime time.Time, format string) (version string, err error) {
|
||||
switch format {
|
||||
case "":
|
||||
err = errInvalidTimeFormat
|
||||
case "unix":
|
||||
version = strconv.FormatInt(startTime.Unix(), 10)
|
||||
case "unixNano":
|
||||
version = strconv.FormatInt(startTime.UnixNano(), 10)
|
||||
default:
|
||||
version = startTime.Format(format)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func createCmd(dir string, startTime time.Time, format string, name string, ext string, seq bool, seqDigits int, print bool) error {
|
||||
if seq && format != defaultTimeFormat {
|
||||
return errIncompatibleSeqAndFormat
|
||||
}
|
||||
|
||||
var version string
|
||||
var err error
|
||||
|
||||
dir = filepath.Clean(dir)
|
||||
ext = "." + strings.TrimPrefix(ext, ".")
|
||||
|
||||
if seq {
|
||||
matches, err := filepath.Glob(filepath.Join(dir, "*"+ext))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
version, err = nextSeqVersion(matches, seqDigits)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
version, err = timeVersion(startTime, format)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
versionGlob := filepath.Join(dir, version+"_*"+ext)
|
||||
matches, err := filepath.Glob(versionGlob)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(matches) > 0 {
|
||||
return fmt.Errorf("duplicate migration version: %s", version)
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, direction := range []string{"up", "down"} {
|
||||
basename := fmt.Sprintf("%s_%s.%s%s", version, name, direction, ext)
|
||||
filename := filepath.Join(dir, basename)
|
||||
|
||||
if err = createFile(filename); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if print {
|
||||
absPath, _ := filepath.Abs(filename)
|
||||
log.Println(absPath)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
114
cmd/metagen/util.go
Normal file
114
cmd/metagen/util.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
. "maxwarden/basic"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
func printStatus(b bool) {
|
||||
var status string
|
||||
|
||||
if b {
|
||||
status = "SUCCESS"
|
||||
} else {
|
||||
status = "FAILED"
|
||||
}
|
||||
|
||||
fmt.Printf("... %s\n", status)
|
||||
}
|
||||
|
||||
func handleCmdOutput(out []byte, err error) {
|
||||
if err != nil {
|
||||
fmt.Printf("\n%s\n", out)
|
||||
fmt.Printf("%s\n", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func handleErr(err error) {
|
||||
if err != nil {
|
||||
printStatus(false)
|
||||
fmt.Println(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// convert an sqlite DSN to a file name
|
||||
func parseSQLiteFilename(dsn string) (string, error) {
|
||||
u, err := url.Parse(dsn)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// For simple file paths
|
||||
if u.Scheme == "" {
|
||||
return u.Path, nil
|
||||
}
|
||||
|
||||
// For more complex DSNs
|
||||
if u.Scheme == "file" {
|
||||
return u.Opaque, nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("invalid DSN format: %s", dsn)
|
||||
}
|
||||
|
||||
// given an ast.Decl, and destination struct, look at the struct for any
|
||||
// boolean fields with the struct tag `Note`. If valid notes are found in the given Decl doc string
|
||||
// set the tagged booleans to true on the input struct.
|
||||
// file is the file that the decl originated from.
|
||||
func parseNotesFromDocComment(decl ast.Decl, file *os.File, dest any) error {
|
||||
re := regexp.MustCompile(`@(\w+)`)
|
||||
|
||||
var identifier string
|
||||
var docstring string
|
||||
|
||||
var docNotes []string
|
||||
var validNotes []string
|
||||
|
||||
// check if decl is a function
|
||||
if funcDecl, ok := decl.(*ast.FuncDecl); ok {
|
||||
identifier = funcDecl.Name.Name
|
||||
docstring = funcDecl.Doc.Text()
|
||||
}
|
||||
|
||||
matches := re.FindAllStringSubmatch(docstring, -1)
|
||||
|
||||
for _, match := range matches {
|
||||
// match[1] contains the first capture group (the word after '@')
|
||||
docNotes = append(docNotes, match[1])
|
||||
}
|
||||
|
||||
v := reflect.ValueOf(dest).Elem()
|
||||
t := v.Type()
|
||||
|
||||
if v.Kind() != reflect.Struct {
|
||||
return fmt.Errorf("\ndestination struct must be a pointer to a struct")
|
||||
}
|
||||
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
|
||||
// Look for the `Note` tag in the struct field
|
||||
if field.Tag == "note:\"true\"" {
|
||||
if Contains(docNotes, field.Name) {
|
||||
v.Field(i).SetBool(true)
|
||||
}
|
||||
|
||||
validNotes = append(validNotes, field.Name)
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range docNotes {
|
||||
if !Contains(validNotes, v) {
|
||||
return fmt.Errorf("\n`%s`: Unknown note `@%s`, Identifier: `%s`\n\tValid values are: %v", file.Name(), v, identifier, validNotes)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
28
cmd/passgen/main.go
Normal file
28
cmd/passgen/main.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"maxwarden/entries"
|
||||
"maxwarden/security"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) == 2 {
|
||||
passHash, _ := security.HashPassword(os.Args[1])
|
||||
|
||||
testData := []entries.Secret{}
|
||||
|
||||
for range 10 {
|
||||
dummyData := entries.Secret{ID: security.RandBase58String(32), Description: "Twitter / X.com", URL: "https://x.com", Notes: "2fa is enabled for this account.", Username: "@johntwitter", Password: "##CORRECT_HORSE_BATTERY_STAPLE_51"}
|
||||
testData = append(testData, dummyData)
|
||||
}
|
||||
|
||||
masterKey := security.SHA512_58(os.Args[1])
|
||||
cryptData, _ := security.EncryptDataWithKey(&testData, masterKey)
|
||||
|
||||
println(passHash)
|
||||
println(cryptData)
|
||||
} else {
|
||||
println("Please input a password as first program argument")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user