add password generator
This commit is contained in:
@@ -2,9 +2,7 @@ package app
|
||||
|
||||
import (
|
||||
"maxwarden/entries"
|
||||
"maxwarden/security"
|
||||
. "maxwarden/ui"
|
||||
"maxwarden/users"
|
||||
|
||||
. "maragu.dev/gomponents"
|
||||
. "maragu.dev/gomponents/html"
|
||||
@@ -56,6 +54,7 @@ func EditorHandler(w http.ResponseWriter, r *http.Request) {
|
||||
url := r.FormValue("url")
|
||||
|
||||
secret = entries.Secret{
|
||||
ID: r.PathValue("id"),
|
||||
Description: desc,
|
||||
URL: url,
|
||||
Notes: notes,
|
||||
@@ -63,42 +62,26 @@ func EditorHandler(w http.ResponseWriter, r *http.Request) {
|
||||
Username: username,
|
||||
}
|
||||
|
||||
user, _ := users.FetchById(identity.UserID)
|
||||
|
||||
// Get current secret store
|
||||
secrets, _ := security.DecryptDataWithKey[[]entries.Secret](user.Data, identity.MasterKey)
|
||||
|
||||
if secrets == nil {
|
||||
http.Redirect(w, r, "/app", http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
if editorType == EDITOR_TYPE_ADD {
|
||||
secret.ID = security.RandBase58String(32)
|
||||
*secrets = append(*secrets, secret)
|
||||
entries.Add(identity.UserID, identity.MasterKey, secret)
|
||||
} else {
|
||||
secret.ID = r.PathValue("id")
|
||||
|
||||
// linear search and replace
|
||||
for i, v := range *secrets {
|
||||
if v.ID == secret.ID {
|
||||
(*secrets)[i] = secret
|
||||
}
|
||||
}
|
||||
entries.Update(identity.UserID, identity.MasterKey, secret)
|
||||
}
|
||||
|
||||
// Serialize and encrypt modified store using master key
|
||||
enc, _ := security.EncryptDataWithKey(secrets, identity.MasterKey)
|
||||
|
||||
user.Data = enc
|
||||
|
||||
users.Update(user)
|
||||
|
||||
http.Redirect(w, r, "/app", http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
AppLayout(title, *identity, session,
|
||||
Modal(
|
||||
"password_generator",
|
||||
Text("Passkey Generator"),
|
||||
HxLoad("/app/generator-hx"),
|
||||
[]Node {
|
||||
ButtonUIOutline(ModalCloser(), Text("Close")),
|
||||
},
|
||||
),
|
||||
|
||||
If(editorType == EDITOR_TYPE_EDIT,
|
||||
Group{
|
||||
Modal(
|
||||
@@ -106,7 +89,7 @@ func EditorHandler(w http.ResponseWriter, r *http.Request) {
|
||||
Text("Warning!"),
|
||||
Text("Are you sure you want to delete this entry? This action cannot be undone."),
|
||||
[]Node{
|
||||
A(Href("/app/delete/" + secret.ID), ButtonUIDanger(Text("Delete"))),
|
||||
A(Href("/app/delete/"+secret.ID), ButtonUIDanger(Text("Delete"))),
|
||||
ButtonUIOutline(ModalCloser(), Text("Close")),
|
||||
},
|
||||
),
|
||||
@@ -119,16 +102,41 @@ func EditorHandler(w http.ResponseWriter, r *http.Request) {
|
||||
Form(
|
||||
AutoComplete("off"),
|
||||
Method("POST"),
|
||||
|
||||
FormLabel(Text("Description")),
|
||||
FormInput(Type("text"), Name("description"), Value(secret.Description)),
|
||||
FormInput(Type("text"), Name("description"), Value(secret.Description), AutoFocus(), Required()),
|
||||
Br(),
|
||||
|
||||
FormLabel(Text("Username")),
|
||||
FormInput(Type("text"), Name("un"), Value(secret.Username)),
|
||||
Br(),
|
||||
|
||||
FormLabel(Text("Password")),
|
||||
FormInput(Type("password"), Name("pas"), Value(secret.Password)),
|
||||
Div(
|
||||
FlexLeftRight(
|
||||
FormLabel(Text("Password")),
|
||||
ModalActuator("password_generator", ButtonUI(Type("button"), Text("Generate a secure password"))),
|
||||
),
|
||||
Br(),
|
||||
|
||||
FormInput(Class("password"), Type("password"), Name("pas"), Value(secret.Password)),
|
||||
Br(),
|
||||
|
||||
FormLabel(Text("Show password?"), For("show")),
|
||||
FormCheck(Class("checkbox"), ID("show")),
|
||||
InlineScript(`
|
||||
let check = me(".checkbox", me());
|
||||
let passInput = me(".password", me());
|
||||
|
||||
check.on("click", () => {
|
||||
if (passInput.type === "password") {
|
||||
passInput.type = "text";
|
||||
} else if (passInput.type === "text") {
|
||||
passInput.type = "password";
|
||||
}
|
||||
});
|
||||
`),
|
||||
),
|
||||
|
||||
Br(),
|
||||
|
||||
FormLabel(Text("URL")),
|
||||
@@ -144,6 +152,15 @@ func EditorHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ButtonUISuccess(Text(btnLabel), Type("submit")),
|
||||
A(Href("/app"), ButtonUIOutline(Text("Close"), Type("button"))),
|
||||
),
|
||||
|
||||
If(editorType == EDITOR_TYPE_EDIT,
|
||||
Div(
|
||||
InlineStyle("$me { margin-top: $3; color: $color(neutral-400); font-size: var(--text-sm);}"),
|
||||
Text("Last modified: "), FormatDateTime(secret.Modified),
|
||||
Br(),
|
||||
Text("Created: "), FormatDateTime(secret.Created),
|
||||
),
|
||||
),
|
||||
),
|
||||
).Render(w)
|
||||
}
|
||||
|
||||
17
handlers/app/generator.go
Normal file
17
handlers/app/generator.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"maxwarden/middleware"
|
||||
. "maxwarden/ui"
|
||||
)
|
||||
|
||||
func GeneratorHandler(w http.ResponseWriter, r *http.Request) {
|
||||
identity := middleware.GetIdentity(r)
|
||||
session := middleware.GetSession(r)
|
||||
|
||||
AppLayout("Passkey Generator", *identity, session,
|
||||
HxLoad("/app/generator-hx"),
|
||||
).Render(w)
|
||||
}
|
||||
114
handlers/app/generator_hx.go
Normal file
114
handlers/app/generator_hx.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"maxwarden/generator"
|
||||
. "maxwarden/ui"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
. "maxwarden/basic"
|
||||
|
||||
. "maragu.dev/gomponents"
|
||||
. "maragu.dev/gomponents/html"
|
||||
hx "maragu.dev/gomponents-htmx"
|
||||
)
|
||||
|
||||
func GeneratorHxHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
r.ParseForm()
|
||||
|
||||
wordCount, _ := strconv.Atoi(r.FormValue("wordCount"))
|
||||
|
||||
if wordCount <= 0 {
|
||||
wordCount = 6
|
||||
}
|
||||
|
||||
phraseOutput := generator.GeneratePassphrase(wordCount)
|
||||
|
||||
length, _ := strconv.Atoi(r.FormValue("length"))
|
||||
if length <= 0 {
|
||||
length = 24
|
||||
}
|
||||
|
||||
numbers, _ := strconv.Atoi(r.FormValue("numbers"))
|
||||
if numbers <= 0 {
|
||||
numbers = 5
|
||||
}
|
||||
|
||||
symbols, _ := strconv.Atoi(r.FormValue("symbols"))
|
||||
if symbols <= 0 {
|
||||
symbols = 5
|
||||
}
|
||||
|
||||
passwordOutput := generator.GeneratePassword(length, numbers, symbols, false, true)
|
||||
|
||||
customFormInput := func(children ...Node) Node {
|
||||
return Div(
|
||||
InlineStyle(`
|
||||
$me {
|
||||
border: 1px solid $color(neutral-300);
|
||||
margin-bottom: $3;
|
||||
}
|
||||
`),
|
||||
FormInput(
|
||||
Group(children),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
Div(ID("generator"),
|
||||
Card(
|
||||
Heading("Generate Passphrase"),
|
||||
Form(
|
||||
hx.Post(r.URL.Path),
|
||||
hx.Swap("outerHTML"),
|
||||
hx.Target("#generator"),
|
||||
FormLabel(Text("Word Count")),
|
||||
customFormInput(Type("number"), Name("wordCount"), Value(ToString(wordCount))),
|
||||
|
||||
ButtonUI(Text("Generate"), Type("submit")),
|
||||
),
|
||||
|
||||
Br(),
|
||||
|
||||
FormLabel(Text("Output")),
|
||||
customFormInput(
|
||||
Value(phraseOutput),
|
||||
),
|
||||
),
|
||||
|
||||
Br(),
|
||||
|
||||
Card(
|
||||
Heading("Generate Password"),
|
||||
Form(
|
||||
hx.Post(r.URL.Path),
|
||||
hx.Swap("outerHTML"),
|
||||
hx.Target("#generator"),
|
||||
Flex(
|
||||
Div(
|
||||
FormLabel(Text("Password length")),
|
||||
customFormInput(Type("number"), Name("length"), Value(ToString(length))),
|
||||
),
|
||||
Div(
|
||||
FormLabel(Text("Number Count")),
|
||||
customFormInput(Type("number"), Name("numbers"), Value(ToString(numbers))),
|
||||
),
|
||||
Div(
|
||||
FormLabel(Text("Symbol Count")),
|
||||
customFormInput(Type("number"), Name("symbols"), Value(ToString(symbols))),
|
||||
),
|
||||
),
|
||||
|
||||
ButtonUI(Text("Generate"), Type("submit")),
|
||||
),
|
||||
|
||||
Br(),
|
||||
|
||||
FormLabel(Text("Output")),
|
||||
customFormInput(
|
||||
Value(passwordOutput),
|
||||
),
|
||||
),
|
||||
).Render(w)
|
||||
}
|
||||
@@ -25,7 +25,7 @@ func VaultHandler(w http.ResponseWriter, r *http.Request) {
|
||||
margin-bottom: $5;
|
||||
}
|
||||
`),
|
||||
A(Href("/app/editor/add"), ButtonUI(Text("+ Add Item"))),
|
||||
A(Href("/app/editor/add"), ButtonUI(Text("+ Add Credentials"))),
|
||||
),
|
||||
HxLoad("/app/vault-hx"),
|
||||
).Render(w)
|
||||
|
||||
@@ -72,10 +72,58 @@ func VaultHxHandler(w http.ResponseWriter, r *http.Request) {
|
||||
func(entry entries.Secret) Node {
|
||||
return Tr(
|
||||
TdLeft(Text(entry.Description)),
|
||||
TdLeft(Text(entry.Username)),
|
||||
TdLeft(Text("********")),
|
||||
TdLeft(PageLink(security.SanitizationPolicy.Sanitize(entry.URL), Text(entry.URL), false)),
|
||||
TdCenter(A(Href("/app/editor/edit/" + entry.ID), ButtonUIOutline(Icon(ICON_PENCIL, 16)))),
|
||||
TdLeft(
|
||||
IfElse(entry.Username != "",
|
||||
Flex(
|
||||
P(Text(entry.Username)),
|
||||
Span(
|
||||
Title("Click to copy username"),
|
||||
InlineStyle("$me { cursor: pointer; }"),
|
||||
Icon(ICON_COPY, 16),
|
||||
),
|
||||
InlineScript(`
|
||||
let btn = me("span", me());
|
||||
let text = me("p", me());
|
||||
|
||||
btn.on("click", () => {
|
||||
navigator.clipboard.writeText(text.innerHTML);
|
||||
});
|
||||
`),
|
||||
),
|
||||
Text("---"),
|
||||
),
|
||||
),
|
||||
TdLeft(
|
||||
IfElse(entry.Password != "",
|
||||
Flex(
|
||||
P(Text("•••••••")),
|
||||
Input(Type("hidden"), Value(entry.Password)),
|
||||
Span(
|
||||
Class("copy"),
|
||||
Title("Click to copy password"),
|
||||
InlineStyle("$me { cursor: pointer; }"),
|
||||
Icon(ICON_COPY, 16),
|
||||
),
|
||||
InlineScript(`
|
||||
let copyBtn = me(".copy", me());
|
||||
let password = me("input", me()).value;
|
||||
|
||||
|
||||
copyBtn.on("click", () => {
|
||||
navigator.clipboard.writeText(password);
|
||||
});
|
||||
`),
|
||||
),
|
||||
Text("---"),
|
||||
),
|
||||
),
|
||||
TdLeft(
|
||||
IfElse(entry.URL != "",
|
||||
PageLink(security.SanitizationPolicy.Sanitize(entry.URL), Text(entry.URL), true),
|
||||
Text("---"),
|
||||
),
|
||||
),
|
||||
TdCenter(A(Href("/app/editor/edit/"+entry.ID), ButtonUIOutline(Icon(ICON_PENCIL, 16)))),
|
||||
)
|
||||
},
|
||||
nil,
|
||||
|
||||
Reference in New Issue
Block a user