# EMLy Application Documentation EMLy is a desktop email viewer application built for 3gIT, designed to open and display `.eml` and `.msg` email files on Windows. It provides a modern, user-friendly interface for viewing email content, attachments, and metadata. ## Table of Contents 1. [Architecture Overview](#architecture-overview) 2. [Technology Stack](#technology-stack) 3. [Project Structure](#project-structure) 4. [Backend (Go)](#backend-go) 5. [Frontend (SvelteKit)](#frontend-sveltekit) 6. [State Management](#state-management) 7. [Internationalization (i18n)](#internationalization-i18n) 8. [UI Components](#ui-components) 9. [Key Features](#key-features) 10. [Build & Development](#build--development) --- ## Architecture Overview EMLy is built using the **Wails v2** framework, which combines a Go backend with a web-based frontend. This architecture allows: - **Go Backend**: Handles file operations, Windows API calls, email parsing, and system interactions - **Web Frontend**: Provides the user interface using SvelteKit with Svelte 5 - **Bridge**: Wails automatically generates TypeScript bindings for Go functions, enabling seamless communication ``` ┌─────────────────────────────────────────────────────────┐ │ EMLy Application │ ├─────────────────────────────────────────────────────────┤ │ Frontend (SvelteKit + Svelte 5) │ │ ├── Routes & Pages │ │ ├── UI Components (shadcn-svelte) │ │ ├── State Management (Svelte 5 Runes) │ │ └── i18n (ParaglideJS) │ ├─────────────────────────────────────────────────────────┤ │ Wails Bridge (Auto-generated TypeScript bindings) │ ├─────────────────────────────────────────────────────────┤ │ Backend (Go - Modular Architecture) │ │ ├── app.go - Core struct & lifecycle │ │ ├── app_mail.go - Email parsing (EML/MSG/PEC) │ │ ├── app_viewer.go - Viewer window management │ │ ├── app_screenshot.go - Window capture │ │ ├── app_bugreport.go - Bug reporting system │ │ ├── app_settings.go - Settings import/export │ │ ├── app_system.go - Windows system utilities │ │ ├── app_update.go - Self-hosted update system │ │ └── backend/utils/ - Shared utilities │ └─────────────────────────────────────────────────────────┘ ``` --- ## Technology Stack ### Backend - **Go 1.21+**: Primary backend language - **Wails v2**: Desktop application framework - **Windows Registry API**: For checking default file handlers - **GDI/User32 APIs**: For screenshot functionality ### Frontend - **SvelteKit**: Application framework - **Svelte 5**: UI framework with Runes ($state, $derived, $effect, $props) - **TypeScript**: Type-safe JavaScript - **shadcn-svelte**: UI component library - **Tailwind CSS**: Utility-first CSS framework - **ParaglideJS**: Internationalization - **Lucide Icons**: Icon library - **Bun**: JavaScript runtime and package manager --- ## Project Structure ``` EMLy/ ├── app.go # Core App struct, lifecycle, and configuration ├── app_mail.go # Email reading methods (EML, MSG, PEC) ├── app_viewer.go # Viewer window management (image, PDF, EML) ├── app_screenshot.go # Screenshot capture functionality ├── app_bugreport.go # Bug report creation and submission ├── app_settings.go # Settings import/export ├── app_system.go # Windows system utilities (registry, encoding) ├── main.go # Application entry point ├── logger.go # Logging utilities ├── wails.json # Wails configuration ├── backend/ │ └── utils/ │ ├── mail/ │ │ ├── eml_reader.go # EML file parsing │ │ ├── msg_reader.go # MSG file parsing │ │ ├── mailparser.go # MIME email parsing │ │ └── file_dialog.go # File dialog utilities │ ├── screenshot_windows.go # Windows screenshot capture │ ├── debug_windows.go # Debugger detection │ ├── ini-reader.go # Configuration file parsing │ ├── machine-identifier.go # System info collection │ └── file-metadata.go # File metadata utilities ├── frontend/ │ ├── src/ │ │ ├── routes/ # SvelteKit routes │ │ │ ├── +layout.svelte # Root layout │ │ │ ├── +error.svelte # Error page │ │ │ ├── (app)/ # Main app route group │ │ │ │ ├── +layout.svelte # App layout with sidebar │ │ │ │ ├── +page.svelte # Mail viewer page │ │ │ │ └── settings/ │ │ │ │ └── +page.svelte # Settings page │ │ │ ├── image/ # Image viewer route │ │ │ │ ├── +layout.svelte │ │ │ │ └── +page.svelte │ │ │ └── pdf/ # PDF viewer route │ │ │ ├── +layout.svelte │ │ │ └── +page.svelte │ │ ├── lib/ │ │ │ ├── components/ # Svelte components │ │ │ │ ├── MailViewer.svelte │ │ │ │ ├── SidebarApp.svelte │ │ │ │ ├── UnsavedBar.svelte │ │ │ │ └── ui/ # shadcn-svelte components │ │ │ ├── stores/ # State management │ │ │ │ ├── app.ts │ │ │ │ ├── mail-state.svelte.ts │ │ │ │ └── settings.svelte.ts │ │ │ ├── paraglide/ # i18n runtime │ │ │ ├── wailsjs/ # Auto-generated Go bindings │ │ │ ├── types/ # TypeScript types │ │ │ └── utils/ # Utility functions │ │ │ └── mail/ # Email utilities (modular) │ │ │ ├── index.ts # Barrel export │ │ │ ├── constants.ts # IFRAME_UTIL_HTML, CONTENT_TYPES, etc. │ │ │ ├── data-utils.ts # arrayBufferToBase64, createDataUrl │ │ │ ├── attachment-handlers.ts # openPDFAttachment, openImageAttachment │ │ │ └── email-loader.ts # loadEmailFromPath, processEmailBody │ │ └── messages/ # i18n translation files │ │ ├── en.json │ │ └── it.json │ ├── static/ # Static assets │ └── package.json └── config.ini # Application configuration ``` --- ## Backend (Go) ### Entry Point (`main.go`) The application starts in `main.go`, which: 1. Initializes the logger 2. Parses command-line arguments for: - `.eml` or `.msg` files to open on startup - `--view-image=` for image viewer mode - `--view-pdf=` for PDF viewer mode 3. Configures Wails with window options 4. Sets up single-instance lock to prevent multiple main windows 5. Binds the `App` struct for frontend access ```go // Single instance with unique ID based on mode uniqueId := "emly-app-lock" if strings.Contains(arg, "--view-image") { uniqueId = "emly-viewer-" + arg windowTitle = "EMLy Image Viewer" } ``` ### Application Core (`app.go`) The `App` struct is the main application controller, exposed to the frontend via Wails bindings. The code is organized into multiple files for maintainability. #### Key Properties ```go type App struct { ctx context.Context // Wails application context StartupFilePath string // File opened via command line CurrentMailFilePath string // Currently loaded mail file openImagesMux sync.Mutex // Mutex for image viewer tracking openImages map[string]bool // Track open image viewers openPDFsMux sync.Mutex // Mutex for PDF viewer tracking openPDFs map[string]bool // Track open PDF viewers openEMLsMux sync.Mutex // Mutex for EML viewer tracking openEMLs map[string]bool // Track open EML viewers } ``` #### Backend File Organization The Go backend is split into logical files: | File | Purpose | |------|---------| | `app.go` | Core App struct, constructor, lifecycle methods (startup/shutdown), configuration | | `app_mail.go` | Email reading: `ReadEML`, `ReadMSG`, `ReadPEC`, `ReadMSGOSS`, `ShowOpenFileDialog` | | `app_viewer.go` | Viewer windows: `OpenImageWindow`, `OpenPDFWindow`, `OpenEMLWindow`, `OpenPDF`, `OpenImage`, `GetViewerData` | | `app_screenshot.go` | Screenshots: `TakeScreenshot`, `SaveScreenshot`, `SaveScreenshotAs` | | `app_bugreport.go` | Bug reports: `CreateBugReportFolder`, `SubmitBugReport`, `zipFolder` | | `app_settings.go` | Settings I/O: `ExportSettings`, `ImportSettings` | | `app_system.go` | System utilities: `CheckIsDefaultEMLHandler`, `OpenDefaultAppsSettings`, `ConvertToUTF8`, `OpenFolderInExplorer` | | `app_update.go` | Update system: `CheckForUpdates`, `DownloadUpdate`, `InstallUpdate`, `GetUpdateStatus` | #### Core Methods by Category **Lifecycle & Configuration (`app.go`)** | Method | Description | |--------|-------------| | `startup(ctx)` | Wails startup callback, saves context | | `shutdown(ctx)` | Wails shutdown callback for cleanup | | `QuitApp()` | Terminates the application | | `GetConfig()` | Returns application configuration from `config.ini` | | `SaveConfig(cfg)` | Saves configuration to `config.ini` | | `GetStartupFile()` | Returns file path passed via command line | | `SetCurrentMailFilePath()` | Updates the current mail file path | | `GetMachineData()` | Returns system information | | `IsDebuggerRunning()` | Checks if a debugger is attached | **Email Reading (`app_mail.go`)** | Method | Description | |--------|-------------| | `ReadEML(path)` | Parses a standard .eml file | | `ReadMSG(path, useExternal)` | Parses a Microsoft .msg file | | `ReadPEC(path)` | Parses PEC (Italian certified email) files | | `ShowOpenFileDialog()` | Opens native file picker for EML/MSG files | **Viewer Windows (`app_viewer.go`)** | Method | Description | |--------|-------------| | `OpenImageWindow(data, filename)` | Opens image in built-in viewer | | `OpenPDFWindow(data, filename)` | Opens PDF in built-in viewer | | `OpenEMLWindow(data, filename)` | Opens EML attachment in new EMLy window | | `OpenImage(data, filename)` | Opens image with system default app | | `OpenPDF(data, filename)` | Opens PDF with system default app | | `GetViewerData()` | Returns viewer data for viewer mode detection | **Screenshots (`app_screenshot.go`)** | Method | Description | |--------|-------------| | `TakeScreenshot()` | Captures window screenshot as base64 PNG | | `SaveScreenshot()` | Saves screenshot to temp directory | | `SaveScreenshotAs()` | Opens save dialog for screenshot | **Bug Reports (`app_bugreport.go`)** | Method | Description | |--------|-------------| | `CreateBugReportFolder()` | Creates folder with screenshot and mail file | | `SubmitBugReport(input)` | Creates complete bug report with ZIP archive, attempts server upload | | `UploadBugReport(folderPath, input)` | Uploads bug report files to configured API server via multipart POST | **Settings (`app_settings.go`)** | Method | Description | |--------|-------------| | `ExportSettings(json)` | Exports settings to JSON file | | `ImportSettings()` | Imports settings from JSON file | **System Utilities (`app_system.go`)** | Method | Description | |--------|-------------| | `CheckIsDefaultEMLHandler()` | Checks if EMLy is default for .eml files | | `OpenDefaultAppsSettings()` | Opens Windows default apps settings | | `ConvertToUTF8(string)` | Converts string to valid UTF-8 | | `OpenFolderInExplorer(path)` | Opens folder in Windows Explorer | ### Email Parsing (`backend/utils/mail/`) #### EML Reader (`eml_reader.go`) Reads standard `.eml` files using the `mailparser.go` MIME parser. #### MSG Reader (`msg_reader.go`) Handles Microsoft Outlook `.msg` files using external conversion. #### Mail Parser (`mailparser.go`) A comprehensive MIME email parser that handles: - Multipart messages (mixed, alternative, related) - Text and HTML bodies - Attachments with proper content-type detection - Embedded files (inline images) - Various content transfer encodings (base64, quoted-printable, 7bit, 8bit) The `EmailData` structure returned to the frontend: ```go type EmailData struct { Subject string From string To []string Cc []string Bcc []string Body string // HTML or text body Attachments []AttachmentData IsPec bool // Italian certified email } ``` ### Screenshot Utility (`backend/utils/screenshot_windows.go`) Captures the application window using Windows GDI APIs: - Uses `FindWindowW` to locate window by title - Uses `DwmGetWindowAttribute` for DPI-aware window bounds - Creates compatible DC and bitmap for capture - Returns image as base64-encoded PNG --- ## Frontend (SvelteKit) ### Route Structure SvelteKit uses file-based routing. The `(app)` folder is a route group that applies the main app layout. ``` routes/ ├── +layout.svelte # Root layout (minimal) ├── +error.svelte # Global error page ├── (app)/ # Main app group │ ├── +layout.svelte # App layout with titlebar, sidebar, footer │ ├── +layout.ts # Server data loader │ ├── +page.svelte # Main mail viewer page │ └── settings/ │ ├── +page.svelte # Settings page │ └── +layout.ts # Settings data loader ├── image/ # Standalone image viewer │ ├── +layout.svelte │ └── +page.svelte └── pdf/ # Standalone PDF viewer ├── +layout.svelte └── +page.svelte ``` ### Main App Layout (`(app)/+layout.svelte`) The app layout provides: 1. **Custom Titlebar**: Windows-style titlebar with minimize/maximize/close buttons - Draggable for window movement - Double-click to maximize/restore 2. **Sidebar Provider**: Collapsible navigation sidebar 3. **Footer Bar**: Quick access icons for: - Toggle sidebar - Navigate to home - Navigate to settings - Open bug report dialog - Reload application 4. **Bug Report Dialog**: Complete bug reporting system with: - Screenshot capture on dialog open - Name, email, description fields - System info collection - Creates ZIP archive with all data 5. **Toast Notifications**: Using svelte-sonner 6. **Debugger Protection**: Detects attached debuggers and can quit if detected ### Mail Viewer (`(app)/+page.svelte` + `MailViewer.svelte`) The mail viewer is split into two parts: - `+page.svelte`: Page wrapper that initializes mail state from startup file - `MailViewer.svelte`: Core email viewing component #### MailViewer Features - **Empty State**: Shows "Open EML/MSG File" button when no email loaded - **Email Header**: Displays subject, from, to, cc, bcc fields - **PEC Badge**: Shows green badge for Italian certified emails - **Attachments Bar**: Horizontal scrollable list of attachments with type-specific icons - **Email Body**: Rendered in sandboxed iframe for security - **Loading Overlay**: Shows spinner during file loading #### Attachment Handling ```typescript // Different handlers based on file type if (att.contentType.startsWith("image/")) { // Opens in built-in or external viewer based on settings await OpenImageWindow(base64Data, filename); } else if (att.filename.toLowerCase().endsWith(".pdf")) { // Opens in built-in or external PDF viewer await OpenPDFWindow(base64Data, filename); } else if (att.filename.toLowerCase().endsWith(".eml")) { // Opens in new EMLy instance await OpenEMLWindow(base64Data, filename); } else { // Download as file ... } ``` ### Frontend Mail Utilities (`lib/utils/mail/`) The frontend email handling code is organized into modular utility files: | File | Purpose | |------|---------| | `index.ts` | Barrel export for all mail utilities | | `constants.ts` | Constants: `IFRAME_UTIL_HTML`, `CONTENT_TYPES`, `PEC_FILES`, `EMAIL_EXTENSIONS` | | `data-utils.ts` | Data conversion: `arrayBufferToBase64`, `createDataUrl`, `looksLikeBase64`, `tryDecodeBase64` | | `attachment-handlers.ts` | Attachment opening: `openPDFAttachment`, `openImageAttachment`, `openEMLAttachment` | | `email-loader.ts` | Email loading: `loadEmailFromPath`, `openAndLoadEmail`, `processEmailBody`, `isEmailFile` | #### Key Functions **Data Utilities** (`data-utils.ts`) ```typescript // Convert ArrayBuffer to base64 string function arrayBufferToBase64(buffer: unknown): string; // Create data URL for file downloads function createDataUrl(contentType: string, base64Data: string): string; // Check if string looks like base64 encoded content function looksLikeBase64(content: string): boolean; // Attempt to decode base64, returns null on failure function tryDecodeBase64(content: string): string | null; ``` **Attachment Handlers** (`attachment-handlers.ts`) ```typescript // Open PDF using built-in or external viewer based on settings async function openPDFAttachment(base64Data: string, filename: string): Promise; // Open image using built-in or external viewer based on settings async function openImageAttachment(base64Data: string, filename: string): Promise; // Open EML attachment in new EMLy window async function openEMLAttachment(base64Data: string, filename: string): Promise; ``` **Email Loader** (`email-loader.ts`) ```typescript // Load email from file path, handles EML/MSG/PEC detection async function loadEmailFromPath(filePath: string): Promise; // Open file dialog and load selected email async function openAndLoadEmail(): Promise; // Process email body (decode base64, fix encoding) async function processEmailBody(body: string): Promise; // Check if file path is a valid email file function isEmailFile(filePath: string): boolean; ``` ### Settings Page (`(app)/settings/+page.svelte`) Organized into cards with various configuration options: 1. **Language Settings** - Radio buttons for English/Italian - Triggers full page reload on change 2. **Export/Import Settings** - Export current settings to JSON file - Import settings from JSON file 3. **Preview Page Settings** - Supported image types (JPG, JPEG, PNG) - Toggle built-in image viewer - Toggle built-in PDF viewer 4. **Danger Zone** (Hidden by default, revealed by clicking settings 10 times rapidly) - Open DevTools hint - Reload application - Reset to defaults - Debugger protection toggle (disabled in production) - Version information display #### Unsaved Changes Detection The settings page tracks changes and shows a persistent toast when there are unsaved modifications: ```typescript $effect(() => { const dirty = !isSameSettings(normalizeSettings(form), lastSaved); unsavedChanges.set(dirty); if (dirty) { showUnsavedChangesToast({ onSave: saveToStorage, onReset: resetToLastSaved, }); } }); ``` --- ## State Management EMLy uses a combination of Svelte 5 Runes and traditional Svelte stores. ### Mail State (`stores/mail-state.svelte.ts`) Uses Svelte 5's `$state` rune for reactive email data: ```typescript class MailState { currentEmail = $state(null); setParams(email: internal.EmailData | null) { this.currentEmail = email; } clear() { this.currentEmail = null; } } export const mailState = new MailState(); ``` ### Settings Store (`stores/settings.svelte.ts`) Manages application settings with localStorage persistence: ```typescript class SettingsStore { settings = $state({ ...defaults }); hasHydrated = $state(false); load() { /* Load from localStorage */ } save() { /* Save to localStorage */ } update(newSettings: Partial) { /* Merge and save */ } reset() { /* Reset to defaults */ } } ``` Settings schema: ```typescript interface EMLy_GUI_Settings { selectedLanguage: "en" | "it"; useBuiltinPreview: boolean; useBuiltinPDFViewer: boolean; previewFileSupportedTypes: string[]; enableAttachedDebuggerProtection: boolean; } ``` ### App Store (`stores/app.ts`) Traditional Svelte writable stores for UI state: ```typescript export const dangerZoneEnabled = writable(false); export const unsavedChanges = writable(false); export const sidebarOpen = writable(true); export const bugReportDialogOpen = writable(false); export const events = writable([]); ``` --- ## Internationalization (i18n) EMLy uses **ParaglideJS** for compile-time type-safe translations. ### Translation Files Located in `frontend/messages/`: - `en.json` - English translations - `it.json` - Italian translations ### Message Format ```json { "$schema": "https://inlang.com/schema/inlang-message-format", "mail_no_email_selected": "No email selected", "mail_open_eml_btn": "Open EML/MSG File", "settings_title": "Settings", "settings_language_english": "English", "settings_language_italian": "Italiano" } ``` ### Usage in Components ```typescript import * as m from "$lib/paraglide/messages"; // In template

