From 49a586db531112a8928e3c8971bfa5c93ec98508 Mon Sep 17 00:00:00 2001 From: StarFleetCPTN Date: Sat, 22 Mar 2025 16:55:36 -0700 Subject: [PATCH] feat: Update dependencies and enhance admin role management - Upgraded `golang.org/x/crypto` to v0.36.0 and other indirect dependencies to their latest versions. - Added functionality to assign admin roles to users upon creation in the main application logic. - Introduced new templates for admin role management, including forms for creating and editing roles. - Removed deprecated admin tools template to streamline the admin interface. - Added audit logging for role changes to improve tracking and accountability. --- .gitignore | 2 +- components/admin.templ | 125 ++ components/admin_audit.templ | 441 +++++ components/admin_database.templ | 302 ++-- components/admin_role_form.templ | 175 ++ components/admin_roles.templ | 123 ++ components/admin_tools.templ | 1389 -------------- components/config_form.templ | 401 +++-- components/configs.templ | 188 +- components/dashboard.templ | 360 ++-- components/error.templ | 91 + components/file_metadata.templ | 1588 +++++++---------- components/forgot_password.templ | 386 ++-- components/history.templ | 244 +-- components/home.templ | 223 ++- components/job_form.templ | 915 ++++------ components/job_run_details.templ | 308 ++-- components/jobs.templ | 322 ++-- components/layout.templ | 760 ++++---- components/login.templ | 220 ++- components/profile.templ | 284 +-- components/providers/common/common.templ | 224 +-- components/providers/destination/ftp.templ | 130 +- components/providers/destination/gdrive.templ | 259 +-- .../providers/destination/gphotos.templ | 378 ++-- components/providers/destination/local.templ | 62 +- components/providers/destination/minio.templ | 144 +- .../providers/destination/nextcloud.templ | 106 +- components/providers/destination/s3.templ | 144 +- components/providers/destination/sftp.templ | 178 +- components/providers/destination/smb.templ | 167 +- components/providers/destination/webdav.templ | 113 +- components/providers/provider_form.templ | 2 +- components/providers/source/ftp.templ | 130 +- components/providers/source/gdrive.templ | 259 +-- components/providers/source/gphotos.templ | 378 ++-- components/providers/source/local.templ | 60 +- components/providers/source/minio.templ | 144 +- components/providers/source/nextcloud.templ | 113 +- components/providers/source/s3.templ | 138 +- components/providers/source/sftp.templ | 178 +- components/providers/source/smb.templ | 167 +- components/providers/source/webdav.templ | 113 +- components/register.templ | 234 ++- components/settings.templ | 457 +++++ components/two_factor_backup_codes.templ | 316 ++-- components/two_factor_backup_verify.templ | 163 +- components/two_factor_setup.templ | 315 ++-- components/two_factor_verify.templ | 165 +- components/user_edit.templ | 241 +++ components/user_management.templ | 379 ++++ components/users.templ | 319 ++-- go.mod | 8 +- go.sum | 16 +- internal/db/audit_log.go | 46 + internal/db/db.go | 175 +- internal/db/migrations/001_initial_schema.go | 57 - .../db/migrations/002_update_gdrive_type.go | 59 + internal/db/migrations/004_add_audit_logs.go | 133 ++ .../db/migrations/005_add_default_roles.go | 227 +++ .../006_add_timestamps_to_job_histories.go | 71 + .../007_add_notification_services.go | 78 + internal/db/migrations/migrations.go | 21 +- internal/db/role.go | 299 ++++ internal/web/handlers/admin_handlers.go | 573 ++++++ internal/web/handlers/admin_tools_handlers.go | 1424 --------------- .../web/handlers/admin_tools_handlers_test.go | 1444 --------------- internal/web/handlers/auth_handlers.go | 101 +- internal/web/handlers/config_handlers.go | 154 +- internal/web/handlers/dashboard_handlers.go | 4 + internal/web/handlers/database_handlers.go | 467 +++++ internal/web/handlers/error_handlers.go | 76 + .../web/handlers/file_metadata_handlers.go | 9 +- internal/web/handlers/job_handlers.go | 161 +- internal/web/handlers/routes.go | 88 +- internal/web/handlers/settings_handlers.go | 417 +++++ internal/web/handlers/user_handlers.go | 979 +++++++++- main.go | 6 + static/js/app.js | 76 +- 79 files changed, 11902 insertions(+), 10290 deletions(-) create mode 100644 components/admin.templ create mode 100644 components/admin_audit.templ create mode 100644 components/admin_role_form.templ create mode 100644 components/admin_roles.templ delete mode 100644 components/admin_tools.templ create mode 100644 components/error.templ create mode 100644 components/settings.templ create mode 100644 components/user_edit.templ create mode 100644 components/user_management.templ create mode 100644 internal/db/audit_log.go create mode 100644 internal/db/migrations/004_add_audit_logs.go create mode 100644 internal/db/migrations/005_add_default_roles.go create mode 100644 internal/db/migrations/006_add_timestamps_to_job_histories.go create mode 100644 internal/db/migrations/007_add_notification_services.go create mode 100644 internal/db/role.go create mode 100644 internal/web/handlers/admin_handlers.go delete mode 100644 internal/web/handlers/admin_tools_handlers.go delete mode 100644 internal/web/handlers/admin_tools_handlers_test.go create mode 100644 internal/web/handlers/database_handlers.go create mode 100644 internal/web/handlers/error_handlers.go create mode 100644 internal/web/handlers/settings_handlers.go diff --git a/.gitignore b/.gitignore index 7d8fffa..b8726fd 100644 --- a/.gitignore +++ b/.gitignore @@ -81,4 +81,4 @@ backups/ # Ignore binaries -gomft +gomft \ No newline at end of file diff --git a/components/admin.templ b/components/admin.templ new file mode 100644 index 0000000..a48177c --- /dev/null +++ b/components/admin.templ @@ -0,0 +1,125 @@ +package components + +import ( + "context" +) + +// AdminLayout renders the main admin dashboard +templ AdminLayout(ctx context.Context) { + @LayoutWithContext("Administration", ctx) { +
+ + + + +
+
+ +
+
+
+

