updated layout for small screens - colapsable sidepanel, fix issue with link to favicon when url_prefix is used
This commit is contained in:
@@ -0,0 +1,18 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Gobsidian Notes Service
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=webuser
|
||||||
|
Group=webuser
|
||||||
|
WorkingDirectory=/opt/projects/gobsidian
|
||||||
|
ExecStart=/opt/projects/gobsidian/gobsidian
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
|
||||||
|
# sudo cp gobsidian.service /etc/systemd/system/
|
||||||
|
# sudo systemctl daemon-reload && sudo systemctl enable gobsidian --now
|
||||||
@@ -31,6 +31,7 @@ func (h *Handlers) LoginPage(c *gin.Context) {
|
|||||||
returnTo := c.Query("return_to")
|
returnTo := c.Query("return_to")
|
||||||
c.HTML(http.StatusOK, "login", gin.H{
|
c.HTML(http.StatusOK, "login", gin.H{
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"csrf_token": token,
|
"csrf_token": token,
|
||||||
"return_to": returnTo,
|
"return_to": returnTo,
|
||||||
"ContentTemplate": "login_content",
|
"ContentTemplate": "login_content",
|
||||||
@@ -143,6 +144,7 @@ func (h *Handlers) MFALoginPage(c *gin.Context) {
|
|||||||
token, _ := c.Get("csrf_token")
|
token, _ := c.Get("csrf_token")
|
||||||
c.HTML(http.StatusOK, "mfa", gin.H{
|
c.HTML(http.StatusOK, "mfa", gin.H{
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"csrf_token": token,
|
"csrf_token": token,
|
||||||
"ContentTemplate": "mfa_content",
|
"ContentTemplate": "mfa_content",
|
||||||
"ScriptsTemplate": "mfa_scripts",
|
"ScriptsTemplate": "mfa_scripts",
|
||||||
@@ -159,6 +161,7 @@ func (h *Handlers) MFALoginVerify(c *gin.Context) {
|
|||||||
h.recordFailedAttempt(c, "mfa", "", nil)
|
h.recordFailedAttempt(c, "mfa", "", nil)
|
||||||
c.HTML(http.StatusUnauthorized, "mfa", gin.H{
|
c.HTML(http.StatusUnauthorized, "mfa", gin.H{
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"csrf_token": token,
|
"csrf_token": token,
|
||||||
"error": "Invalid code format",
|
"error": "Invalid code format",
|
||||||
"ContentTemplate": "mfa_content",
|
"ContentTemplate": "mfa_content",
|
||||||
@@ -177,7 +180,7 @@ func (h *Handlers) MFALoginVerify(c *gin.Context) {
|
|||||||
var secret string
|
var secret string
|
||||||
if err := h.authSvc.DB.QueryRow(`SELECT mfa_secret FROM users WHERE id = ?`, uid).Scan(&secret); err != nil || secret == "" {
|
if err := h.authSvc.DB.QueryRow(`SELECT mfa_secret FROM users WHERE id = ?`, uid).Scan(&secret); err != nil || secret == "" {
|
||||||
h.recordFailedAttempt(c, "mfa", "", &uid)
|
h.recordFailedAttempt(c, "mfa", "", &uid)
|
||||||
c.HTML(http.StatusUnauthorized, "mfa", gin.H{"error": "MFA not enabled", "Page": "mfa", "ContentTemplate": "mfa_content", "ScriptsTemplate": "mfa_scripts", "app_name": h.config.AppName})
|
c.HTML(http.StatusUnauthorized, "mfa", gin.H{"error": "MFA not enabled", "Page": "mfa", "ContentTemplate": "mfa_content", "ScriptsTemplate": "mfa_scripts", "app_name": h.config.AppName, "LoginAndEdits": h.config.LoginAndEdits})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !verifyTOTP(secret, code, time.Now()) {
|
if !verifyTOTP(secret, code, time.Now()) {
|
||||||
@@ -185,6 +188,7 @@ func (h *Handlers) MFALoginVerify(c *gin.Context) {
|
|||||||
h.recordFailedAttempt(c, "mfa", "", &uid)
|
h.recordFailedAttempt(c, "mfa", "", &uid)
|
||||||
c.HTML(http.StatusUnauthorized, "mfa", gin.H{
|
c.HTML(http.StatusUnauthorized, "mfa", gin.H{
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"csrf_token": token,
|
"csrf_token": token,
|
||||||
"error": "Invalid code",
|
"error": "Invalid code",
|
||||||
"ContentTemplate": "mfa_content",
|
"ContentTemplate": "mfa_content",
|
||||||
@@ -223,7 +227,7 @@ func (h *Handlers) ProfileMFASetupPage(c *gin.Context) {
|
|||||||
// create new enrollment
|
// create new enrollment
|
||||||
s, e := generateBase32Secret()
|
s, e := generateBase32Secret()
|
||||||
if e != nil {
|
if e != nil {
|
||||||
c.HTML(http.StatusInternalServerError, "error", gin.H{"error": "Failed to create enrollment", "Page": "error", "ContentTemplate": "error_content", "ScriptsTemplate": "error_scripts", "app_name": h.config.AppName})
|
c.HTML(http.StatusInternalServerError, "error", gin.H{"error": "Failed to create enrollment", "Page": "error", "ContentTemplate": "error_content", "ScriptsTemplate": "error_scripts", "app_name": h.config.AppName, "LoginAndEdits": h.config.LoginAndEdits})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, _ = h.authSvc.DB.Exec(`INSERT OR REPLACE INTO mfa_enrollments (user_id, secret) VALUES (?, ?)`, *uidPtr, s)
|
_, _ = h.authSvc.DB.Exec(`INSERT OR REPLACE INTO mfa_enrollments (user_id, secret) VALUES (?, ?)`, *uidPtr, s)
|
||||||
@@ -240,6 +244,7 @@ func (h *Handlers) ProfileMFASetupPage(c *gin.Context) {
|
|||||||
notesTree, _ := utils.BuildTreeStructure(h.config.NotesDir, h.config.NotesDirHideSidepane, h.config)
|
notesTree, _ := utils.BuildTreeStructure(h.config.NotesDir, h.config.NotesDirHideSidepane, h.config)
|
||||||
c.HTML(http.StatusOK, "mfa_setup", gin.H{
|
c.HTML(http.StatusOK, "mfa_setup", gin.H{
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"notes_tree": notesTree,
|
"notes_tree": notesTree,
|
||||||
"active_path": []string{},
|
"active_path": []string{},
|
||||||
"current_note": nil,
|
"current_note": nil,
|
||||||
@@ -342,6 +347,7 @@ func (h *Handlers) LoginPost(c *gin.Context) {
|
|||||||
h.recordFailedAttempt(c, "password", username, nil)
|
h.recordFailedAttempt(c, "password", username, nil)
|
||||||
c.HTML(http.StatusUnauthorized, "login", gin.H{
|
c.HTML(http.StatusUnauthorized, "login", gin.H{
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"csrf_token": token,
|
"csrf_token": token,
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"return_to": returnTo,
|
"return_to": returnTo,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ func (h *Handlers) CreateNotePageHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
||||||
"error": "Failed to build tree structure",
|
"error": "Failed to build tree structure",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -32,6 +33,7 @@ func (h *Handlers) CreateNotePageHandler(c *gin.Context) {
|
|||||||
|
|
||||||
c.HTML(http.StatusOK, "create", gin.H{
|
c.HTML(http.StatusOK, "create", gin.H{
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"folder_path": folderPath,
|
"folder_path": folderPath,
|
||||||
"notes_tree": notesTree,
|
"notes_tree": notesTree,
|
||||||
"active_path": utils.GetActivePath(folderPath),
|
"active_path": utils.GetActivePath(folderPath),
|
||||||
@@ -163,6 +165,7 @@ func (h *Handlers) EditNotePageHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusBadRequest, "error", gin.H{
|
c.HTML(http.StatusBadRequest, "error", gin.H{
|
||||||
"error": "Invalid note path",
|
"error": "Invalid note path",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": "Note path must end with .md or .markdown",
|
"message": "Note path must end with .md or .markdown",
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -176,6 +179,7 @@ func (h *Handlers) EditNotePageHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusBadRequest, "error", gin.H{
|
c.HTML(http.StatusBadRequest, "error", gin.H{
|
||||||
"error": "Invalid path",
|
"error": "Invalid path",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": "Path traversal is not allowed",
|
"message": "Path traversal is not allowed",
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -189,6 +193,7 @@ func (h *Handlers) EditNotePageHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusForbidden, "error", gin.H{
|
c.HTML(http.StatusForbidden, "error", gin.H{
|
||||||
"error": "Access denied",
|
"error": "Access denied",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": "This note cannot be edited",
|
"message": "This note cannot be edited",
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -203,6 +208,7 @@ func (h *Handlers) EditNotePageHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusNotFound, "error", gin.H{
|
c.HTML(http.StatusNotFound, "error", gin.H{
|
||||||
"error": "Note not found",
|
"error": "Note not found",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": "The requested note does not exist",
|
"message": "The requested note does not exist",
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -216,6 +222,7 @@ func (h *Handlers) EditNotePageHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
||||||
"error": "Failed to read note",
|
"error": "Failed to read note",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -229,6 +236,7 @@ func (h *Handlers) EditNotePageHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
||||||
"error": "Failed to build tree structure",
|
"error": "Failed to build tree structure",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -251,6 +259,7 @@ func (h *Handlers) EditNotePageHandler(c *gin.Context) {
|
|||||||
|
|
||||||
c.HTML(http.StatusOK, "edit", gin.H{
|
c.HTML(http.StatusOK, "edit", gin.H{
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"title": title,
|
"title": title,
|
||||||
"content": string(content),
|
"content": string(content),
|
||||||
"note_path": notePath,
|
"note_path": notePath,
|
||||||
|
|||||||
@@ -297,6 +297,7 @@ func (h *Handlers) AdminLogsPage(c *gin.Context) {
|
|||||||
|
|
||||||
c.HTML(http.StatusOK, "admin_logs", gin.H{
|
c.HTML(http.StatusOK, "admin_logs", gin.H{
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"NoSidebar": true,
|
"NoSidebar": true,
|
||||||
"breadcrumbs": []gin.H{{"Name": "/", "URL": "/"}, {"Name": "Admin", "URL": "/editor/admin"}, {"Name": "Logs", "URL": ""}},
|
"breadcrumbs": []gin.H{{"Name": "/", "URL": "/"}, {"Name": "Admin", "URL": "/editor/admin"}, {"Name": "Logs", "URL": ""}},
|
||||||
"Page": "admin_logs",
|
"Page": "admin_logs",
|
||||||
@@ -354,6 +355,7 @@ func (h *Handlers) ProfilePage(c *gin.Context) {
|
|||||||
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
||||||
"error": "Failed to build tree structure",
|
"error": "Failed to build tree structure",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -370,6 +372,7 @@ func (h *Handlers) ProfilePage(c *gin.Context) {
|
|||||||
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
||||||
"error": "Failed to load profile",
|
"error": "Failed to load profile",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -386,8 +389,8 @@ func (h *Handlers) ProfilePage(c *gin.Context) {
|
|||||||
"breadcrumbs": utils.GenerateBreadcrumbs(""),
|
"breadcrumbs": utils.GenerateBreadcrumbs(""),
|
||||||
"Authenticated": true,
|
"Authenticated": true,
|
||||||
"IsAdmin": isAdmin(c),
|
"IsAdmin": isAdmin(c),
|
||||||
"Email": email,
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"MFAEnabled": mfa.Valid && mfa.String != "",
|
"Email": email, "MFAEnabled": mfa.Valid && mfa.String != "",
|
||||||
"ContentTemplate": "profile_content",
|
"ContentTemplate": "profile_content",
|
||||||
"ScriptsTemplate": "profile_scripts",
|
"ScriptsTemplate": "profile_scripts",
|
||||||
"Page": "profile",
|
"Page": "profile",
|
||||||
@@ -739,6 +742,7 @@ func (h *Handlers) EditTextPageHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusBadRequest, "error", gin.H{
|
c.HTML(http.StatusBadRequest, "error", gin.H{
|
||||||
"error": "Invalid path",
|
"error": "Invalid path",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": "Path traversal is not allowed",
|
"message": "Path traversal is not allowed",
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -752,6 +756,7 @@ func (h *Handlers) EditTextPageHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
||||||
"error": "Permission check failed",
|
"error": "Permission check failed",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -762,6 +767,7 @@ func (h *Handlers) EditTextPageHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusForbidden, "error", gin.H{
|
c.HTML(http.StatusForbidden, "error", gin.H{
|
||||||
"error": "Access denied",
|
"error": "Access denied",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": "You do not have permission to view this file",
|
"message": "You do not have permission to view this file",
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -777,6 +783,7 @@ func (h *Handlers) EditTextPageHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusNotFound, "error", gin.H{
|
c.HTML(http.StatusNotFound, "error", gin.H{
|
||||||
"error": "File not found",
|
"error": "File not found",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": "The requested file does not exist",
|
"message": "The requested file does not exist",
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -792,6 +799,7 @@ func (h *Handlers) EditTextPageHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusForbidden, "error", gin.H{
|
c.HTML(http.StatusForbidden, "error", gin.H{
|
||||||
"error": "Editing not allowed",
|
"error": "Editing not allowed",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": "This file type cannot be edited here",
|
"message": "This file type cannot be edited here",
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -806,6 +814,7 @@ func (h *Handlers) EditTextPageHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
||||||
"error": "Failed to read file",
|
"error": "Failed to read file",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -820,6 +829,7 @@ func (h *Handlers) EditTextPageHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
||||||
"error": "Failed to build notes tree",
|
"error": "Failed to build notes tree",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -843,6 +853,7 @@ func (h *Handlers) EditTextPageHandler(c *gin.Context) {
|
|||||||
"notes_tree": notesTree,
|
"notes_tree": notesTree,
|
||||||
"active_path": utils.GetActivePath(folderPath),
|
"active_path": utils.GetActivePath(folderPath),
|
||||||
"breadcrumbs": utils.GenerateBreadcrumbs(folderPath),
|
"breadcrumbs": utils.GenerateBreadcrumbs(folderPath),
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"Authenticated": isAuthenticated(c),
|
"Authenticated": isAuthenticated(c),
|
||||||
"IsAdmin": isAdmin(c),
|
"IsAdmin": isAdmin(c),
|
||||||
"ContentTemplate": "edit_text_content",
|
"ContentTemplate": "edit_text_content",
|
||||||
@@ -913,6 +924,7 @@ func (h *Handlers) IndexHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
||||||
"error": "Failed to read directory",
|
"error": "Failed to read directory",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -929,6 +941,7 @@ func (h *Handlers) IndexHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
||||||
"error": "Failed to build tree structure",
|
"error": "Failed to build tree structure",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -944,6 +957,7 @@ func (h *Handlers) IndexHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
||||||
"error": "Permission check failed",
|
"error": "Permission check failed",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -954,6 +968,7 @@ func (h *Handlers) IndexHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusForbidden, "error", gin.H{
|
c.HTML(http.StatusForbidden, "error", gin.H{
|
||||||
"error": "Access denied",
|
"error": "Access denied",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": "You do not have permission to view this folder",
|
"message": "You do not have permission to view this folder",
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -989,6 +1004,7 @@ func (h *Handlers) FolderHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusBadRequest, "error", gin.H{
|
c.HTML(http.StatusBadRequest, "error", gin.H{
|
||||||
"error": "Invalid path",
|
"error": "Invalid path",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": "Path traversal is not allowed",
|
"message": "Path traversal is not allowed",
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -1002,6 +1018,7 @@ func (h *Handlers) FolderHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusForbidden, "error", gin.H{
|
c.HTML(http.StatusForbidden, "error", gin.H{
|
||||||
"error": "Access denied",
|
"error": "Access denied",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": "This directory is not accessible",
|
"message": "This directory is not accessible",
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -1015,6 +1032,7 @@ func (h *Handlers) FolderHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
||||||
"error": "Permission check failed",
|
"error": "Permission check failed",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -1025,6 +1043,7 @@ func (h *Handlers) FolderHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusForbidden, "error", gin.H{
|
c.HTML(http.StatusForbidden, "error", gin.H{
|
||||||
"error": "Access denied",
|
"error": "Access denied",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": "You do not have permission to view this folder",
|
"message": "You do not have permission to view this folder",
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -1038,6 +1057,7 @@ func (h *Handlers) FolderHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusNotFound, "error", gin.H{
|
c.HTML(http.StatusNotFound, "error", gin.H{
|
||||||
"error": "Folder not found",
|
"error": "Folder not found",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -1051,6 +1071,7 @@ func (h *Handlers) FolderHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
||||||
"error": "Failed to build tree structure",
|
"error": "Failed to build tree structure",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -1069,6 +1090,7 @@ func (h *Handlers) FolderHandler(c *gin.Context) {
|
|||||||
"breadcrumbs": utils.GenerateBreadcrumbs(folderPath),
|
"breadcrumbs": utils.GenerateBreadcrumbs(folderPath),
|
||||||
"allowed_image_extensions": h.config.AllowedImageExtensions,
|
"allowed_image_extensions": h.config.AllowedImageExtensions,
|
||||||
"allowed_file_extensions": h.config.AllowedFileExtensions,
|
"allowed_file_extensions": h.config.AllowedFileExtensions,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"Authenticated": isAuthenticated(c),
|
"Authenticated": isAuthenticated(c),
|
||||||
"IsAdmin": isAdmin(c),
|
"IsAdmin": isAdmin(c),
|
||||||
"ContentTemplate": "folder_content",
|
"ContentTemplate": "folder_content",
|
||||||
@@ -1085,6 +1107,7 @@ func (h *Handlers) NoteHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusBadRequest, "error", gin.H{
|
c.HTML(http.StatusBadRequest, "error", gin.H{
|
||||||
"error": "Invalid note path",
|
"error": "Invalid note path",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": "Note path must end with .md or .markdown",
|
"message": "Note path must end with .md or .markdown",
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -1098,6 +1121,7 @@ func (h *Handlers) NoteHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusBadRequest, "error", gin.H{
|
c.HTML(http.StatusBadRequest, "error", gin.H{
|
||||||
"error": "Invalid path",
|
"error": "Invalid path",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": "Path traversal is not allowed",
|
"message": "Path traversal is not allowed",
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -1111,6 +1135,7 @@ func (h *Handlers) NoteHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusForbidden, "error", gin.H{
|
c.HTML(http.StatusForbidden, "error", gin.H{
|
||||||
"error": "Access denied",
|
"error": "Access denied",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": "This note is not accessible",
|
"message": "This note is not accessible",
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -1124,6 +1149,7 @@ func (h *Handlers) NoteHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
||||||
"error": "Permission check failed",
|
"error": "Permission check failed",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -1134,6 +1160,7 @@ func (h *Handlers) NoteHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusForbidden, "error", gin.H{
|
c.HTML(http.StatusForbidden, "error", gin.H{
|
||||||
"error": "Access denied",
|
"error": "Access denied",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": "You do not have permission to view this note",
|
"message": "You do not have permission to view this note",
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -1169,6 +1196,7 @@ func (h *Handlers) NoteHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusNotFound, "error", gin.H{
|
c.HTML(http.StatusNotFound, "error", gin.H{
|
||||||
"error": "Note not found",
|
"error": "Note not found",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": "The requested note does not exist",
|
"message": "The requested note does not exist",
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -1180,6 +1208,7 @@ func (h *Handlers) NoteHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusNotFound, "error", gin.H{
|
c.HTML(http.StatusNotFound, "error", gin.H{
|
||||||
"error": "Note not found",
|
"error": "Note not found",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": "The requested note does not exist",
|
"message": "The requested note does not exist",
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -1194,6 +1223,7 @@ func (h *Handlers) NoteHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
||||||
"error": "Failed to read note",
|
"error": "Failed to read note",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -1207,6 +1237,7 @@ func (h *Handlers) NoteHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
||||||
"error": "Failed to render markdown",
|
"error": "Failed to render markdown",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -1220,6 +1251,7 @@ func (h *Handlers) NoteHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
||||||
"error": "Failed to build tree structure",
|
"error": "Failed to build tree structure",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -1253,6 +1285,7 @@ func (h *Handlers) NoteHandler(c *gin.Context) {
|
|||||||
"active_path": utils.GetActivePath(folderPath),
|
"active_path": utils.GetActivePath(folderPath),
|
||||||
"current_note": notePath,
|
"current_note": notePath,
|
||||||
"breadcrumbs": utils.GenerateBreadcrumbs(folderPath),
|
"breadcrumbs": utils.GenerateBreadcrumbs(folderPath),
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"Authenticated": isAuthenticated(c),
|
"Authenticated": isAuthenticated(c),
|
||||||
"IsAdmin": isAdmin(c),
|
"IsAdmin": isAdmin(c),
|
||||||
"ContentTemplate": "note_content",
|
"ContentTemplate": "note_content",
|
||||||
@@ -1377,6 +1410,7 @@ func (h *Handlers) ViewTextHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusBadRequest, "error", gin.H{
|
c.HTML(http.StatusBadRequest, "error", gin.H{
|
||||||
"error": "Invalid path",
|
"error": "Invalid path",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": "Path traversal is not allowed",
|
"message": "Path traversal is not allowed",
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -1390,6 +1424,7 @@ func (h *Handlers) ViewTextHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
||||||
"error": "Permission check failed",
|
"error": "Permission check failed",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -1400,6 +1435,7 @@ func (h *Handlers) ViewTextHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusForbidden, "error", gin.H{
|
c.HTML(http.StatusForbidden, "error", gin.H{
|
||||||
"error": "Access denied",
|
"error": "Access denied",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": "You do not have permission to view this file",
|
"message": "You do not have permission to view this file",
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -1414,6 +1450,7 @@ func (h *Handlers) ViewTextHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusNotFound, "error", gin.H{
|
c.HTML(http.StatusNotFound, "error", gin.H{
|
||||||
"error": "File not found",
|
"error": "File not found",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": "The requested file does not exist",
|
"message": "The requested file does not exist",
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -1427,6 +1464,7 @@ func (h *Handlers) ViewTextHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusForbidden, "error", gin.H{
|
c.HTML(http.StatusForbidden, "error", gin.H{
|
||||||
"error": "File type not allowed",
|
"error": "File type not allowed",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": "This file type cannot be viewed",
|
"message": "This file type cannot be viewed",
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -1440,6 +1478,7 @@ func (h *Handlers) ViewTextHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
||||||
"error": "Failed to read file",
|
"error": "Failed to read file",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -1453,6 +1492,7 @@ func (h *Handlers) ViewTextHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
||||||
"error": "Failed to build tree structure",
|
"error": "Failed to build tree structure",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -1482,6 +1522,7 @@ func (h *Handlers) ViewTextHandler(c *gin.Context) {
|
|||||||
"notes_tree": notesTree,
|
"notes_tree": notesTree,
|
||||||
"active_path": utils.GetActivePath(folderPath),
|
"active_path": utils.GetActivePath(folderPath),
|
||||||
"breadcrumbs": utils.GenerateBreadcrumbs(folderPath),
|
"breadcrumbs": utils.GenerateBreadcrumbs(folderPath),
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"Authenticated": isAuthenticated(c),
|
"Authenticated": isAuthenticated(c),
|
||||||
"IsAdmin": isAdmin(c),
|
"IsAdmin": isAdmin(c),
|
||||||
"ContentTemplate": "view_text_content",
|
"ContentTemplate": "view_text_content",
|
||||||
@@ -1664,6 +1705,7 @@ func (h *Handlers) AdminPage(c *gin.Context) {
|
|||||||
"breadcrumbs": utils.GenerateBreadcrumbs(""),
|
"breadcrumbs": utils.GenerateBreadcrumbs(""),
|
||||||
"Authenticated": true,
|
"Authenticated": true,
|
||||||
"IsAdmin": true,
|
"IsAdmin": true,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"users": users,
|
"users": users,
|
||||||
"groups": groups,
|
"groups": groups,
|
||||||
"permissions": perms,
|
"permissions": perms,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ func (h *Handlers) SettingsPageHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
c.HTML(http.StatusInternalServerError, "error", gin.H{
|
||||||
"error": "Failed to build tree structure",
|
"error": "Failed to build tree structure",
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
"ContentTemplate": "error_content",
|
"ContentTemplate": "error_content",
|
||||||
"ScriptsTemplate": "error_scripts",
|
"ScriptsTemplate": "error_scripts",
|
||||||
@@ -27,6 +28,7 @@ func (h *Handlers) SettingsPageHandler(c *gin.Context) {
|
|||||||
|
|
||||||
c.HTML(http.StatusOK, "settings", gin.H{
|
c.HTML(http.StatusOK, "settings", gin.H{
|
||||||
"app_name": h.config.AppName,
|
"app_name": h.config.AppName,
|
||||||
|
"LoginAndEdits": h.config.LoginAndEdits,
|
||||||
"notes_tree": notesTree,
|
"notes_tree": notesTree,
|
||||||
"active_path": []string{},
|
"active_path": []string{},
|
||||||
"current_note": nil,
|
"current_note": nil,
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
+72
-48
@@ -5,6 +5,7 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>{{.app_name}}</title>
|
<title>{{.app_name}}</title>
|
||||||
|
<link rel="icon" type="image/x-icon" href="{{url "/favicon.ico"}}">
|
||||||
<link rel="stylesheet" href="{{url "/static/tailwind.css"}}">
|
<link rel="stylesheet" href="{{url "/static/tailwind.css"}}">
|
||||||
<link rel="stylesheet" href="{{url "/static/styles.css"}}">
|
<link rel="stylesheet" href="{{url "/static/styles.css"}}">
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
|
||||||
@@ -166,26 +167,6 @@
|
|||||||
background-color: #b91c1c;
|
background-color: #b91c1c;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sidebar collapse behavior */
|
|
||||||
#sidebar.collapsed {
|
|
||||||
width: 2.5rem !important; /* 10px * 10 = 2.5rem */
|
|
||||||
border-right: none;
|
|
||||||
}
|
|
||||||
#sidebar.collapsed .sidebar-title,
|
|
||||||
#sidebar.collapsed .sidebar-actions,
|
|
||||||
#sidebar.collapsed .sidebar-content {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
#sidebar .toggle-btn {
|
|
||||||
background: transparent;
|
|
||||||
color: #cbd5e1;
|
|
||||||
padding: 0.25rem;
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
}
|
|
||||||
#sidebar .toggle-btn:hover {
|
|
||||||
background: #374151;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Modal styles */
|
/* Modal styles */
|
||||||
.modal-overlay {
|
.modal-overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -315,9 +296,6 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<button id="sidebar-toggle" class="toggle-btn" title="Toggle sidebar" aria-label="Toggle sidebar">
|
|
||||||
<i id="sidebar-toggle-icon" class="fas fa-chevron-left"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -345,9 +323,16 @@
|
|||||||
|
|
||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
<div class="flex-1 flex flex-col overflow-hidden">
|
<div class="flex-1 flex flex-col overflow-hidden">
|
||||||
<!-- Breadcrumbs -->
|
<!-- Top Bar (Toggle + Breadcrumbs) -->
|
||||||
|
{{if or (not .NoSidebar) .breadcrumbs}}
|
||||||
|
<div class="bg-slate-800 border-b border-gray-700 px-6 py-3 flex items-center">
|
||||||
|
{{if not .NoSidebar}}
|
||||||
|
<button id="mobile-sidebar-toggle-btn" class="text-gray-400 hover:text-white mr-4" aria-label="Toggle sidebar">
|
||||||
|
<i class="fas fa-bars text-xl"></i>
|
||||||
|
</button>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{if .breadcrumbs}}
|
{{if .breadcrumbs}}
|
||||||
<div class="bg-slate-800 border-b border-gray-700 px-6 py-3">
|
|
||||||
<nav class="flex items-center flex-wrap gap-1.5 text-sm">
|
<nav class="flex items-center flex-wrap gap-1.5 text-sm">
|
||||||
{{range $i, $crumb := .breadcrumbs}}
|
{{range $i, $crumb := .breadcrumbs}}
|
||||||
{{if $i}}<i class="fas fa-chevron-right text-gray-500 text-xs mx-1"></i>{{end}}
|
{{if $i}}<i class="fas fa-chevron-right text-gray-500 text-xs mx-1"></i>{{end}}
|
||||||
@@ -363,7 +348,9 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
</nav>
|
</nav>
|
||||||
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
<div id="sidebar-overlay" class="sidebar-overlay hidden"></div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<!-- Content Area -->
|
<!-- Content Area -->
|
||||||
@@ -563,26 +550,24 @@
|
|||||||
}, duration);
|
}, duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sidebar toggle persistence
|
// Sidebar visibility persistence
|
||||||
function applySidebarState(collapsed) {
|
function applySidebarVisibility() {
|
||||||
const sidebar = document.getElementById('sidebar');
|
const sidebar = document.getElementById('sidebar');
|
||||||
const icon = document.getElementById('sidebar-toggle-icon');
|
if (!sidebar) return;
|
||||||
if (!sidebar || !icon) return;
|
// Only apply persisted state on desktop
|
||||||
if (collapsed) {
|
if (window.innerWidth > 768) {
|
||||||
sidebar.classList.add('collapsed');
|
const visible = localStorage.getItem('sidebarVisible') !== 'false';
|
||||||
icon.classList.remove('fa-chevron-left');
|
if (!visible) {
|
||||||
icon.classList.add('fa-chevron-right');
|
sidebar.classList.add('hidden');
|
||||||
} else {
|
} else {
|
||||||
sidebar.classList.remove('collapsed');
|
sidebar.classList.remove('hidden');
|
||||||
icon.classList.remove('fa-chevron-right');
|
}
|
||||||
icon.classList.add('fa-chevron-left');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
// Restore sidebar state
|
// Apply persisted visibility
|
||||||
const collapsed = localStorage.getItem('sidebarCollapsed') === 'true';
|
applySidebarVisibility();
|
||||||
applySidebarState(collapsed);
|
|
||||||
|
|
||||||
// Check for error in query params
|
// Check for error in query params
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
@@ -590,16 +575,55 @@
|
|||||||
showNotification('The page does not exist', 'error', 5000);
|
showNotification('The page does not exist', 'error', 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wire toggle button
|
// Sidebar toggle (unified for mobile/desktop)
|
||||||
const toggleBtn = document.getElementById('sidebar-toggle');
|
const mobileToggleBtn = document.getElementById('mobile-sidebar-toggle-btn');
|
||||||
if (toggleBtn) {
|
const sidebarOverlay = document.getElementById('sidebar-overlay');
|
||||||
toggleBtn.addEventListener('click', function() {
|
const sidebar = document.getElementById('sidebar');
|
||||||
const currentlyCollapsed = document.getElementById('sidebar').classList.contains('collapsed');
|
|
||||||
const next = !currentlyCollapsed;
|
function toggleSidebar() {
|
||||||
localStorage.setItem('sidebarCollapsed', String(next));
|
if (!sidebar) return;
|
||||||
applySidebarState(next);
|
|
||||||
});
|
if (window.innerWidth <= 768) {
|
||||||
|
// Mobile behavior: toggle mobile-open class and overlay
|
||||||
|
const isOpen = sidebar.classList.contains('mobile-open');
|
||||||
|
if (isOpen) {
|
||||||
|
sidebar.classList.remove('mobile-open');
|
||||||
|
sidebarOverlay && sidebarOverlay.classList.add('hidden');
|
||||||
|
document.body.classList.remove('overflow-hidden');
|
||||||
|
} else {
|
||||||
|
sidebar.classList.add('mobile-open');
|
||||||
|
sidebarOverlay && sidebarOverlay.classList.remove('hidden');
|
||||||
|
document.body.classList.add('overflow-hidden');
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Desktop behavior: toggle hidden class and persist
|
||||||
|
const isHidden = sidebar.classList.contains('hidden');
|
||||||
|
if (isHidden) {
|
||||||
|
sidebar.classList.remove('hidden');
|
||||||
|
localStorage.setItem('sidebarVisible', 'true');
|
||||||
|
} else {
|
||||||
|
sidebar.classList.add('hidden');
|
||||||
|
localStorage.setItem('sidebarVisible', 'false');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mobileToggleBtn) {
|
||||||
|
mobileToggleBtn.addEventListener('click', toggleSidebar);
|
||||||
|
}
|
||||||
|
if (sidebarOverlay) {
|
||||||
|
sidebarOverlay.addEventListener('click', toggleSidebar);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close sidebar when clicking on a sidebar item on mobile
|
||||||
|
sidebar && sidebar.addEventListener('click', function(e) {
|
||||||
|
if (window.innerWidth <= 768) {
|
||||||
|
const item = e.target.closest('.sidebar-item');
|
||||||
|
if (item && !item.hasAttribute('target')) {
|
||||||
|
toggleSidebar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Apply persisted expanded folders, then ensure active path is expanded
|
// Apply persisted expanded folders, then ensure active path is expanded
|
||||||
applyExpandedState();
|
applyExpandedState();
|
||||||
|
|||||||
Reference in New Issue
Block a user