From d30597a921bd0275072485f75ea4bcdd1209e1be Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Tue, 12 Aug 2025 03:52:46 +0000 Subject: [PATCH 1/8] feat(logging): implement configurable log levels via VITE_LOG_LEVEL Add comprehensive logging configuration system with environment variable support. Environment files now include appropriate log levels per build mode: - Development: debug (maximum visibility) - Production: warn (minimal noise) - Testing: info (balanced output) Includes smart default behavior based on platform and environment, enhanced logger methods for level checking, and comprehensive documentation. All existing logging calls remain backward compatible. Closes logging configuration request --- .env.development | 8 +- .env.production | 3 +- .env.test | 7 +- README.md | 29 +++++++ doc/logging-configuration.md | 117 +++++++++++++++++++++++++++ src/components/DataExportSection.vue | 7 +- src/components/OfferDialog.vue | 6 +- src/utils/logger.ts | 80 +++++++++++++----- 8 files changed, 223 insertions(+), 34 deletions(-) create mode 100644 doc/logging-configuration.md diff --git a/.env.development b/.env.development index 70091e74..44a2ffdf 100644 --- a/.env.development +++ b/.env.development @@ -1,10 +1,14 @@ - # Only the variables that start with VITE_ are seen in the application import.meta.env in Vue. +# Logging Configuration - Development environment gets maximum visibility +VITE_LOG_LEVEL=debug + # iOS doesn't like spaces in the app title. TIME_SAFARI_APP_TITLE="TimeSafari_Dev" VITE_APP_SERVER=http://localhost:8080 -# This is the claim ID for actions in the BVC project, with the JWT ID on this environment (not production). +# This is the claim ID for actions in the BVC project, with the JWT ID on this environment (not + production). + VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZYYPS272F VITE_DEFAULT_ENDORSER_API_SERVER=http://localhost:3000 # Using shared server by default to ease setup, which works for shared test users. diff --git a/.env.production b/.env.production index bbf73a08..aef4d6a4 100644 --- a/.env.production +++ b/.env.production @@ -1,6 +1,7 @@ # Only the variables that start with VITE_ are seen in the application import.meta.env in Vue. - +# Logging Configuration - Production environment gets minimal logging for performance +VITE_LOG_LEVEL=warn VITE_APP_SERVER=https://timesafari.app # This is the claim ID for actions in the BVC project. diff --git a/.env.test b/.env.test index a01c323c..5776e66c 100644 --- a/.env.test +++ b/.env.test @@ -1,9 +1,14 @@ # Only the variables that start with VITE_ are seen in the application import.meta.env in Vue. +# Logging Configuration - Test environment gets balanced logging for debugging +VITE_LOG_LEVEL=info + # iOS doesn't like spaces in the app title. TIME_SAFARI_APP_TITLE="TimeSafari_Test" VITE_APP_SERVER=https://test.timesafari.app -# This is the claim ID for actions in the BVC project, with the JWT ID on this environment (not production). +# This is the claim ID for actions in the BVC project, with the JWT ID on this environment (not + production). + VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZYYPS272F VITE_DEFAULT_ENDORSER_API_SERVER=https://test-api.endorser.ch diff --git a/README.md b/README.md index 6d987467..ba8bf89b 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,35 @@ See [BUILDING.md](BUILDING.md) for comprehensive build instructions for all plat TimeSafari provides a simple script-based approach to clear the database for development purposes. +## Logging Configuration + +TimeSafari supports configurable logging levels via the `VITE_LOG_LEVEL` environment variable. This allows developers to control console output verbosity without modifying code. + +### Quick Usage + +```bash +# Show only errors +VITE_LOG_LEVEL=error npm run dev + +# Show warnings and errors +VITE_LOG_LEVEL=warn npm run dev + +# Show info, warnings, and errors (default) +VITE_LOG_LEVEL=info npm run dev + +# Show all log levels including debug +VITE_LOG_LEVEL=debug npm run dev +``` + +### Available Levels + +- **`error`**: Critical errors only +- **`warn`**: Warnings and errors (default for production web) +- **`info`**: Info, warnings, and errors (default for development/capacitor) +- **`debug`**: All log levels including verbose debugging + +See [Logging Configuration Guide](doc/logging-configuration.md) for complete details. + ### Quick Usage ```bash # Run the database clearing script diff --git a/doc/logging-configuration.md b/doc/logging-configuration.md new file mode 100644 index 00000000..2ef2a6d2 --- /dev/null +++ b/doc/logging-configuration.md @@ -0,0 +1,117 @@ +# Logging Configuration Guide + +## Overview + +TimeSafari now supports configurable logging levels via the `VITE_LOG_LEVEL` environment variable. This allows developers to control the verbosity of console output without modifying code. + +## Available Log Levels + +| Level | Value | Description | Console Output | +|-------|-------|-------------|----------------| +| `error` | 0 | Errors only | Critical errors only | +| `warn` | 1 | Warnings and errors | Warnings and errors | +| `info` | 2 | Info, warnings, and errors | General information, warnings, and errors | +| `debug` | 3 | All log levels | Verbose debugging information | + +## Environment Variable + +Set the `VITE_LOG_LEVEL` environment variable to control logging: + +```bash +# Show only errors +VITE_LOG_LEVEL=error + +# Show warnings and errors (default for production web) +VITE_LOG_LEVEL=warn + +# Show info, warnings, and errors (default for development/capacitor) +VITE_LOG_LEVEL=info + +# Show all log levels including debug +VITE_LOG_LEVEL=debug +``` + +## Default Behavior by Platform + +The logger automatically selects appropriate default log levels based on your platform and environment: + +- **Production Web**: `warn` (warnings and errors only) +- **Electron**: `error` (errors only - very quiet) +- **Development/Capacitor**: `info` (info and above) + +## Usage Examples + +### Setting Log Level in Development + +```bash +# In your terminal before running the app +export VITE_LOG_LEVEL=debug +npm run dev + +# Or inline +VITE_LOG_LEVEL=debug npm run dev +``` + +### Setting Log Level in Production + +```bash +# For verbose production logging +VITE_LOG_LEVEL=info npm run build:web + +# For minimal production logging +VITE_LOG_LEVEL=warn npm run build:web +``` + +### Programmatic Access + +The logger provides methods to check current configuration: + +```typescript +import { logger } from '@/utils/logger'; + +// Get current log level +const currentLevel = logger.getCurrentLevel(); // 'info' + +// Check if a level is enabled +const debugEnabled = logger.isLevelEnabled('debug'); // false if level < debug + +// Get available levels +const levels = logger.getAvailableLevels(); // ['error', 'warn', 'info', 'debug'] +``` + +## Database Logging + +Database logging continues to work regardless of console log level settings. All log messages are still stored in the database for debugging and audit purposes. + +## Migration Notes + +- **Existing code**: No changes required - logging behavior remains the same +- **New feature**: Use `VITE_LOG_LEVEL` to override default behavior +- **Backward compatible**: All existing logging calls work as before + +## Best Practices + +1. **Development**: Use `VITE_LOG_LEVEL=debug` for maximum visibility +2. **Testing**: Use `VITE_LOG_LEVEL=info` for balanced output +3. **Production**: Use `VITE_LOG_LEVEL=warn` for minimal noise +4. **Debugging**: Temporarily set `VITE_LOG_LEVEL=debug` to troubleshoot issues + +## Troubleshooting + +### No Logs Appearing + +Check your `VITE_LOG_LEVEL` setting: +```bash +echo $VITE_LOG_LEVEL +``` + +### Too Many Logs + +Reduce verbosity by setting a lower log level: +```bash +VITE_LOG_LEVEL=warn +``` + +### Platform-Specific Issues + +Remember that Electron is very quiet by default. Use `VITE_LOG_LEVEL=info` to see more output in Electron builds. diff --git a/src/components/DataExportSection.vue b/src/components/DataExportSection.vue index ce8ee2fc..a21be7b2 100644 --- a/src/components/DataExportSection.vue +++ b/src/components/DataExportSection.vue @@ -187,9 +187,10 @@ export default class DataExportSection extends Vue { const exContact: Contact = R.omit(["contactMethods"], contact); // now add contactMethods as a true array of ContactMethod objects exContact.contactMethods = contact.contactMethods - ? (typeof contact.contactMethods === 'string' && contact.contactMethods.trim() !== '' - ? JSON.parse(contact.contactMethods) - : []) + ? typeof contact.contactMethods === "string" && + contact.contactMethods.trim() !== "" + ? JSON.parse(contact.contactMethods) + : [] : []; return exContact; }); diff --git a/src/components/OfferDialog.vue b/src/components/OfferDialog.vue index b8b8093f..81664088 100644 --- a/src/components/OfferDialog.vue +++ b/src/components/OfferDialog.vue @@ -18,7 +18,7 @@ Raymer */
@@ -152,8 +152,6 @@ export default class OfferDialog extends Vue { }; } - - // ================================================= // COMPONENT METHODS // ================================================= @@ -199,8 +197,6 @@ export default class OfferDialog extends Vue { this.visible = false; } - - /** * Handle amount updates from AmountInput component * @param value - New amount value diff --git a/src/utils/logger.ts b/src/utils/logger.ts index 4b78ed3a..52ae5daa 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -2,9 +2,10 @@ * Enhanced logger with self-contained database logging * * Provides comprehensive logging with console and database output. + * Supports configurable log levels via VITE_LOG_LEVEL environment variable. * * @author Matthew Raymer - * @version 2.0.0 + * @version 2.1.0 * @since 2025-01-25 */ @@ -46,6 +47,42 @@ export function safeStringify(obj: unknown) { const isElectron = process.env.VITE_PLATFORM === "electron"; const isProduction = process.env.NODE_ENV === "production"; +// Log level configuration via environment variable +const LOG_LEVELS = { + error: 0, + warn: 1, + info: 2, + debug: 3, +} as const; + +type LogLevel = keyof typeof LOG_LEVELS; + +// Parse VITE_LOG_LEVEL environment variable +const getLogLevel = (): LogLevel => { + const envLogLevel = process.env.VITE_LOG_LEVEL?.toLowerCase(); + + if (envLogLevel && envLogLevel in LOG_LEVELS) { + return envLogLevel as LogLevel; + } + + // Default log levels based on environment + if (isProduction && !isElectron) { + return "warn"; // Production web: warnings and errors only + } else if (isElectron) { + return "error"; // Electron: errors only + } else { + return "info"; // Development/Capacitor: info and above + } +}; + +const currentLogLevel = getLogLevel(); +const currentLevelValue = LOG_LEVELS[currentLogLevel]; + +// Helper function to check if a log level should be displayed +const shouldLog = (level: LogLevel): boolean => { + return LOG_LEVELS[level] <= currentLevelValue; +}; + // Track initialization state to prevent circular dependencies let isInitializing = true; @@ -105,11 +142,11 @@ async function logToDatabase( } } -// Enhanced logger with self-contained database methods +// Enhanced logger with self-contained database methods and log level control export const logger = { debug: (message: string, ...args: unknown[]) => { - // Debug logs are very verbose - only show in development mode for web - if (!isProduction && !isElectron) { + // Debug logs only show if VITE_LOG_LEVEL allows it + if (shouldLog("debug")) { // eslint-disable-next-line no-console console.debug(message, ...args); } @@ -117,11 +154,8 @@ export const logger = { }, log: (message: string, ...args: unknown[]) => { - // Regular logs - show in development or for capacitor, but quiet for Electron - if ( - (!isProduction && !isElectron) || - process.env.VITE_PLATFORM === "capacitor" - ) { + // Regular logs - show if VITE_LOG_LEVEL allows info level + if (shouldLog("info")) { // eslint-disable-next-line no-console console.log(message, ...args); } @@ -132,11 +166,7 @@ export const logger = { }, info: (message: string, ...args: unknown[]) => { - if ( - process.env.NODE_ENV !== "production" || - process.env.VITE_PLATFORM === "capacitor" || - process.env.VITE_PLATFORM === "electron" - ) { + if (shouldLog("info")) { // eslint-disable-next-line no-console console.info(message, ...args); } @@ -147,8 +177,7 @@ export const logger = { }, warn: (message: string, ...args: unknown[]) => { - // Always show warnings, but for Electron, suppress routine database warnings - if (!isElectron || !message.includes("[CapacitorPlatformService]")) { + if (shouldLog("warn")) { // eslint-disable-next-line no-console console.warn(message, ...args); } @@ -159,9 +188,10 @@ export const logger = { }, error: (message: string, ...args: unknown[]) => { - // Errors will always be logged to console - // eslint-disable-next-line no-console - console.error(message, ...args); + if (shouldLog("error")) { + // eslint-disable-next-line no-console + console.error(message, ...args); + } // Database logging const messageString = safeStringify(message); @@ -175,11 +205,11 @@ export const logger = { }, toConsoleAndDb: async (message: string, isError = false): Promise => { - // Console output - if (isError) { + // Console output based on log level + if (isError && shouldLog("error")) { // eslint-disable-next-line no-console console.error(message); - } else { + } else if (!isError && shouldLog("info")) { // eslint-disable-next-line no-console console.log(message); } @@ -194,6 +224,12 @@ export const logger = { error: (message: string) => logToDatabase(`[${componentName}] ${message}`, "error"), }), + + // Log level information methods + getCurrentLevel: (): LogLevel => currentLogLevel, + getCurrentLevelValue: (): number => currentLevelValue, + isLevelEnabled: (level: LogLevel): boolean => shouldLog(level), + getAvailableLevels: (): LogLevel[] => Object.keys(LOG_LEVELS) as LogLevel[], }; // Function to manually mark initialization as complete From 7df52312bac3cc31f5ba591caef9fa8e57724069 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Tue, 12 Aug 2025 06:26:28 +0000 Subject: [PATCH 2/8] fix(build): resolve shell script export error in .env.development Fixed malformed comment in .env.development that was causing "export: production).=: not a valid identifier" error in build scripts. The comment was split across lines, causing the shell to interpret " production)." as a variable assignment. - Removed malformed comment line that was breaking build:web script - Build script now successfully validates environment and starts server - Resolves issue preventing Playwright tests from running properly --- .env.development | 2 +- package-lock.json | 2 +- package.json | 2 +- scripts/build-web.sh | 14 ++++++++------ 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.env.development b/.env.development index 44a2ffdf..726f3b7a 100644 --- a/.env.development +++ b/.env.development @@ -7,7 +7,7 @@ VITE_LOG_LEVEL=debug TIME_SAFARI_APP_TITLE="TimeSafari_Dev" VITE_APP_SERVER=http://localhost:8080 # This is the claim ID for actions in the BVC project, with the JWT ID on this environment (not - production). + VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZYYPS272F VITE_DEFAULT_ENDORSER_API_SERVER=http://localhost:3000 diff --git a/package-lock.json b/package-lock.json index 6d19bd08..3b85f9c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -96,7 +96,7 @@ }, "devDependencies": { "@capacitor/assets": "^3.0.5", - "@playwright/test": "^1.45.2", + "@playwright/test": "^1.54.2", "@types/dom-webcodecs": "^0.1.7", "@types/jest": "^30.0.0", "@types/js-yaml": "^4.0.9", diff --git a/package.json b/package.json index cd34bc17..f4fc0ea4 100644 --- a/package.json +++ b/package.json @@ -204,7 +204,7 @@ }, "devDependencies": { "@capacitor/assets": "^3.0.5", - "@playwright/test": "^1.45.2", + "@playwright/test": "^1.54.2", "@types/dom-webcodecs": "^0.1.7", "@types/jest": "^30.0.0", "@types/js-yaml": "^4.0.9", diff --git a/scripts/build-web.sh b/scripts/build-web.sh index c01bd55d..de0710a2 100755 --- a/scripts/build-web.sh +++ b/scripts/build-web.sh @@ -300,18 +300,20 @@ serve_build() { exit 5 fi - # Use a simple HTTP server to serve the build - if command -v python3 &> /dev/null; then + # Use a server that supports SPA routing (serves index.html for all routes) + if command -v npx &> /dev/null; then + log_info "Starting npx serve with SPA support on port 8080..." + npx serve -s dist -l 8080 + elif command -v python3 &> /dev/null; then + log_warn "Python HTTP server doesn't support SPA routing. Routes like /discover, /account will return 404." log_info "Starting Python HTTP server on port 8080..." cd dist && python3 -m http.server 8080 elif command -v python &> /dev/null; then + log_warn "Python HTTP server doesn't support SPA routing. Routes like /discover, /account will return 404." log_info "Starting Python HTTP server on port 8080..." cd dist && python -m SimpleHTTPServer 8080 - elif command -v npx &> /dev/null; then - log_info "Starting npx serve on port 8080..." - npx serve -s dist -l 8080 else - log_error "No suitable HTTP server found. Install Python or npx serve." + log_error "No suitable HTTP server found. Install npx serve or Python." exit 5 fi } From bb357f294aeb3ba2a931fd99cbc64b4d87e205a3 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Tue, 12 Aug 2025 08:45:08 +0000 Subject: [PATCH 3/8] refactor(ui): remove unused image cache system and fix TypeScript compilation - Remove imageCache Map that only stored null values - Remove selectedImageData Blob property (never used) - Remove cacheImageData method and related function props - Remove handleImageLoad method from ActivityListItem - Clean up ImageViewer component props - Fix TypeScript compilation by replacing legacy logConsoleAndDb calls - Replace logConsoleAndDb with logger.error in deepLinks service - Images continue to work via direct URL references The image cache system was non-functional and only stored null values. ImageViewer component ignored blob data and only used URLs. Fixed production build failure caused by undefined logConsoleAndDb function. Removing dead code improves maintainability without affecting functionality. --- src/components/ActivityListItem.vue | 16 ---------------- src/components/ImageViewer.vue | 1 - src/services/deepLinks.ts | 10 ++++------ src/views/HomeView.vue | 27 +-------------------------- 4 files changed, 5 insertions(+), 49 deletions(-) diff --git a/src/components/ActivityListItem.vue b/src/components/ActivityListItem.vue index d1525e8d..257259b9 100644 --- a/src/components/ActivityListItem.vue +++ b/src/components/ActivityListItem.vue @@ -73,7 +73,6 @@ class="w-full h-auto max-w-lg max-h-96 object-contain mx-auto drop-shadow-md" :src="record.image" alt="Activity image" - @load="handleImageLoad(record.image)" />
@@ -272,13 +271,6 @@ export default class ActivityListItem extends Vue { @Prop() isRegistered!: boolean; @Prop() activeDid!: string; - /** - * Function prop for handling image caching - * Called when an image loads successfully, allowing parent to control caching behavior - */ - @Prop({ type: Function, default: () => {} }) - onImageCache!: (imageUrl: string) => void | Promise; - isHiddenDid = isHiddenDid; notify!: ReturnType; $notify!: (notification: any, timeout?: number) => void; @@ -295,14 +287,6 @@ export default class ActivityListItem extends Vue { this.notify.warning(NOTIFY_UNKNOWN_PERSON.message, TIMEOUTS.STANDARD); } - /** - * Handle image load event - call function prop for caching - * Allows parent to control caching behavior and validation - */ - handleImageLoad(imageUrl: string): void { - this.onImageCache(imageUrl); - } - get fetchAmount(): string { const claim = (this.record.fullClaim as any)?.claim || this.record.fullClaim; diff --git a/src/components/ImageViewer.vue b/src/components/ImageViewer.vue index 07486705..3e8e577f 100644 --- a/src/components/ImageViewer.vue +++ b/src/components/ImageViewer.vue @@ -45,7 +45,6 @@ import { logger } from "../utils/logger"; @Component({ emits: ["update:isOpen"] }) export default class ImageViewer extends Vue { @Prop() imageUrl!: string; - @Prop() imageData!: Blob | null; @Prop() isOpen!: boolean; userAgent = new UAParser(); diff --git a/src/services/deepLinks.ts b/src/services/deepLinks.ts index ad97ef46..62ae7b15 100644 --- a/src/services/deepLinks.ts +++ b/src/services/deepLinks.ts @@ -53,6 +53,7 @@ import { DeepLinkRoute, } from "../interfaces/deepLinks"; import type { DeepLinkError } from "../interfaces/deepLinks"; +import { logger } from "../utils/logger"; // Helper function to extract the first key from a Zod object schema function getFirstKeyFromZodObject( @@ -204,9 +205,8 @@ export class DeepLinkHandler { validatedParams = await schema.parseAsync(params); } catch (error) { // For parameter validation errors, provide specific error feedback - logConsoleAndDb( + logger.error( `[DeepLink] Invalid parameters for route name ${routeName} for path: ${path}: ${JSON.stringify(error)} ... with params: ${JSON.stringify(params)} ... and query: ${JSON.stringify(query)}`, - true, ); await this.router.replace({ name: "deep-link-error", @@ -229,9 +229,8 @@ export class DeepLinkHandler { params: validatedParams, }); } catch (error) { - logConsoleAndDb( + logger.error( `[DeepLink] Error routing to route name ${routeName} for path: ${path}: ${JSON.stringify(error)} ... with validated params: ${JSON.stringify(validatedParams)}`, - true, ); // For parameter validation errors, provide specific error feedback await this.router.replace({ @@ -263,9 +262,8 @@ export class DeepLinkHandler { await this.validateAndRoute(path, sanitizedParams, query); } catch (error) { const deepLinkError = error as DeepLinkError; - logConsoleAndDb( + logger.error( `[DeepLink] Error (${deepLinkError.code}): ${deepLinkError.details}`, - true, ); throw { diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index 25bcac3e..4af9b5bc 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -234,7 +234,6 @@ Raymer * @version 1.0.0 */ :last-viewed-claim-id="feedLastViewedClaimId" :is-registered="isRegistered" :active-did="activeDid" - :on-image-cache="cacheImageData" @load-claim="onClickLoadClaim" @view-image="openImageViewer" /> @@ -255,11 +254,7 @@ Raymer * @version 1.0.0 */ - + diff --git a/src/services/deepLinks.ts b/src/services/deepLinks.ts index 62ae7b15..d8445607 100644 --- a/src/services/deepLinks.ts +++ b/src/services/deepLinks.ts @@ -179,7 +179,7 @@ export class DeepLinkHandler { const validRoute = routeSchema.parse(path) as DeepLinkRoute; routeName = ROUTE_MAP[validRoute].name; } catch (error) { - console.error(`[DeepLink] Invalid route path: ${path}`); + logger.error(`[DeepLink] Invalid route path: ${path}`); // Redirect to error page with information about the invalid link await this.router.replace({ diff --git a/src/services/platforms/WebPlatformService.ts b/src/services/platforms/WebPlatformService.ts index 7ec13c01..a731ee22 100644 --- a/src/services/platforms/WebPlatformService.ts +++ b/src/services/platforms/WebPlatformService.ts @@ -693,7 +693,8 @@ export class WebPlatformService implements PlatformService { const setClause = keys.map((key) => `${key} = ?`).join(", "); const sql = `UPDATE settings SET ${setClause} WHERE accountDid = ?`; const params = [...keys.map((key) => settings[key]), did]; - console.log( + // Log update operation for debugging + logger.debug( "[WebPlatformService] updateDidSpecificSettings", sql, JSON.stringify(params, null, 2), diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue index 892e1ebc..81c2978e 100644 --- a/src/views/AccountViewView.vue +++ b/src/views/AccountViewView.vue @@ -61,7 +61,8 @@ /> - +

- + Date: Wed, 13 Aug 2025 09:05:04 -0600 Subject: [PATCH 5/8] doc: Add helpful setup instructions for quick start for new devs. --- BUILDING.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/BUILDING.md b/BUILDING.md index ba5649da..e1e94fcd 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -8,8 +8,10 @@ This guide explains how to build TimeSafari for different platforms using the co ```bash # 🖥️ Web Development -npm run build:web:dev # Start development server with hot reload -npm run build:web:prod # Production build +npm install # setup -- and pkgx.dev `dev` command before this will set environment with npm, etc +npm run build:web:serve -- --test # Start with test endorser server +npm run build:web:dev # Start development server with hot reload with local endorser server +npm run build:web:prod # Production build # 📱 Mobile Development npm run build:ios # iOS build (opens Xcode) @@ -2401,4 +2403,4 @@ All scripts use consistent error handling: --- -**Note**: This documentation is maintained alongside the build system. For the most up-to-date information, refer to the actual script files and Vite configuration files in the repository. \ No newline at end of file +**Note**: This documentation is maintained alongside the build system. For the most up-to-date information, refer to the actual script files and Vite configuration files in the repository. From 24a2dce43e32ef41d849d084acb62551117eaa24 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Wed, 13 Aug 2025 11:17:08 -0600 Subject: [PATCH 6/8] chore: Bump version and add "-beta". --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6d19bd08..5b40d345 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "timesafari", - "version": "1.0.6", + "version": "1.0.7-beta", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "timesafari", - "version": "1.0.6", + "version": "1.0.7-beta", "dependencies": { "@capacitor-community/electron": "^5.0.1", "@capacitor-community/sqlite": "6.0.2", diff --git a/package.json b/package.json index cd34bc17..53e3f5b0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "timesafari", - "version": "1.0.6", + "version": "1.0.7-beta", "description": "Time Safari Application", "author": { "name": "Time Safari Team" From a221a5c5ed4a385c13bd9db68f207f2472a29166 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Wed, 13 Aug 2025 12:18:06 -0600 Subject: [PATCH 7/8] feat: Add easier way for test users to register themselves. --- src/libs/endorserServer.ts | 10 +++--- src/router/index.ts | 18 +++++----- src/test/index.ts | 32 ++++++++++++++---- src/views/TestView.vue | 67 ++++++++++++++++++++++++++++++-------- 4 files changed, 92 insertions(+), 35 deletions(-) diff --git a/src/libs/endorserServer.ts b/src/libs/endorserServer.ts index e28b3ac9..735252f7 100644 --- a/src/libs/endorserServer.ts +++ b/src/libs/endorserServer.ts @@ -1348,12 +1348,12 @@ export async function createEndorserJwtVcFromClaim( } /** - * Create a JWT for a RegisterAction claim. + * Create a JWT for a RegisterAction claim, used for registrations & invites. * * @param activeDid - The DID of the user creating the invite - * @param contact - The contact to register, with a 'did' field (all optional for invites) - * @param identifier - The identifier for the invite, usually random - * @param expiresIn - The number of seconds until the invite expires + * @param contact - Optional - The contact to register, with a 'did' field (all optional for invites) + * @param identifier - Optional - The identifier for the invite, usually random + * @param expiresIn - Optional - The number of seconds until the invite expires * @returns The JWT for the RegisterAction claim */ export async function createInviteJwt( @@ -1367,7 +1367,7 @@ export async function createInviteJwt( "@type": "RegisterAction", agent: { identifier: activeDid }, object: SERVICE_ID, - identifier: identifier, + identifier: identifier, // not sent if undefined }; if (contact?.did) { vcClaim.participant = { identifier: contact.did }; diff --git a/src/router/index.ts b/src/router/index.ts index 7d1e738c..043d3d0c 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -82,6 +82,15 @@ const routes: Array = [ name: "database-migration", component: () => import("../views/DatabaseMigration.vue"), }, + { + path: "/deep-link-error", + name: "deep-link-error", + component: () => import("../views/DeepLinkErrorView.vue"), + meta: { + title: "Invalid Deep Link", + requiresAuth: false, + }, + }, { path: "/did/:did?", name: "did", @@ -276,15 +285,6 @@ const routes: Array = [ name: "user-profile", component: () => import("../views/UserProfileView.vue"), }, - { - path: "/deep-link-error", - name: "deep-link-error", - component: () => import("../views/DeepLinkErrorView.vue"), - meta: { - title: "Invalid Deep Link", - requiresAuth: false, - }, - }, ]; const isElectron = window.location.protocol === "file:"; diff --git a/src/test/index.ts b/src/test/index.ts index 28b0e349..8974713b 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -1,9 +1,29 @@ import axios from "axios"; import * as didJwt from "did-jwt"; import { SERVICE_ID } from "../libs/endorserServer"; -import { deriveAddress, newIdentifier } from "../libs/crypto"; +import { + DEFAULT_ROOT_DERIVATION_PATH, + deriveAddress, + newIdentifier, +} from "../libs/crypto"; import { logger } from "../utils/logger"; import { AppString } from "../constants/app"; +import { saveNewIdentity } from "@/libs/util"; +import { PlatformServiceFactory } from "@/services/PlatformServiceFactory"; + +const TEST_USER_0_MNEMONIC = + "rigid shrug mobile smart veteran half all pond toilet brave review universe ship congress found yard skate elite apology jar uniform subway slender luggage"; + +export async function testBecomeUser0() { + const [addr, privateHex, publicHex, deriPath] = deriveAddress(TEST_USER_0_MNEMONIC); + + const identity0 = newIdentifier(addr, publicHex, privateHex, deriPath); + await saveNewIdentity(identity0, TEST_USER_0_MNEMONIC, DEFAULT_ROOT_DERIVATION_PATH); + const platformService = await PlatformServiceFactory.getInstance(); + await platformService.updateDidSpecificSettings(identity0.did, { + isRegistered: true, + }); +} /** * Get User #0 to sign & submit a RegisterAction for the user's activeDid. @@ -15,10 +35,7 @@ import { AppString } from "../constants/app"; * @throws Error if registration fails or database access fails */ export async function testServerRegisterUser() { - const testUser0Mnem = - "seminar accuse mystery assist delay law thing deal image undo guard initial shallow wrestle list fragile borrow velvet tomorrow awake explain test offer control"; - - const [addr, privateHex, publicHex, deriPath] = deriveAddress(testUser0Mnem); + const [addr, privateHex, publicHex, deriPath] = deriveAddress(TEST_USER_0_MNEMONIC); const identity0 = newIdentifier(addr, publicHex, privateHex, deriPath); @@ -32,9 +49,9 @@ export async function testServerRegisterUser() { const vcClaim = { "@context": "https://schema.org", "@type": "RegisterAction", - agent: { did: identity0.did }, + agent: { identifier: identity0.did }, object: SERVICE_ID, - participant: { did: settings.activeDid }, + participant: { identifier: settings.activeDid }, }; // Make a payload for the claim @@ -71,4 +88,5 @@ export async function testServerRegisterUser() { const resp = await axios.post(url, payload, { headers }); logger.log("User registration result:", resp); + return resp; } diff --git a/src/views/TestView.vue b/src/views/TestView.vue index a7592afa..df879ad2 100644 --- a/src/views/TestView.vue +++ b/src/views/TestView.vue @@ -21,7 +21,17 @@

-
+
+

User Registration

+ + +
+ +

Notiwind Alerts

@@ -99,7 +109,7 @@
Register Passkey -
@@ -235,6 +245,7 @@ import { registerAndSavePasskey, SHARED_PHOTO_BASE64_KEY, } from "../libs/util"; +import { testBecomeUser0, testServerRegisterUser } from "@/test"; import { logger } from "../utils/logger"; import { Account } from "../db/tables/accounts"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; @@ -300,6 +311,7 @@ export default class Help extends Vue { // for passkeys credIdHex?: string; activeDid?: string; + apiServer?: string; jwt?: string; peerSetup?: PeerSetup; userName?: string; @@ -521,17 +533,6 @@ export default class Help extends Vue { ]; } - /** - * Method to trigger notification test - * Centralizes notification testing logic - */ - triggerTestNotification(config: { - notification: NotificationIface; - timeout?: number; - }) { - this.$notify(config.notification, config.timeout); - } - /** * Component initialization * @@ -541,6 +542,7 @@ export default class Help extends Vue { async mounted() { const settings = await this.$accountSettings(); this.activeDid = settings.activeDid || ""; + this.apiServer = settings.apiServer || ""; this.userName = settings.firstName; const account = await retrieveAccountMetadata(this.activeDid); @@ -553,6 +555,43 @@ export default class Help extends Vue { } } + /** + * Checks if running on production server + * + * @returns True if not on production server (enables test utilities) + */ + public isNotProdServer() { + return this.apiServer !== AppString.PROD_ENDORSER_API_SERVER; + } + + async registerMe() { + const response = await testServerRegisterUser(); + if (response.status === 201) { + alert("Registration successful."); + this.$router.push({ name: "home" }); // because this page checks for registered status and sets things if it detects a change + } else { + logger.error("Registration failure response:", response); + alert("Registration failed: " + (response.data.error || response.data)); + } + } + + async becomeUser0() { + await testBecomeUser0(); + alert("You are now User 0."); + this.$router.push({ name: "home" }); // because this page checks for registered status and sets things if it detects a change + } + + /** + * Method to trigger notification test + * Centralizes notification testing logic + */ + triggerTestNotification(config: { + notification: NotificationIface; + timeout?: number; + }) { + this.$notify(config.notification, config.timeout); + } + /** * Handles file upload for image sharing tests * @@ -609,7 +648,7 @@ export default class Help extends Vue { * Includes validation and user confirmation workflow * Uses notification helpers for consistent messaging */ - public async register() { + public async registerPasskey() { const DEFAULT_USERNAME = AppString.APP_NAME + " Tester"; if (!this.userName) { const modalConfig = createPasskeyNameModal( From 81096a8bee0a2be936e8e90fae5e30efb401b73f Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Wed, 13 Aug 2025 18:59:00 -0600 Subject: [PATCH 8/8] doc: Add instructions to become test user, and other README refactors. --- README.md | 36 +++++------------------------------- 1 file changed, 5 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 6d987467..59290d4d 100644 --- a/README.md +++ b/README.md @@ -3,36 +3,9 @@ [Time Safari](https://timesafari.org/) allows people to ease into collaboration: start with expressions of gratitude and expand to crowd-fund with time & money, then record and see the impact of contributions. -## Database Migration Status - -**Current Status**: The application is undergoing a migration from Dexie (IndexedDB) to SQLite using absurd-sql. This migration is in **Phase 2** with a well-defined migration fence in place. - -### Migration Progress -- ✅ **SQLite Database Service**: Fully implemented with absurd-sql -- ✅ **Platform Service Layer**: Unified database interface across platforms -- ✅ **Settings Migration**: Core user settings transferred -- ✅ **Account Migration**: Identity and key management -- 🔄 **Contact Migration**: User contact data (via import interface) -- 📋 **Code Cleanup**: Remove unused Dexie imports - -### Migration Fence -The migration is controlled by a **migration fence** that separates legacy Dexie code from the new SQLite implementation. See [Migration Fence Definition](doc/migration-fence-definition.md) for complete details. - -**Key Points**: -- Legacy Dexie database is disabled by default -- All database operations go through `PlatformServiceMixin` -- Migration tools provide controlled access to both databases -- Clear separation between legacy and new code - -### Migration Documentation -- [Migration Guide](doc/migration-to-wa-sqlite.md) - Complete migration process -- [Migration Fence Definition](doc/migration-fence-definition.md) - Fence boundaries and rules -- [Database Migration Guide](doc/database-migration-guide.md) - User-facing migration tools - ## Roadmap -See [project.task.yaml](project.task.yaml) for current priorities. -(Numbers at the beginning of lines are estimated hours. See [taskyaml.org](https://taskyaml.org/) for details.) +See [ClickUp](https://sharing.clickup.com/9014278710/l/h/8cmnyhp-174/10573fec74e2ba0) for current priorities. ## Setup & Building @@ -42,14 +15,16 @@ Quick start: ```bash npm install -npm run dev +npm run build:web:serve -- --test ``` +To be able to make submissions: go to "profile" (bottom left), go to the bottom and expand "Show Advanced Settings", go to the bottom and to the "Test Page", and finally "Become User 0" to see all the functionality. + See [BUILDING.md](BUILDING.md) for comprehensive build instructions for all platforms (Web, Electron, iOS, Android, Docker). ## Development Database Clearing -TimeSafari provides a simple script-based approach to clear the database for development purposes. +TimeSafari provides a simple script-based approach to clear the local database (not the claim server) for development purposes. ### Quick Usage ```bash @@ -126,7 +101,6 @@ const apiUrl = `${APP_SERVER}/api/claim/123`; ### Documentation -- [Domain Configuration System](docs/domain-configuration.md) - Complete guide - [Constants and Configuration](src/constants/app.ts) - Core constants ## Tests