Total Users

+

1,234

+
+
+ +
+
+
+ +
+
+
+

Active Sessions

+

42

+
+
+ +
+
+
+ +
+
+
+

System Health

+

98%

+
+
+ +
+
+
+
+ + +
+
+

Recent Activity

+
+
+ + + + + + + + + + + + + + + + +
UserActionTimestamp
admin@example.comUpdated user permissions2024-02-20 14:32:10
+
+
+
+
+ } +} \ No newline at end of file diff --git a/components/admin_audit.templ b/components/admin_audit.templ new file mode 100644 index 0000000..9ad2913 --- /dev/null +++ b/components/admin_audit.templ @@ -0,0 +1,441 @@ +package components + +import ( + "context" + "fmt" + "time" +) + +// AuditLogEntry represents an audit log entry for display +type AuditLogEntry struct { + ID uint + Action string + EntityType string + EntityID uint + Username string + UserID uint + Timestamp time.Time + Details map[string]interface{} + DetailsSummary string +} + +type AuditLogsData struct { + Logs []AuditLogEntry + TotalPages int + CurrentPage int + TotalRecords int + FilterAction string + FilterEntity string + FilterUser string + FilterDateFrom string + FilterDateTo string +} + +// AdminAuditLogs renders the audit logs page +templ AdminAuditLogs(ctx context.Context, data AuditLogsData) { + @LayoutWithContext("Audit Logs", ctx) { +
+
+ +
+

+ + Audit Logs +

+ + + Export Logs + +
+ + +
+ + + + + + +
+
+

Filter Logs

+
+
+
+ +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ +
+ + +
+ + +
+ + Clear Filters + + +
+
+
+
+ + +
+
+

Audit Log Entries