{m.settings_title()}

``` ### Changing Language ```typescript import { setLocale } from "$lib/paraglide/runtime"; await setLocale("it", { reload: false }); location.reload(); // Page reload required for full update ``` --- ## UI Components EMLy uses **shadcn-svelte**, a port of shadcn/ui for Svelte. Components are located in `frontend/src/lib/components/ui/`. ### Available Components | Component | Usage | |-----------|-------| | `Button` | Primary buttons with variants (default, destructive, outline, ghost) | | `Card` | Container with header, content, footer sections | | `Dialog` | Modal dialogs for bug reports, confirmations | | `AlertDialog` | Confirmation dialogs with cancel/continue actions | | `Switch` | Toggle switches for boolean settings | | `Checkbox` | Multi-select checkboxes | | `RadioGroup` | Single-select options (language selection) | | `Label` | Form labels | | `Input` | Text input fields | | `Textarea` | Multi-line text input | | `Separator` | Visual dividers | | `Sidebar` | Collapsible navigation sidebar | | `Tooltip` | Hover tooltips | | `Sonner` | Toast notifications | | `Badge` | Status badges | | `Skeleton` | Loading placeholders | ### Custom Components | Component | Purpose | |-----------|---------| | `MailViewer.svelte` | Email display with header, attachments, body | | `SidebarApp.svelte` | Navigation sidebar with menu items | | `UnsavedBar.svelte` | Unsaved changes notification bar | --- ## Key Features ### 1. Email Viewing - Parse and display EML and MSG files - Show email metadata (from, to, cc, bcc, subject) - Render HTML email bodies in sandboxed iframe - List and handle attachments with type-specific actions ### 2. Attachment Handling - **Images**: Open in built-in viewer or external app - **PDFs**: Open in built-in viewer or external app - **EML files**: Open in new EMLy window - **Other files**: Download directly ### 3. Multi-Window Support The app can spawn separate viewer windows: - Image viewer (`--view-image=`) - PDF viewer (`--view-pdf=`) - EML viewer (new app instance with file path) Each viewer has a unique instance ID to allow multiple concurrent viewers. ### 4. Bug Reporting Complete bug reporting system: 1. Captures screenshot when dialog opens 2. Collects user input (name, email, description) 3. Includes current mail file if loaded 4. Gathers system information 5. Creates ZIP archive in temp folder 6. Attempts to upload to the bug report API server (if configured) 7. Falls back to local ZIP if server is unreachable 8. Shows server confirmation with report ID, or local path with upload warning #### Bug Report API Server A separate API server (`server/` directory) receives bug reports: - **Stack**: Bun.js + ElysiaJS + MySQL 8 - **Deployment**: Docker Compose (`docker compose up -d` from `server/`) - **Auth**: Static API key for clients (`X-API-Key`), static admin key (`X-Admin-Key`) - **Rate limiting**: HWID-based, configurable (default 5 reports per 24h) - **Endpoints**: `POST /api/bug-reports` (client), `GET/DELETE /api/admin/bug-reports` (admin) #### Configuration (config.ini) ```ini [EMLy] BUGREPORT_API_URL="https://your-server.example.com" BUGREPORT_API_KEY="your-api-key" ``` ### 5. Settings Management - Language selection (English/Italian) - Built-in viewer preferences - Supported file type configuration - Export/import settings as JSON - Reset to defaults ### 6. Security Features - Debugger detection and protection - Sandboxed iframe for email body - Single-instance lock for main window - Disabled link clicking in email body ### 7. PEC Support Special handling for Italian Posta Elettronica Certificata (PEC): - Detects PEC emails - Shows signed mail badge - Handles P7S signature files - Processes daticert.xml metadata ### 8. Self-Hosted Update System **Corporate Network Update Management** - No third-party services required: - **Network Share Integration**: Check for updates from corporate file shares (UNC paths like `\\server\emly-updates`) - **Version Manifest**: JSON-based version.json controls what versions are available - **Dual Channel Support**: Separate stable and beta release channels - **Manual or Automatic**: Users can manually check, or app auto-checks on startup - **Download & Verify**: Downloads installers from network share with SHA256 checksum verification - **One-Click Install**: Auto-launches installer with UAC elevation, optionally quits app - **UI Integration**: Full update UI in Settings page with progress indicators - **Event-Driven**: Real-time status updates via Wails events #### Configuration (config.ini) ```ini [EMLy] UPDATE_CHECK_ENABLED="true" # Enable/disable update checking UPDATE_PATH="\\server\updates" # Network share or file:// path UPDATE_AUTO_CHECK="true" # Check on startup ``` #### Network Share Structure ``` \\server\emly-updates\ ├── version.json # Update manifest ├── EMLy_Installer_1.5.0.exe # Stable release installer └── EMLy_Installer_1.5.1-beta.exe # Beta release installer ``` #### version.json Format ```json { "stableVersion": "1.5.0", "betaVersion": "1.5.1-beta", "stableDownload": "EMLy_Installer_1.5.0.exe", "betaDownload": "EMLy_Installer_1.5.1-beta.exe", "sha256Checksums": { "EMLy_Installer_1.5.0.exe": "abc123...", "EMLy_Installer_1.5.1-beta.exe": "def456..." }, "releaseNotes": { "1.5.0": "Bug fixes and performance improvements", "1.5.1-beta": "New feature preview" } } ``` #### Update Flow 1. **Check**: App reads `version.json` from configured network path 2. **Compare**: Compares current version with available version for active channel (stable/beta) 3. **Notify**: If update available, shows toast notification with action button 4. **Download**: User clicks download, installer copied from network share to temp folder 5. **Verify**: SHA256 checksum validated against manifest 6. **Install**: User clicks install, app launches installer with UAC, optionally quits #### Backend Methods (app_update.go) | Method | Description | |--------|-------------| | `CheckForUpdates()` | Reads manifest from network share, compares versions | | `DownloadUpdate()` | Copies installer to temp folder, verifies checksum | | `InstallUpdate(quit)` | Launches installer with UAC elevation | | `GetUpdateStatus()` | Returns current update system state | | `loadUpdateManifest(path)` | Parses version.json from network share | | `compareSemanticVersions(v1, v2)` | Semantic version comparison | | `verifyChecksum(file, hash)` | SHA256 integrity verification | | `resolveUpdatePath(base, file)` | Handles UNC paths and file:// URLs | #### Deployment Workflow for IT Admins 1. **Build new version**: `wails build --upx` 2. **Create installer**: Run Inno Setup with `installer/installer.iss` 3. **Generate checksum**: `certutil -hashfile EMLy_Installer_1.5.0.exe SHA256` 4. **Update manifest**: Edit `version.json` with new version and checksum 5. **Deploy to share**: Copy installer and manifest to `\\server\emly-updates\` 6. **Users notified**: Apps auto-check within 5 seconds of startup (if enabled) --- ## Build & Development ### Prerequisites - Go 1.21+ - Node.js 18+ or Bun - Wails CLI v2 ### Development ```bash # Install frontend dependencies cd frontend && bun install # Run in development mode wails dev ``` ### Building ```bash # Build for Windows wails build -platform windows/amd64 # Output: build/bin/EMLy.exe ``` ### Configuration `wails.json` configures the build: ```json { "name": "EMLy", "frontend:install": "bun install", "frontend:build": "bun run build", "frontend:dev:watcher": "bun run dev", "fileAssociations": [ { "ext": "eml", "name": "Email Message", "description": "EML File" } ] } ``` ### File Association The app registers as a handler for `.eml` files via Windows file associations, configured in `wails.json`. --- ## Wails Bindings Wails automatically generates TypeScript bindings for Go functions. These are located in `frontend/src/lib/wailsjs/`. ### Generated Files - `go/main/App.ts` - TypeScript functions calling Go methods - `go/models.ts` - TypeScript types for Go structs - `runtime/runtime.ts` - Wails runtime functions ### Usage Example ```typescript import { ReadEML, ShowOpenFileDialog } from "$lib/wailsjs/go/main/App"; import type { internal } from "$lib/wailsjs/go/models"; // Open file dialog const filePath = await ShowOpenFileDialog(); // Parse email const email: internal.EmailData = await ReadEML(filePath); ``` ### Runtime Events Wails provides event system for Go-to-JS communication: ```typescript import { EventsOn } from "$lib/wailsjs/runtime/runtime"; // Listen for second instance launch EventsOn("launchArgs", (args: string[]) => { // Handle file opened from second instance }); ``` --- ## Error Handling ### Frontend Toast notifications for user-facing errors: ```typescript import { toast } from "svelte-sonner"; import * as m from "$lib/paraglide/messages"; try { await ReadEML(filePath); } catch (error) { toast.error(m.mail_error_opening()); } ``` ### Backend Errors are returned to frontend and logged: ```go func (a *App) ReadEML(filePath string) (*internal.EmailData, error) { data, err := internal.ReadEmlFile(filePath) if err != nil { Log("Failed to read EML:", err) return nil, err } return data, nil } ``` --- ## Debugging ### Development Mode In dev mode (`wails dev`): - Hot reload enabled - Debug logs visible - DevTools accessible via Ctrl+Shift+F12 - Danger Zone always visible in settings ### Production - Debugger protection can terminate app if debugger detected - Danger Zone hidden by default - Access Danger Zone by clicking settings link 10 times within 4 seconds --- ## License & Credits EMLy is developed by FOISX @ 3gIT.