+ Total entries: {fmt.Sprint(data.TotalRecords)} +
+
+ + + + + + + + + + + + if len(data.Logs) == 0 { + + + + } else { + for _, log := range data.Logs { + + + + + + + + } + } + +
TimestampUserActionEntityDetails
+ No audit logs found matching your filters. +
+ {log.Timestamp.Format("2006-01-02 15:04:05")} + + {log.Username} + + + {log.Action} + + + {log.EntityType} + + (ID: {fmt.Sprint(log.EntityID)}) + + + +
+
+ + + if data.TotalPages > 1 { +
+
+
+ Showing page {fmt.Sprint(data.CurrentPage)} of {fmt.Sprint(data.TotalPages)} +
+
+ if data.CurrentPage > 1 { + + Previous + + } + + + + + if data.CurrentPage < data.TotalPages { + + Next + + } +
+
+
+ } +
+
+
+ + + + + + } +} + +// Helper functions +func getActionClass(action string) string { + baseClass := "px-2 py-1 text-xs rounded-full " + + switch action { + case "create": + return baseClass + "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300" + case "update": + return baseClass + "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300" + case "delete": + return baseClass + "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300" + case "login": + return baseClass + "bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300" + case "logout": + return baseClass + "bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300" + default: + return baseClass + "bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300" + } +} + +func buildPaginationURL(data AuditLogsData, page int) templ.SafeURL { + url := fmt.Sprintf("/admin/audit?page=%d", page) + + if data.FilterAction != "" { + url += fmt.Sprintf("&action=%s", data.FilterAction) + } + + if data.FilterEntity != "" { + url += fmt.Sprintf("&entity=%s", data.FilterEntity) + } + + if data.FilterUser != "" { + url += fmt.Sprintf("&user=%s", data.FilterUser) + } + + if data.FilterDateFrom != "" { + url += fmt.Sprintf("&date_from=%s", data.FilterDateFrom) + } + + if data.FilterDateTo != "" { + url += fmt.Sprintf("&date_to=%s", data.FilterDateTo) + } + + return templ.SafeURL(url) +} + + diff --git a/components/admin_database.templ b/components/admin_database.templ index ca95414..81e4f4a 100644 --- a/components/admin_database.templ +++ b/components/admin_database.templ @@ -16,84 +16,29 @@ type BackupFile struct { // AdminDatabaseTools renders the database tools page with backup and restore functionality templ AdminDatabaseTools(ctx context.Context, backups []BackupFile) { @LayoutWithContext("Database Tools", ctx) { -
- - - - -
- -
+
+
+ +

- Database Management + Database Tools

-

Manage database backups, restoration and maintenance

+
+ +
- -
- -
-
-
- -
-

Backup Database

-
-

Create a backup of the current database state.

-
- -
-
+ +
- -
+ +
+ +
@@ -101,7 +46,7 @@ templ AdminDatabaseTools(ctx context.Context, backups []BackupFile) {

Optimize Database

Vacuum the database to reclaim space and optimize performance.

-
+
-
+
@@ -118,7 +63,7 @@ templ AdminDatabaseTools(ctx context.Context, backups []BackupFile) {

Clear Job History

Remove old job history records to free up database space.

- +
-
+
@@ -149,10 +94,10 @@ templ AdminDatabaseTools(ctx context.Context, backups []BackupFile) {
-
+

Database Backups

- + Refresh @@ -182,22 +127,17 @@ templ AdminDatabaseTools(ctx context.Context, backups []BackupFile) { { backup.Created.Format("2006-01-02 15:04:05") }
- - - - - -
- -
+ +

Restore from Backup File

- +
@@ -245,13 +185,15 @@ templ AdminDatabaseTools(ctx context.Context, backups []BackupFile) {
- -
-
- -
-

Important Safety Information

-
    + +
    +
    +
    + +
    +
    +

    Important Safety Information

    +
    • Always create a backup before making significant changes
    • Restoring a backup will overwrite all current data
    • Database optimization may take time for large databases
    • @@ -264,13 +206,13 @@ templ AdminDatabaseTools(ctx context.Context, backups []BackupFile) {
    -
+ + } +
} // Update the main FileMetadataList template to use the partial template templ FileMetadataList(ctx context.Context, data FileMetadataListData) { @LayoutWithContext("File Metadata", ctx) { - - -
-
-

- if data.Job != nil { - Files for Job: { data.Job.Name } - } else { - File Metadata - } -

- -
- - -
-
-

Filter Files

-
-
+ + +
+
Filter Files
if data.Job == nil {
- + + class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" />
}
- - @@ -478,39 +388,41 @@ templ FileMetadataList(ctx context.Context, data FileMetadataListData) {
- + + class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" />
-
-
- +
-
- - -
-
-

Files ({ strconv.FormatInt(data.TotalCount, 10) })

-
- Page { strconv.Itoa(data.Page) } of { strconv.Itoa(data.TotalPages) } + + +
+
+
Files ({ strconv.FormatInt(data.TotalCount, 10) })
+ + Page { strconv.Itoa(data.Page) } of { strconv.Itoa(data.TotalPages) } + +
+
+ if len(data.Files) > 0 { + @FileMetadataListPartial(data) + } else { +
+ + + +

No file metadata found matching your criteria.

+
+ }
-
-
- if len(data.Files) > 0 { - @FileMetadataListPartial(data) - } else { -
- -

No file metadata found matching your criteria.

-
- }
@@ -520,265 +432,208 @@ templ FileMetadataList(ctx context.Context, data FileMetadataListData) { // FileMetadataDetails renders detailed information about a file templ FileMetadataDetails(ctx context.Context, data FileMetadataDetailsData) { @LayoutWithContext("File Details", ctx) { - - -
+
-
-
-

- File: { data.File.FileName } -

+
+ +
+
+ { data.File.FileName } +
+

+ File ID: { strconv.FormatUint(uint64(data.File.ID), 10) } +

-
+ + +
+
-

File Information

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ID{ strconv.FormatUint(uint64(data.File.ID), 10) }
Filename{ data.File.FileName }
Size{ formatFileSize(data.File.FileSize) }
Hash - if data.File.FileHash != "" { - { data.File.FileHash } - } else { - Not available - } -
Status - - { data.File.Status } - -
Original Path{ data.File.OriginalPath }
Destination Path{ data.File.DestinationPath }
+
+ File Information +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Filename + + { data.File.FileName } +
+ Size + + { formatFileSize(data.File.FileSize) } +
+ Hash + + if data.File.FileHash != "" { + { data.File.FileHash } + } else { + Not available + } +
+ Status + + + { data.File.Status } + +
+ Original Path + +
+ + { data.File.OriginalPath } +
+
+ Destination Path + +
+ + { data.File.DestinationPath } +
+
+
+
-

Processing Information

- - - - - - - - - - - - - - - - - - - if data.File.Status == "error" && data.File.ErrorMessage != "" { - - - +
+ Processing Information +
+
+
Job - - { data.File.Job.Name } - -
Processed Time{ data.File.ProcessedTime.Format("2006-01-02 15:04:05") }
Creation Time{ data.File.CreationTime.Format("2006-01-02 15:04:05") }
Modification Time{ data.File.ModTime.Format("2006-01-02 15:04:05") }
Error{ data.File.ErrorMessage }
+ + + + - } - - - - - - - - - -
+ Job + + + { data.File.Job.Name } + +
Record Created{ data.File.CreatedAt.Format("2006-01-02 15:04:05") }
Record Updated{ data.File.UpdatedAt.Format("2006-01-02 15:04:05") }
+ + + Processed Time + + +
+ + { data.File.ProcessedTime.Format("2006-01-02 15:04:05") } +
+ + + + + Creation Time + + +
+ + { data.File.CreationTime.Format("2006-01-02 15:04:05") } +
+ + + + + Modification Time + + +
+ + { data.File.ModTime.Format("2006-01-02 15:04:05") } +
+ + + if data.File.Status == "error" && data.File.ErrorMessage != "" { + + + Error + + +
+ + { data.File.ErrorMessage } +
+ + + } + + + Record Created + + + { data.File.CreatedAt.Format("2006-01-02 15:04:05") } + + + + + Record Updated + + + { data.File.UpdatedAt.Format("2006-01-02 15:04:05") } + + + + +
-
- + + @FileMetadataDialog( + fmt.Sprintf("delete-file-dialog-%d", data.File.ID), + "Delete File Metadata", + fmt.Sprintf("Are you sure you want to delete the metadata for '%s'? This cannot be undone.", data.File.FileName), + "btn-danger", + "Delete", + "delete", + data.File.ID, + data.File.FileName, + "details", + ) + + +
+ Back to Files - - - @FileMetadataDialog( - fmt.Sprintf("delete-file-dialog-%d", data.File.ID), - "Delete File Metadata", - fmt.Sprintf("Are you sure you want to delete the metadata for '%s'? This cannot be undone.", data.File.FileName), - "btn-danger", - "Delete", - "delete", - data.File.ID, - data.File.FileName, - "details", - )
@@ -791,279 +646,79 @@ templ FileMetadataDetails(ctx context.Context, data FileMetadataDetailsData) { // FileMetadataSearch renders an advanced search form for file metadata templ FileMetadataSearch(ctx context.Context, data FileMetadataSearchData) { @LayoutWithContext("Search Files", ctx) { - - -
+
-
-
-

- Advanced File Search -

-
-
-
-
- - +
+
Advanced File Search
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- -
- -
-
- -
+
+
-
-
-

Search Results ({ strconv.FormatInt(data.TotalCount, 10) })

-
+
+
+
Search Results ({ strconv.FormatInt(data.TotalCount, 10) })
+ Page { strconv.Itoa(data.Page) } of { strconv.Itoa(data.TotalPages) } -
+
-
+
@FileMetadataSearchContent(data)
@@ -1090,120 +745,149 @@ templ FileMetadataSearchContent(data FileMetadataSearchData) { ) }
- - - - - - - - - - - - - - if len(data.Files) > 0 { - for _, file := range data.Files { - - - - - - - +
IDFilenameSizeProcessedStatusJobActions
{ strconv.FormatUint(uint64(file.ID), 10) } - - { file.FileName } - - { formatFileSize(file.FileSize) }{ file.ProcessedTime.Format("2006-01-02 15:04:05") } - - { file.Status } - - - if file.Job.ID > 0 { - - { file.Job.Name } + +
+ + + + + + + + + + + + + + if len(data.Files) > 0 { + for _, file := range data.Files { + + + - + + + + + + + } + } else { + + } - } else { - - - - } - -
IDFilenameSizeProcessedStatusJobActions
{ strconv.FormatUint(uint64(file.ID), 10) } + + { file.FileName } - } else { - N/A - } - - - - - - + { formatFileSize(file.FileSize) }{ file.ProcessedTime.Format("2006-01-02 15:04:05") } + + { file.Status } + + + if file.Job.ID > 0 { + + { file.Job.Name } + + } else { + N/A + } + +
+ + + + +
+
+ + + +

No files found matching your search criteria.

- -

No files found matching your search criteria.

-
+
+
if data.TotalPages > 1 { -
-
- if data.Page > 1 { - - - - } else { - - - - } - - for i := 1; i <= data.TotalPages; i++ { - if i == data.Page { - - { strconv.Itoa(i) } +
-
+
  • + if data.Page == data.TotalPages { + + Next + + } else { + + Next + + } +
  • + + } } diff --git a/components/forgot_password.templ b/components/forgot_password.templ index 1de4528..eecab8b 100644 --- a/components/forgot_password.templ +++ b/components/forgot_password.templ @@ -6,102 +6,91 @@ import ( templ ForgotPassword(ctx context.Context, errorMessage string, successMessage string) { @LayoutWithContext("Forgot Password", ctx) { -
    -
    -
    -
    -
    -
    - -
    -

    Password Reset

    -

    Enter your email to receive a reset link

    +
    +
    +
    +
    +
    +
    - - if errorMessage != "" { - - } - - if successMessage != "" { - - } - -
    -
    - -
    -
    - -
    - -
    -

    - We'll send a password reset link to this email -

    -
    - -
    - -
    -
    +

    Password Reset

    +

    Enter your email to receive a reset link

    -
    -

    - - - Back to login - -

    + if errorMessage != "" { + + } + + if successMessage != "" { + + } + +
    +
    + +
    +
    + +
    + +
    +

    + We'll send a password reset link to this email +

    +
    + + +
    + +
    - -
    -
    +
    +
    Secure, encrypted connection
    @@ -114,125 +103,116 @@ templ ForgotPassword(ctx context.Context, errorMessage string, successMessage st // Reset password page for when users click the link from their email templ ResetPassword(ctx context.Context, token string, errorMessage string) { @LayoutWithContext("Reset Password", ctx) { -
    -
    -
    -
    -
    -
    - -
    -

    Reset Password

    -

    Create a new password for your account

    +
    +
    +
    +
    +
    +
    - - if errorMessage != "" { - - } - -
    - - -
    - -
    -
    - -
    - -
    -

    - Minimum 8 characters -

    -
    - -
    - -
    -
    - -
    - -
    -

    - - Passwords must match - - - - Passwords do not match - -

    -
    - -
    - -
    -
    +

    Reset Password

    +

    Create a new password for your account

    -
    -

    - - - Back to login - -

    + if errorMessage != "" { + + } + +
    + + +
    + +
    +
    + +
    + +
    +

    + Minimum 8 characters +

    +
    + +
    + +
    +
    + +
    + +
    +

    + + Passwords must match + + + + Passwords do not match + +

    +
    + + +
    + +
    - -
    -
    +
    +
    Secure, encrypted connection
    diff --git a/components/history.templ b/components/history.templ index 60cc885..ed77e64 100644 --- a/components/history.templ +++ b/components/history.templ @@ -49,66 +49,88 @@ func getConfigNameForHistory(history db.JobHistory, configs map[uint]db.Transfer // HistoryContent renders only the content part of the history page for HTMX requests templ HistoryContent(ctx context.Context, data HistoryData) { if len(data.History) == 0 { -
    -
    - +
    +
    + + +
    -

    No transfer history

    +

    No transfer history

    if data.SearchTerm != "" { -

    No results found for "{ data.SearchTerm }". Try a different search term or .

    +

    + No results found for "{ data.SearchTerm }". Try a different search term or + . +

    } else if data.CurrentPage > 1 { -

    No more results on this page. .

    +

    + No more results on this page. + . +

    } else { -

    Transfer history will appear here once jobs have run.

    +

    Transfer history will appear here once jobs have run.

    }
    } else { -
    -
      +
      +
        for _, history := range data.History { -
      • +
      • -

        { getConfigNameForHistory(history, data.Configs) }

        +

        { getConfigNameForHistory(history, data.Configs) }

        if history.Status == "completed" { - + Completed } else if history.Status == "failed" { - + Failed } else { - + { history.Status } }
        -

        - +

        + Started: { history.StartTime.Format("Jan 02, 2006 15:04:05") }

        if history.EndTime != nil { -

        - +

        + Duration: { history.EndTime.Sub(history.StartTime).String() }

        }
        -
        +
        if history.BytesTransferred > 0 { - +

        { formatBytes(history.BytesTransferred) } transferred

        @@ -116,9 +138,9 @@ templ HistoryContent(ctx context.Context, data HistoryData) {
        if history.ErrorMessage != "" { -
        +

        - + Error: { history.ErrorMessage }

        @@ -131,15 +153,17 @@ templ HistoryContent(ctx context.Context, data HistoryData) { if data.TotalPages > 1 { -
        -
        +
        + +
        if data.CurrentPage > 1 { } else { - + Previous } @@ -159,7 +183,8 @@ templ HistoryContent(ctx context.Context, data HistoryData) { hx-target="#history-content" hx-vals={ fmt.Sprintf(`{"page": %d, "pageSize": %d, "search": "%s"}`, data.CurrentPage+1, data.PageSize, data.SearchTerm) } hx-indicator="#mobile-next-indicator" - class="flex-1 h-10 flex items-center justify-center bg-secondary-200 text-secondary-800 hover:bg-secondary-300 rounded dark:bg-secondary-700 dark:text-secondary-200 dark:hover:bg-secondary-600 relative"> + class="w-full inline-flex justify-center items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-400 dark:hover:bg-gray-700 relative" + > Next @@ -168,14 +193,16 @@ templ HistoryContent(ctx context.Context, data HistoryData) { } else { - + Next }
        -
      • + } +
      +
    }
    -
    -

    - - Transfer jobs run according to their schedule and transfer files between configured sources and destinations -

    +
    +
    +
    + +
    +
    +

    + Transfer jobs run according to their schedule and transfer files between configured sources and destinations +

    +
    +
    + + } } diff --git a/components/layout.templ b/components/layout.templ index 5b33a6a..b96515e 100644 --- a/components/layout.templ +++ b/components/layout.templ @@ -41,7 +41,7 @@ templ Layout(title string) { templ LayoutWithContext(title string, ctx context.Context) { - + @@ -55,7 +55,10 @@ templ LayoutWithContext(title string, ctx context.Context) { { title } - GoMFT + + + @@ -113,393 +116,482 @@ templ LayoutWithContext(title string, ctx context.Context) { 800: '#075985', 900: '#0c4a6e', 950: '#082f49', - }, - secondary: { - 50: '#f8fafc', - 100: '#f1f5f9', - 200: '#e2e8f0', - 300: '#cbd5e1', - 400: '#94a3b8', - 500: '#64748b', - 600: '#475569', - 700: '#334155', - 800: '#1e293b', - 900: '#0f172a', - 950: '#020617', - }, + } }, fontFamily: { sans: ['Inter var', 'ui-sans-serif', 'system-ui', '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Roboto', 'Helvetica Neue', 'Arial', 'sans-serif'], - }, - boxShadow: { - 'custom': '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)', - 'custom-lg': '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)', - }, + } } } } - + - + if isLoggedIn(ctx) { - +
    + + + +
    +
    +
    + + + +
    + -
    - - -
    - - +
    - - - -
    -
    - - Dashboard - - - Configs - - - Jobs - - - History - - - Files - - if isAdmin(ctx) { - - Users - - - Admin Tools - - } - - Profile - -
    - -
    + + +
    + +
    + +
    + + +
    +
    +
    + + + { children... } +
    +
    +
    + + +
    + } else { + +
    +
    + { children... } +
    + +
    } -
    - - - - { children... } -
    - - if isLoggedIn(ctx) { - - - } + } diff --git a/components/login.templ b/components/login.templ index 6e617f2..23f52c3 100644 --- a/components/login.templ +++ b/components/login.templ @@ -7,131 +7,119 @@ import ( templ Login(ctx context.Context, errorMessage string) { @LayoutWithContext("Login", ctx) { -
    -
    -
    -
    -
    -
    - -
    -

    Sign In

    -

    Access your GoMFT account

    +
    +
    +
    +
    +
    +
    - - if errorMessage != "" { - if strings.HasPrefix(errorMessage, "Password reset") || strings.Contains(errorMessage, "success") { - - } else { - - } - } - -
    -
    -
    - -
    -
    - -
    - -
    -
    -
    - -
    -
    - -
    - -
    -
    -
    - -
    -
    - - -
    - - -
    - -
    - -
    -
    +

    Sign In

    +

    Access your GoMFT account

    -
    -

    + if errorMessage != "" { + if strings.HasPrefix(errorMessage, "Password reset") || strings.Contains(errorMessage, "success") { +

    + } else { + + } + } + +
    +
    +
    + +
    +
    + +
    + +
    +
    +
    + +
    +
    + +
    + +
    +
    +
    + +
    +
    + + +
    + Forgot password? +
    + + +
    + +
    +

    Contact an administrator to create an account

    - -
    -
    +
    +
    Secure, encrypted connection
    diff --git a/components/profile.templ b/components/profile.templ index eca0c2b..221042c 100644 --- a/components/profile.templ +++ b/components/profile.templ @@ -5,55 +5,53 @@ import ( "github.com/starfleetcptn/gomft/internal/db" ) -// Dialog component for 2FA disable confirmation +// Dialog component for 2FA disable confirmation with Flowbite styling templ TwoFactorDisableDialog() { -