From 25974cae22f6e58468d94a0e4bf3a4228b244c7e Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Tue, 24 Jun 2025 11:11:33 +0000 Subject: [PATCH] migration: move to bash based build scripts --- .../rules/architectural_decision_record.mdc | 25 +- .dockerignore | 171 +++++ BUILDING.md | 628 ++++++++++-------- Dockerfile | 201 +++++- android/build.gradle | 2 +- docker-compose.yml | 210 ++++++ docker/README.md | 509 ++++++++++++++ docker/default.conf | 110 +++ docker/nginx.conf | 72 ++ docker/run.sh | 272 ++++++++ docker/staging.conf | 110 +++ experiment.sh | 155 +++++ index.html | 5 +- package.json | 26 +- requirements.txt | 6 - scripts/README.md | 285 ++++++++ scripts/build-android.sh | 71 ++ scripts/build-electron-linux.sh | 82 +++ scripts/build-electron-mac.sh | 74 +++ scripts/build-electron.sh | 53 ++ scripts/common.sh | 331 +++++++++ scripts/electron-dev.sh | 44 ++ scripts/test-all.sh | 44 ++ scripts/test-common.sh | 74 +++ scripts/test-env.sh | 59 ++ scripts/test-mobile.sh | 40 ++ src/main.pywebview.ts | 4 - src/pywebview/main.py | 59 -- src/services/PlatformServiceFactory.ts | 5 - .../platforms/PyWebViewPlatformService.ts | 135 ---- src/services/platforms/WebPlatformService.ts | 8 - src/views/HomeView.vue | 3 +- vite.config.common.mts | 6 +- vite.config.pywebview.mts | 4 - 34 files changed, 3331 insertions(+), 552 deletions(-) create mode 100644 .dockerignore create mode 100644 docker-compose.yml create mode 100644 docker/README.md create mode 100644 docker/default.conf create mode 100644 docker/nginx.conf create mode 100755 docker/run.sh create mode 100644 docker/staging.conf create mode 100755 experiment.sh delete mode 100644 requirements.txt create mode 100644 scripts/README.md create mode 100755 scripts/build-android.sh create mode 100755 scripts/build-electron-linux.sh create mode 100644 scripts/build-electron-mac.sh create mode 100755 scripts/build-electron.sh create mode 100755 scripts/common.sh create mode 100755 scripts/electron-dev.sh create mode 100755 scripts/test-all.sh create mode 100755 scripts/test-common.sh create mode 100755 scripts/test-env.sh create mode 100755 scripts/test-mobile.sh delete mode 100644 src/main.pywebview.ts delete mode 100644 src/pywebview/main.py delete mode 100644 src/services/platforms/PyWebViewPlatformService.ts delete mode 100644 vite.config.pywebview.mts diff --git a/.cursor/rules/architectural_decision_record.mdc b/.cursor/rules/architectural_decision_record.mdc index a013a3e1..d78a0f23 100644 --- a/.cursor/rules/architectural_decision_record.mdc +++ b/.cursor/rules/architectural_decision_record.mdc @@ -7,13 +7,13 @@ alwaysApply: true ## 1. Platform Support Matrix -| Feature | Web (PWA) | Capacitor (Mobile) | Electron (Desktop) | PyWebView (Desktop) | -|---------|-----------|-------------------|-------------------|-------------------| -| QR Code Scanning | WebInlineQRScanner | @capacitor-mlkit/barcode-scanning | Not Implemented | Not Implemented | -| Deep Linking | URL Parameters | App URL Open Events | Not Implemented | Not Implemented | -| File System | Limited (Browser API) | Capacitor Filesystem | Electron fs | PyWebView Python Bridge | -| Camera Access | MediaDevices API | Capacitor Camera | Not Implemented | Not Implemented | -| Platform Detection | Web APIs | Capacitor.isNativePlatform() | process.env checks | process.env checks | +| Feature | Web (PWA) | Capacitor (Mobile) | Electron (Desktop) | +|---------|-----------|-------------------|-------------------| +| QR Code Scanning | WebInlineQRScanner | @capacitor-mlkit/barcode-scanning | Not Implemented | +| Deep Linking | URL Parameters | App URL Open Events | Not Implemented | +| File System | Limited (Browser API) | Capacitor Filesystem | Electron fs | +| Camera Access | MediaDevices API | Capacitor Camera | Not Implemented | +| Platform Detection | Web APIs | Capacitor.isNativePlatform() | process.env checks | ## 2. Project Structure @@ -42,7 +42,6 @@ src/ ├── main.common.ts # Shared initialization ├── main.capacitor.ts # Mobile entry ├── main.electron.ts # Electron entry -├── main.pywebview.ts # PyWebView entry └── main.web.ts # Web/PWA entry ``` @@ -52,9 +51,7 @@ root/ ├── vite.config.common.mts # Shared config ├── vite.config.capacitor.mts # Mobile build ├── vite.config.electron.mts # Electron build -├── vite.config.pywebview.mts # PyWebView build -├── vite.config.web.mts # Web/PWA build -└── vite.config.utils.mts # Build utilities +└── vite.config.web.mts # Web/PWA build ``` ## 3. Service Architecture @@ -68,8 +65,7 @@ services/ ├── platforms/ # Platform-specific services │ ├── WebPlatformService.ts │ ├── CapacitorPlatformService.ts -│ ├── ElectronPlatformService.ts -│ └── PyWebViewPlatformService.ts +│ └── ElectronPlatformService.ts └── factory/ # Service factories └── PlatformServiceFactory.ts ``` @@ -167,8 +163,7 @@ export function createBuildConfig(mode: string) { # Build commands from package.json "build:web": "vite build --config vite.config.web.mts", "build:capacitor": "vite build --config vite.config.capacitor.mts", -"build:electron": "vite build --config vite.config.electron.mts", -"build:pywebview": "vite build --config vite.config.pywebview.mts" +"build:electron": "vite build --config vite.config.electron.mts" ``` ## 6. Testing Strategy diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..32dbb3f0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,171 @@ +# TimeSafari Docker Ignore File +# Author: Matthew Raymer +# Description: Excludes unnecessary files from Docker build context +# +# Benefits: +# - Faster build times +# - Smaller build context +# - Reduced image size +# - Better security (excludes sensitive files) + +# Dependencies +node_modules +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Build outputs +dist +dist-* +build +*.tsbuildinfo + +# Development files +.git +.gitignore +README.md +CHANGELOG.md +CONTRIBUTING.md +BUILDING.md +LICENSE + +# IDE and editor files +.vscode +.idea +*.swp +*.swo +*~ + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Dependency directories +jspm_packages/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# Test files +test-playwright +test-playwright-results +test-results +test-scripts + +# Documentation +doc + +# Scripts (keep only what's needed for build) +scripts/test-*.sh +scripts/*.js +scripts/README.md + +# Platform-specific files +android +ios +electron + +# Docker files (avoid recursive copying) +Dockerfile* +docker-compose* +.dockerignore + +# CI/CD files +.github +.gitlab-ci.yml +.travis.yml +.circleci + +# Temporary files +tmp +temp + +# Backup files +*.bak +*.backup + +# Archive files +*.tar +*.tar.gz +*.zip +*.rar + +# Certificate files +*.pem +*.key +*.crt +*.p12 + +# Configuration files that might contain secrets +*.secrets +secrets.json +config.local.json \ No newline at end of file diff --git a/BUILDING.md b/BUILDING.md index 04531f01..00211407 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -1,6 +1,6 @@ # Building TimeSafari -This guide explains how to build TimeSafari for different platforms. +This guide explains how to build TimeSafari for different platforms using our unified build scripts. ## Prerequisites @@ -11,6 +11,57 @@ For a quick dev environment setup, use [pkgx](https://pkgx.dev). - Git - For desktop builds: Additional build tools based on your OS +## Unified Build Scripts + +TimeSafari now uses unified build scripts that automatically handle environment variables, logging, error handling, and timing. All scripts are located in the `scripts/` directory and use a common utilities library. + +### Script Features + +- **Automatic Environment Setup**: Each script sets the correct environment variables for its build type +- **Rich Logging**: Colored, timestamped output with different log levels +- **Error Handling**: Proper exit codes and graceful failure recovery +- **Timing**: Automatic execution time tracking for each step +- **Validation**: Checks for required dependencies and files +- **CLI Options**: `--help`, `--verbose`, `--env` flags for all scripts + +### Available Scripts + +| Script | Purpose | Command | +|--------|---------|---------| +| `electron-dev.sh` | Electron development | `./scripts/electron-dev.sh` | +| `electron-build.sh` | Electron build | `./scripts/build-electron.sh` | +| `capacitor-dev.sh` | Capacitor development | `./scripts/capacitor-dev.sh` | +| `capacitor-build.sh` | Capacitor build | `./scripts/build-capacitor.sh` | +| `web-dev.sh` | Web development | `./scripts/web-dev.sh` | +| `web-build.sh` | Web build | `./scripts/build-web.sh` | + +### Environment Variables + +All scripts automatically set the correct environment variables for their build type: + +| Build Type | VITE_PLATFORM | VITE_PWA_ENABLED | VITE_DISABLE_PWA | NODE_ENV | +|------------|---------------|------------------|------------------|----------| +| `electron` | electron | false | true | production* | +| `capacitor` | capacitor | false | true | - | +| `web` | web | true | false | - | + +*NODE_ENV=production only set when production mode is enabled + +### CLI Options + +All scripts support these options: + +```bash +# Show help +./scripts/build-electron.sh --help + +# Enable verbose logging +./scripts/build-electron.sh --verbose + +# Show environment variables +./scripts/build-electron.sh --env +``` + ## Forks If you have forked this to make your own app, you'll want to customize the iOS & Android files. You can either edit existing ones, or you can remove the `ios` and `android` directories and regenerate them before the `npx cap sync` step in each setup. @@ -30,18 +81,19 @@ Install dependencies: npm install ``` -## Web Dev Locally +## Web Development + +### Local Development ```bash npm run dev ``` -## Web Build for Server +### Web Build for Server 1. Run the production build: ```bash - rm -rf dist npm run build:web ``` @@ -49,223 +101,97 @@ Install dependencies: 2. To test the production build locally: - You'll likely want to use test locations for the Endorser & image & partner servers; see "DEFAULT_ENDORSER_API_SERVER" & "DEFAULT_IMAGE_API_SERVER" & "DEFAULT_PARTNER_API_SERVER" below. - ```bash npm run serve ``` -### Compile and minify for test & production - -* If there are DB changes: before updating the test server, open browser(s) with current version to test DB migrations. +### Environment Configuration -* `npx prettier --write ./sw_scripts/` - -* Update the ClickUp tasks & CHANGELOG.md & the version in package.json, run `npm install`. - -* Run a build to make sure package-lock version is updated, linting works, etc: `npm install && npm run build` - -* Commit everything (since the commit hash is used the app). - -* Put the commit hash in the changelog (which will help you remember to bump the version in the step later). - -* Tag with the new version, [online](https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa/releases) or `git tag 1.0.2 && git push origin 1.0.2`. - -* For test, build the app (because test server is not yet set up to build): +For different environments, create `.env` files: ```bash -TIME_SAFARI_APP_TITLE="TimeSafari_Test" VITE_APP_SERVER=https://test.timesafari.app VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZYYPS272F VITE_DEFAULT_ENDORSER_API_SERVER=https://test-api.endorser.ch VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app VITE_DEFAULT_PARTNER_API_SERVER=https://test-partner-api.endorser.ch VITE_DEFAULT_PUSH_SERVER=https://test.timesafari.app VITE_PASSKEYS_ENABLED=true npm run build:web +# .env.development +VITE_APP_SERVER=https://dev.timesafari.app +VITE_DEFAULT_ENDORSER_API_SERVER=https://dev-api.endorser.ch +VITE_DEFAULT_IMAGE_API_SERVER=https://dev-image-api.timesafari.app +VITE_DEFAULT_PARTNER_API_SERVER=https://dev-partner-api.endorser.ch +VITE_DEFAULT_PUSH_SERVER=https://dev.timesafari.app +VITE_PASSKEYS_ENABLED=true + +# .env.production +VITE_APP_SERVER=https://timesafari.app +VITE_DEFAULT_ENDORSER_API_SERVER=https://api.endorser.ch +VITE_DEFAULT_IMAGE_API_SERVER=https://image-api.timesafari.app +VITE_DEFAULT_PARTNER_API_SERVER=https://partner-api.endorser.ch +VITE_DEFAULT_PUSH_SERVER=https://timesafari.app +VITE_PASSKEYS_ENABLED=true ``` - ... and transfer to the test server: - - ```bash - rsync -azvu -e "ssh -i ~/.ssh/..." dist ubuntutest@test.timesafari.app:time-safari - ``` - -(Let's replace that with a .env.development or .env.staging file.) - -(Note: The test BVC_MEETUPS_PROJECT_CLAIM_ID does not resolve as a URL because it's only in the test DB and the prod redirect won't redirect there.) - -* For prod, get on the server and run the correct build: - - ... and log onto the server: - - * `pkgx +npm sh` - - * `cd crowd-funder-for-time-pwa && git checkout master && git pull && git checkout 1.0.2 && npm install && npm run build:web && cd -` - - (The plain `npm run build:web` uses the .env.production file.) - -* Back up the time-safari/dist folder & deploy: `mv time-safari/dist time-safari-dist-prev-2 && mv crowd-funder-for-time-pwa/dist time-safari/` - -* Record the new hash in the changelog. Edit package.json to increment version & add "-beta", `npm install`, commit, and push. Also record what version is on production. - -## Docker Deployment - -The application can be containerized using Docker for consistent deployment across environments. - -### Prerequisites - -- Docker installed on your system -- Docker Compose (optional, for multi-container setups) - -### Building the Docker Image - -1. Build the Docker image: - - ```bash - docker build -t timesafari:latest . - ``` - -2. For development builds with specific environment variables: - - ```bash - docker build --build-arg NODE_ENV=development -t timesafari:dev . - ``` - -### Running the Container - -1. Run the container: - - ```bash - docker run -d -p 80:80 timesafari:latest - ``` - -2. For development with hot-reloading: - - ```bash - docker run -d -p 80:80 -v $(pwd):/app timesafari:dev - ``` - -### Using Docker Compose - -Create a `docker-compose.yml` file: +## Desktop Build (Electron) -```yaml -version: '3.8' -services: - timesafari: - build: . - ports: - - "80:80" - environment: - - NODE_ENV=production - restart: unless-stopped -``` +### Development -Run with Docker Compose: +For development with automatic environment setup: ```bash -docker-compose up -d +./scripts/electron-dev.sh ``` -### Production Deployment - -For production deployment, consider the following: - -1. Use specific version tags instead of 'latest' -2. Implement health checks -3. Configure proper logging -4. Set up reverse proxy with SSL termination -5. Use Docker secrets for sensitive data +### Production Build -Example production deployment: +For production builds with automatic environment setup: ```bash -# Build with specific version -docker build -t timesafari:1.0.0 . - -# Run with production settings -docker run -d \ - --name timesafari \ - -p 80:80 \ - --restart unless-stopped \ - -e NODE_ENV=production \ - timesafari:1.0.0 +./scripts/build-electron.sh ``` -### Troubleshooting Docker - -1. **Container fails to start** - - Check logs: `docker logs ` - - Verify port availability - - Check environment variables - -2. **Build fails** - - Ensure all dependencies are in package.json - - Check Dockerfile syntax - - Verify build context - -3. **Performance issues** - - Monitor container resources: `docker stats` - - Check nginx configuration - - Verify caching settings - -## Desktop Build (Electron) - -### Linux Build - -1. Build the electron app in production mode: - - ```bash - npm run build:electron-prod - ``` - -2. Package the Electron app for Linux: - - ```bash - # For AppImage (recommended) - npm run electron:build-linux - - # For .deb package - npm run electron:build-linux-deb - ``` +### Linux Packaging -3. The packaged applications will be in `dist-electron-packages/`: - - AppImage: `dist-electron-packages/TimeSafari-x.x.x.AppImage` - - DEB: `dist-electron-packages/timesafari_x.x.x_amd64.deb` +```bash +# Build AppImage (recommended) +./scripts/build-electron-linux.sh -### macOS Build +# Build .deb package +./scripts/build-electron-linux.sh deb -1. Build the electron app in production mode: +# Build production AppImage +./scripts/build-electron-linux.sh prod +``` - ```bash - npm run build:web - npm run build:electron - npm run electron:build-mac - ``` +The packaged applications will be in `dist-electron-packages/`: +- AppImage: `dist-electron-packages/TimeSafari-x.x.x.AppImage` +- DEB: `dist-electron-packages/timesafari_x.x.x_amd64.deb` -2. Package the Electron app for macOS: +### macOS Packaging - ```bash - # For Intel Macs - npm run electron:build-mac +```bash +# Build standard Mac package +./scripts/build-electron-mac.sh - # For Universal build (Intel + Apple Silicon) - npm run electron:build-mac-universal - ``` +# Build universal package (Intel + Apple Silicon) +./scripts/build-electron-mac.sh universal +``` -3. The packaged applications will be in `dist-electron-packages/`: - - `.app` bundle: `TimeSafari.app` - - `.dmg` installer: `TimeSafari-x.x.x.dmg` - - `.zip` archive: `TimeSafari-x.x.x-mac.zip` +The packaged applications will be in `dist-electron-packages/`: +- `.app` bundle: `TimeSafari.app` +- `.dmg` installer: `TimeSafari-x.x.x.dmg` +- `.zip` archive: `TimeSafari-x.x.x-mac.zip` ### Code Signing and Notarization (macOS) For public distribution on macOS, you need to code sign and notarize your app: -1. Set up environment variables: +1. Set up environment variables in `.env` file: ```bash - export CSC_LINK=/path/to/your/certificate.p12 - export CSC_KEY_PASSWORD=your_certificate_password - export APPLE_ID=your_apple_id - export APPLE_ID_PASSWORD=your_app_specific_password + CSC_LINK=/path/to/your/certificate.p12 + CSC_KEY_PASSWORD=your_certificate_password + APPLE_ID=your_apple_id + APPLE_ID_PASSWORD=your_app_specific_password ``` 2. Build with signing: ```bash - npm run electron:build-mac + ./scripts/build-electron-mac.sh ``` ### Running the Packaged App @@ -298,174 +224,272 @@ For public distribution on macOS, you need to code sign and notarize your app: 2. Select "Open" 3. Click "Open" in the security dialog -### Development Testing +## Mobile Builds (Capacitor) -For testing the Electron build before packaging: +### Android Build -```bash -# Build and run in development mode (includes DevTools) -npm run electron:dev +Prerequisites: Android Studio with Java SDK installed + +#### Complete Build Process -# Build in production mode and test -npm run build:electron-prod && npm run electron:start +Use the unified Android build script: + +```bash +./scripts/build-android.sh ``` -## Mobile Builds (Capacitor) +This script automatically: +1. Sets up environment variables for Capacitor +2. Cleans previous builds +3. Builds web assets +4. Builds Capacitor version +5. Cleans and builds Gradle project +6. Syncs with Capacitor +7. Generates assets +8. Opens Android Studio -### iOS Build +#### Manual Steps (if needed) -Prerequisites: macOS with Xcode installed +If you need to run individual steps: -#### First-time iOS Configuration +1. Build the web assets: + ```bash + npm run build:web + npm run build:capacitor + ``` + +2. Update Android project: + ```bash + npx cap sync android + ``` -- Generate certificates inside XCode. +3. Generate assets: + ```bash + npx capacitor-assets generate --android + ``` + +4. Open in Android Studio: + ```bash + npx cap open android + ``` + +#### Console Build -- Right-click on App and under Signing & Capabilities set the Team. +For building from the console: -#### Each Release +```bash +cd android +./gradlew clean +./gradlew build -Dlint.baselines.continue=true +cd - +``` -0. First time (or if dependencies change): +For creating an `aab` file: - - `pkgx +rubygems.org sh` +```bash +cd android +./gradlew bundleDebug -Dlint.baselines.continue=true +cd - +``` - - ... and you may have to fix these, especially with pkgx: +For creating a signed release: +1. Setup signing configuration in `app/gradle.properties.secrets` +2. Add signing key file `app/time-safari-upload-key-pkcs12.jks` +3. Update version in `app/build.gradle` +4. Build release: ```bash - gem_path=$(which gem) - shortened_path="${gem_path:h:h}" - export GEM_HOME=$shortened_path - export GEM_PATH=$shortened_path + cd android + ./gradlew bundleRelease -Dlint.baselines.continue=true + cd - ``` -1. Build the web assets & update ios: +The `aab` file will be at `app/build/outputs/bundle/release`. + +### iOS Build + +Prerequisites: macOS with Xcode installed + +#### First-time iOS Configuration + +- Generate certificates inside Xcode +- Right-click on App and under Signing & Capabilities set the Team + +#### Build Process +1. Build the web assets & update iOS: ```bash - rm -rf dist npm run build:web npm run build:capacitor npx cap sync ios ``` - - If that fails with "Could not find..." then look at the "gem_path" instructions above. - -3. Copy the assets: - +2. Generate assets: ```bash - # It makes no sense why capacitor-assets will not run without these but it actually changes the contents. + # Create required directories mkdir -p ios/App/App/Assets.xcassets/AppIcon.appiconset echo '{"images":[]}' > ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json mkdir -p ios/App/App/Assets.xcassets/Splash.imageset echo '{"images":[]}' > ios/App/App/Assets.xcassets/Splash.imageset/Contents.json + + # Generate assets npx capacitor-assets generate --ios ``` -4. Bump the version to match Android & package.json: - - ``` - cd ios/App && xcrun agvtool new-version 35 && perl -p -i -e "s/MARKETING_VERSION = .*;/MARKETING_VERSION = 1.0.2;/g" App.xcodeproj/project.pbxproj && cd - - # Unfortunately this edits Info.plist directly. - #xcrun agvtool new-marketing-version 0.4.5 +3. Update version to match Android & package.json: + ```bash + cd ios/App + xcrun agvtool new-version 35 + perl -p -i -e "s/MARKETING_VERSION = .*;/MARKETING_VERSION = 1.0.2;/g" App.xcodeproj/project.pbxproj + cd - ``` -5. Open the project in Xcode: - +4. Open in Xcode: ```bash npx cap open ios ``` -6. Use Xcode to build and run on simulator or device. +5. Build and run on simulator or device using Xcode - * Select Product -> Destination with some Simulator version. Then click the run arrow. +#### Release Process -7. Release +1. Choose Product -> Destination -> Any iOS Device +2. Choose Product -> Archive +3. Click Distribute -> App Store Connect +4. In App Store Connect, add the build to the distribution - * Someday: Under "General" we want to rename a bunch of things to "Time Safari" - * Choose Product -> Destination -> Any iOS Device - * Choose Product -> Archive - * This will trigger a build and take time, needing user's "login" keychain password (user's login password), repeatedly. - * If it fails with `building for 'iOS', but linking in dylib (.../.pkgx/zlib.net/v1.3.0/lib/libz.1.3.dylib) built for 'macOS'` then run XCode outside that terminal (ie. not with `npx cap open ios`). - * Click Distribute -> App Store Connect - * In AppStoreConnect, add the build to the distribution: remove the current build with the "-" when you hover over it, then "Add Build" with the new build. - * May have to go to App Review, click Submission, then hover over the build and click "-". - * It can take 15 minutes for the build to show up in the list of builds. - * You'll probably have to "Manage" something about encryption, disallowed in France. - * Then "Save" and "Add to Review" and "Resubmit to App Review". +## Testing -### Android Build +### Complete Test Suite -Prerequisites: Android Studio with Java SDK installed +Run all tests with automatic environment setup: -1. Build the web assets: +```bash +./scripts/test-all.sh +``` - ```bash - rm -rf dist - npm run build:web - npm run build:capacitor - ``` +### Mobile Tests -2. Update Android project with latest build: +Run mobile-specific tests: - ```bash - npx cap sync android - ``` +```bash +./scripts/test-mobile.sh +``` + +### Environment Testing + +Test environment variable handling: + +```bash +./scripts/test-env.sh +``` + +## Docker Deployment + +The application can be containerized using Docker for consistent deployment across environments. + +### Prerequisites + +- Docker installed on your system +- Docker Compose (optional, for multi-container setups) + +### Building the Docker Image -3. Copy the assets +1. Build the Docker image: ```bash - npx capacitor-assets generate --android + docker build -t timesafari:latest . ``` -4. Bump version to match iOS & package.json: android/app/build.gradle - -5. Open the project in Android Studio: +2. For development builds with specific environment variables: ```bash - npx cap open android + docker build --build-arg NODE_ENV=development -t timesafari:dev . ``` -6. Use Android Studio to build and run on emulator or device. +### Running the Container -## Android Build from the console +1. Run the container: ```bash - cd android - ./gradlew clean - ./gradlew build -Dlint.baselines.continue=true - cd - + docker run -d -p 80:80 timesafari:latest ``` -... or, to create the `aab` file, `bundle` instead of `build`: +2. For development with hot-reloading: ```bash - ./gradlew bundleDebug -Dlint.baselines.continue=true + docker run -d -p 80:80 -v $(pwd):/app timesafari:dev ``` -... or, to create a signed release: +### Using Docker Compose - * Setup by adding the app/gradle.properties.secrets file (see properties at top of app/build.gradle) and the app/time-safari-upload-key-pkcs12.jks file - * In app/build.gradle, bump the versionCode and maybe the versionName - * Then `bundleRelease`: +Create a `docker-compose.yml` file: - ```bash - cd android - ./gradlew bundleRelease -Dlint.baselines.continue=true - cd - - ``` +```yaml +version: '3.8' +services: + timesafari: + build: . + ports: + - "80:80" + environment: + - NODE_ENV=production + restart: unless-stopped +``` + +Run with Docker Compose: + +```bash +docker-compose up -d +``` + +### Production Deployment + +For production deployment, consider the following: + +1. Use specific version tags instead of 'latest' +2. Implement health checks +3. Configure proper logging +4. Set up reverse proxy with SSL termination +5. Use Docker secrets for sensitive data + +Example production deployment: - ... and find your `aab` file at app/build/outputs/bundle/release +```bash +# Build with specific version +docker build -t timesafari:1.0.0 . + +# Run with production settings +docker run -d \ + --name timesafari \ + -p 80:80 \ + --restart unless-stopped \ + -e NODE_ENV=production \ + timesafari:1.0.0 +``` + +### Troubleshooting Docker + +1. **Container fails to start** + - Check logs: `docker logs ` + - Verify port availability + - Check environment variables -At play.google.com/console: +2. **Build fails** + - Ensure all dependencies are in package.json + - Check Dockerfile syntax + - Verify build context -- Go to the Testing Track (eg. Closed). -- Click "Create new release". -- Upload the `aab` file. -- Hit "Next". -- Save, go to "Publishing Overview" as prompted, and click "Send changes for review". +3. **Performance issues** + - Monitor container resources: `docker stats` + - Check nginx configuration + - Verify caching settings -- Note that if you add testers, you have to go to "Publishing Overview" and send those changes or your (closed) testers won't see it. +## Configuration +### Deep Links -## Android Configuration for deep links +#### Android Configuration You must add the following intent filter to the `android/app/src/main/AndroidManifest.xml` file: @@ -478,4 +502,52 @@ You must add the following intent filter to the `android/app/src/main/AndroidMan ``` -... though when we tried that most recently it failed to 'build' the APK with: http(s) scheme and host attribute are missing, but are required for Android App Links [AppLinkUrlError] +Note: When using `timesafari://` scheme, you may encounter build errors about missing http(s) scheme and host attributes. This is expected for custom URL schemes. + +#### iOS Configuration + +For iOS deep links, configure the URL scheme in Xcode: + +1. Open the project in Xcode +2. Select your app target +3. Go to Info tab +4. Add URL Types with scheme `timesafari` + +## Troubleshooting + +### Common Issues + +1. **Environment Variables Not Set** + - Use `--env` flag to check current environment: `./scripts/build-electron.sh --env` + - Verify `.env` file exists and is properly formatted + - Check script output for environment setup messages + +2. **Build Failures** + - Use `--verbose` flag for detailed logging: `./scripts/build-electron.sh --verbose` + - Check prerequisites are installed + - Verify all dependencies are installed: `npm install` + +3. **Permission Issues** + - Make scripts executable: `chmod +x scripts/*.sh` + - Check file permissions on build directories + +4. **Platform-Specific Issues** + - **Linux**: Ensure AppImage dependencies are installed + - **macOS**: Check code signing certificates and entitlements + - **Android**: Verify Android Studio and SDK are properly configured + - **iOS**: Ensure Xcode and certificates are set up correctly + +### Getting Help + +- Check script help: `./scripts/build-electron.sh --help` +- Review script documentation in `scripts/README.md` +- Test environment setup: `./scripts/test-env.sh` +- Test common utilities: `./scripts/test-common.sh` + +### Platform Support Matrix + +| Platform | Mode | PWA Enabled | Native Features | Notes | +|----------|------|-------------|-----------------|-------| +| `web` | web | true | false | Standard web browser | +| `capacitor` | capacitor | false | true | Mobile app (iOS/Android) | +| `electron` | electron | false | true | Desktop app (Windows/macOS/Linux) | diff --git a/Dockerfile b/Dockerfile index 3b62b209..9cc2543f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,36 +1,209 @@ -# Build stage -FROM node:22-alpine3.20 AS builder +# TimeSafari Docker Build +# Author: Matthew Raymer +# Description: Multi-stage Docker build for TimeSafari web application +# +# Build Process: +# 1. Base stage: Node.js with build dependencies +# 2. Builder stage: Compile web assets with Vite +# 3. Production stage: Nginx server with optimized assets +# +# Security Features: +# - Non-root user execution +# - Minimal attack surface with Alpine Linux +# - Multi-stage build to reduce image size +# - No build dependencies in final image +# +# Usage: +# Production: docker build -t timesafari:latest . +# Staging: docker build --build-arg BUILD_MODE=staging -t timesafari:staging . +# Development: docker build --build-arg BUILD_MODE=development -t timesafari:dev . +# +# Build Arguments: +# BUILD_MODE: development, staging, or production (default: production) +# NODE_ENV: node environment (default: production) +# VITE_PLATFORM: vite platform (default: web) +# VITE_PWA_ENABLED: enable PWA (default: true) +# VITE_DISABLE_PWA: disable PWA (default: false) +# +# Environment Variables: +# NODE_ENV: Build environment (development/production) +# VITE_APP_SERVER: Application server URL +# VITE_DEFAULT_ENDORSER_API_SERVER: Endorser API server URL +# VITE_DEFAULT_IMAGE_API_SERVER: Image API server URL +# VITE_DEFAULT_PARTNER_API_SERVER: Partner API server URL +# VITE_DEFAULT_PUSH_SERVER: Push notification server URL +# VITE_PASSKEYS_ENABLED: Enable passkeys feature -# Install build dependencies +# ============================================================================= +# BASE STAGE - Common dependencies and setup +# ============================================================================= +FROM node:22-alpine3.20 AS base -RUN apk add --no-cache bash git python3 py3-pip py3-setuptools make g++ gcc +# Install system dependencies for build process +RUN apk add --no-cache \ + bash \ + git \ + python3 \ + py3-pip \ + py3-setuptools \ + make \ + g++ \ + gcc \ + && rm -rf /var/cache/apk/* + +# Create non-root user for security +RUN addgroup -g 1001 -S nodejs && \ + adduser -S nextjs -u 1001 # Set working directory WORKDIR /app -# Copy package files +# Copy package files for dependency installation COPY package*.json ./ -# Install dependencies -RUN npm ci +# Install dependencies with security audit +RUN npm ci --only=production --audit --fund=false && \ + npm audit fix --audit-level=moderate || true + +# ============================================================================= +# BUILDER STAGE - Compile web assets +# ============================================================================= +FROM base AS builder + +# Define build arguments with defaults +ARG BUILD_MODE=production +ARG NODE_ENV=production +ARG VITE_PLATFORM=web +ARG VITE_PWA_ENABLED=true +ARG VITE_DISABLE_PWA=false + +# Set environment variables from build arguments +ENV BUILD_MODE=${BUILD_MODE} +ENV NODE_ENV=${NODE_ENV} +ENV VITE_PLATFORM=${VITE_PLATFORM} +ENV VITE_PWA_ENABLED=${VITE_PWA_ENABLED} +ENV VITE_DISABLE_PWA=${VITE_DISABLE_PWA} + +# Install all dependencies (including dev dependencies) +RUN npm ci --audit --fund=false && \ + npm audit fix --audit-level=moderate || true # Copy source code COPY . . -# Build the application -RUN npm run build:web +# Build the application with proper error handling +RUN echo "Building TimeSafari in ${BUILD_MODE} mode..." && \ + npm run build:web || (echo "Build failed. Check the logs above." && exit 1) + +# Verify build output exists +RUN ls -la dist/ || (echo "Build output not found in dist/ directory" && exit 1) + +# ============================================================================= +# PRODUCTION STAGE - Nginx server +# ============================================================================= +FROM nginx:alpine AS production + +# Define build arguments for production stage +ARG BUILD_MODE=production +ARG NODE_ENV=production -# Production stage -FROM nginx:alpine +# Set environment variables +ENV BUILD_MODE=${BUILD_MODE} +ENV NODE_ENV=${NODE_ENV} + +# Install security updates and clean cache +RUN apk update && \ + apk upgrade && \ + apk add --no-cache \ + curl \ + && rm -rf /var/cache/apk/* + +# Create non-root user for nginx +RUN addgroup -g 1001 -S nginx && \ + adduser -S nginx -u 1001 -G nginx + +# Copy appropriate nginx configuration based on build mode +COPY docker/nginx.conf /etc/nginx/nginx.conf +COPY docker/default.conf /etc/nginx/conf.d/default.conf + +# Copy staging configuration if needed +COPY docker/staging.conf /etc/nginx/conf.d/staging.conf # Copy built assets from builder stage -COPY --from=builder /app/dist /usr/share/nginx/html +COPY --from=builder --chown=nginx:nginx /app/dist /usr/share/nginx/html + +# Create necessary directories with proper permissions +RUN mkdir -p /var/cache/nginx /var/log/nginx /var/run && \ + chown -R nginx:nginx /var/cache/nginx /var/log/nginx /var/run && \ + chown -R nginx:nginx /usr/share/nginx/html + +# Switch to non-root user +USER nginx + +# Expose port 80 +EXPOSE 80 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -f http://localhost/ || exit 1 + +# Start nginx with proper signal handling +CMD ["nginx", "-g", "daemon off;"] + +# ============================================================================= +# DEVELOPMENT STAGE - For development with hot reloading +# ============================================================================= +FROM base AS development + +# Define build arguments for development stage +ARG BUILD_MODE=development +ARG NODE_ENV=development +ARG VITE_PLATFORM=web +ARG VITE_PWA_ENABLED=true +ARG VITE_DISABLE_PWA=false + +# Set environment variables +ENV BUILD_MODE=${BUILD_MODE} +ENV NODE_ENV=${NODE_ENV} +ENV VITE_PLATFORM=${VITE_PLATFORM} +ENV VITE_PWA_ENABLED=${VITE_PWA_ENABLED} +ENV VITE_DISABLE_PWA=${VITE_DISABLE_PWA} + +# Install all dependencies including dev dependencies +RUN npm ci --audit --fund=false && \ + npm audit fix --audit-level=moderate || true + +# Copy source code +COPY . . + +# Expose development port +EXPOSE 5173 + +# Start development server +CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"] -# Copy nginx configuration if needed -# COPY nginx.conf /etc/nginx/conf.d/default.conf +# ============================================================================= +# STAGING STAGE - For staging environment testing +# ============================================================================= +FROM production AS staging + +# Define build arguments for staging stage +ARG BUILD_MODE=staging +ARG NODE_ENV=staging + +# Set environment variables +ENV BUILD_MODE=${BUILD_MODE} +ENV NODE_ENV=${NODE_ENV} + +# Copy staging-specific nginx configuration +COPY docker/staging.conf /etc/nginx/conf.d/default.conf # Expose port 80 EXPOSE 80 +# Health check for staging +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -f http://localhost/health || exit 1 + # Start nginx CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index 4d5e98b8..744e49d2 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -7,7 +7,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.9.1' + classpath 'com.android.tools.build:gradle:8.10.1' classpath 'com.google.gms:google-services:4.4.0' // NOTE: Do not place your application dependencies here; they belong diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..f82980f5 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,210 @@ +# TimeSafari Docker Compose Configuration +# Author: Matthew Raymer +# Description: Multi-environment Docker Compose setup for TimeSafari +# +# Usage: +# Development: docker-compose up dev +# Staging: docker-compose up staging +# Production: docker-compose up production +# Custom: BUILD_MODE=staging docker-compose up custom +# +# Environment Variables: +# BUILD_MODE: development, staging, or production (default: production) +# NODE_ENV: node environment (default: production) +# VITE_PLATFORM: vite platform (default: web) +# VITE_PWA_ENABLED: enable PWA (default: true) +# VITE_DISABLE_PWA: disable PWA (default: false) +# PORT: port to expose (default: 80 for production, 5173 for dev) +# ENV_FILE: environment file to use (default: .env.production) +# +# See .env files for application-specific configuration +# VITE_APP_SERVER: Application server URL +# VITE_DEFAULT_ENDORSER_API_SERVER: Endorser API server URL + +version: '3.8' + +# Default values that can be overridden +x-defaults: &defaults + build: + context: . + dockerfile: Dockerfile + args: + BUILD_MODE: ${BUILD_MODE:-production} + NODE_ENV: ${NODE_ENV:-production} + VITE_PLATFORM: ${VITE_PLATFORM:-web} + VITE_PWA_ENABLED: ${VITE_PWA_ENABLED:-true} + VITE_DISABLE_PWA: ${VITE_DISABLE_PWA:-false} + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + +services: + # Development service with hot reloading + dev: + <<: *defaults + build: + context: . + dockerfile: Dockerfile + target: development + args: + BUILD_MODE: development + NODE_ENV: development + VITE_PLATFORM: web + VITE_PWA_ENABLED: true + VITE_DISABLE_PWA: false + ports: + - "${DEV_PORT:-5173}:5173" + volumes: + - .:/app + - /app/node_modules + environment: + - NODE_ENV=development + - VITE_PLATFORM=web + - VITE_PWA_ENABLED=true + - VITE_DISABLE_PWA=false + env_file: + - ${DEV_ENV_FILE:-.env.development} + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:5173"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + # Staging service for testing + staging: + <<: *defaults + build: + context: . + dockerfile: Dockerfile + target: staging + args: + BUILD_MODE: staging + NODE_ENV: staging + VITE_PLATFORM: web + VITE_PWA_ENABLED: true + VITE_DISABLE_PWA: false + ports: + - "${STAGING_PORT:-8080}:80" + environment: + - NODE_ENV=staging + - VITE_PLATFORM=web + - VITE_PWA_ENABLED=true + - VITE_DISABLE_PWA=false + env_file: + - ${STAGING_ENV_FILE:-.env.staging} + + # Production service + production: + <<: *defaults + build: + context: . + dockerfile: Dockerfile + target: production + args: + BUILD_MODE: production + NODE_ENV: production + VITE_PLATFORM: web + VITE_PWA_ENABLED: true + VITE_DISABLE_PWA: false + ports: + - "${PROD_PORT:-80}:80" + environment: + - NODE_ENV=production + - VITE_PLATFORM=web + - VITE_PWA_ENABLED=true + - VITE_DISABLE_PWA=false + env_file: + - ${PROD_ENV_FILE:-.env.production} + + # Production service with SSL (requires certificates) + production-ssl: + <<: *defaults + build: + context: . + dockerfile: Dockerfile + target: production + args: + BUILD_MODE: production + NODE_ENV: production + VITE_PLATFORM: web + VITE_PWA_ENABLED: true + VITE_DISABLE_PWA: false + ports: + - "${SSL_PORT:-443}:443" + - "${HTTP_PORT:-80}:80" + environment: + - NODE_ENV=production + - VITE_PLATFORM=web + - VITE_PWA_ENABLED=true + - VITE_DISABLE_PWA=false + env_file: + - ${PROD_ENV_FILE:-.env.production} + volumes: + - ./ssl:/etc/nginx/ssl:ro + - ./docker/nginx-ssl.conf:/etc/nginx/conf.d/default.conf:ro + healthcheck: + test: ["CMD", "curl", "-f", "https://localhost/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + # Custom service - configurable via environment variables + custom: + <<: *defaults + build: + context: . + dockerfile: Dockerfile + target: ${BUILD_TARGET:-production} + args: + BUILD_MODE: ${BUILD_MODE:-production} + NODE_ENV: ${NODE_ENV:-production} + VITE_PLATFORM: ${VITE_PLATFORM:-web} + VITE_PWA_ENABLED: ${VITE_PWA_ENABLED:-true} + VITE_DISABLE_PWA: ${VITE_DISABLE_PWA:-false} + ports: + - "${CUSTOM_PORT:-8080}:${CUSTOM_INTERNAL_PORT:-80}" + environment: + - NODE_ENV=${NODE_ENV:-production} + - VITE_PLATFORM=${VITE_PLATFORM:-web} + - VITE_PWA_ENABLED=${VITE_PWA_ENABLED:-true} + - VITE_DISABLE_PWA=${VITE_DISABLE_PWA:-false} + env_file: + - ${CUSTOM_ENV_FILE:-.env.production} + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:${CUSTOM_INTERNAL_PORT:-80}/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + # Load balancer for production (optional) + nginx-lb: + image: nginx:alpine + ports: + - "${LB_PORT:-80}:80" + - "${LB_SSL_PORT:-443}:443" + volumes: + - ./docker/nginx-lb.conf:/etc/nginx/nginx.conf:ro + - ./ssl:/etc/nginx/ssl:ro + depends_on: + - production + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + +networks: + default: + driver: bridge + ipam: + config: + - subnet: 172.20.0.0/16 \ No newline at end of file diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 00000000..83f5bc27 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,509 @@ +# TimeSafari Docker Setup + +## Overview + +This directory contains Docker configuration files for building and deploying TimeSafari across different environments with full configurability. + +## Files + +- `Dockerfile` - Multi-stage Docker build for TimeSafari +- `nginx.conf` - Main nginx configuration with security headers +- `default.conf` - Production server configuration +- `staging.conf` - Staging server configuration with relaxed caching +- `docker-compose.yml` - Multi-environment Docker Compose setup +- `.dockerignore` - Optimizes build context +- `run.sh` - Convenient script to run different configurations + +## Quick Start + +### Using the Run Script (Recommended) + +```bash +# Development mode with hot reloading +./docker/run.sh dev + +# Staging mode for testing +./docker/run.sh staging + +# Production mode +./docker/run.sh production + +# Custom mode with environment variables +BUILD_MODE=staging ./docker/run.sh custom + +# Show build arguments for a mode +./docker/run.sh dev --build-args + +# Custom port and environment file +./docker/run.sh staging --port 9000 --env .env.test +``` + +### Using Docker Compose + +```bash +# Development environment with hot reloading +docker-compose up dev + +# Staging environment +docker-compose up staging + +# Production environment +docker-compose up production + +# Custom environment with variables +BUILD_MODE=staging docker-compose up custom +``` + +## Build Commands + +### Manual Docker Build + +```bash +# Build production image (default) +docker build -t timesafari:latest . + +# Build staging image +docker build --build-arg BUILD_MODE=staging -t timesafari:staging . + +# Build development image +docker build --build-arg BUILD_MODE=development -t timesafari:dev . + +# Build with custom arguments +docker build \ + --build-arg BUILD_MODE=staging \ + --build-arg NODE_ENV=staging \ + --build-arg VITE_PWA_ENABLED=true \ + -t timesafari:custom . +``` + +### Run Container + +```bash +# Run production container +docker run -d -p 80:80 timesafari:latest + +# Run with environment file +docker run -d -p 80:80 --env-file .env.production timesafari:latest + +# Run with custom environment variables +docker run -d -p 80:80 \ + -e VITE_APP_SERVER=https://myapp.com \ + -e VITE_DEFAULT_ENDORSER_API_SERVER=https://api.myapp.com \ + timesafari:latest +``` + +## Configuration Options + +### Build Arguments + +The Dockerfile supports these build arguments: + +| Argument | Default | Description | +|----------|---------|-------------| +| `BUILD_MODE` | `production` | Build mode: development, staging, or production | +| `NODE_ENV` | `production` | Node.js environment | +| `VITE_PLATFORM` | `web` | Vite platform type | +| `VITE_PWA_ENABLED` | `true` | Enable PWA features | +| `VITE_DISABLE_PWA` | `false` | Disable PWA features | + +### Environment Variables + +Docker Compose supports these environment variables: + +| Variable | Default | Description | +|----------|---------|-------------| +| `BUILD_MODE` | `production` | Build mode | +| `NODE_ENV` | `production` | Node environment | +| `VITE_PLATFORM` | `web` | Vite platform | +| `VITE_PWA_ENABLED` | `true` | Enable PWA | +| `VITE_DISABLE_PWA` | `false` | Disable PWA | +| `DEV_PORT` | `5173` | Development port | +| `STAGING_PORT` | `8080` | Staging port | +| `PROD_PORT` | `80` | Production port | +| `DEV_ENV_FILE` | `.env.development` | Development env file | +| `STAGING_ENV_FILE` | `.env.staging` | Staging env file | +| `PROD_ENV_FILE` | `.env.production` | Production env file | + +### Environment Files + +Create environment files for different deployments: + +```bash +# .env.development +VITE_APP_SERVER=https://dev.timesafari.app +VITE_DEFAULT_ENDORSER_API_SERVER=https://dev-api.endorser.ch +VITE_DEFAULT_IMAGE_API_SERVER=https://dev-image-api.timesafari.app +VITE_DEFAULT_PARTNER_API_SERVER=https://dev-partner-api.endorser.ch +VITE_DEFAULT_PUSH_SERVER=https://dev.timesafari.app +VITE_PASSKEYS_ENABLED=true + +# .env.staging +VITE_APP_SERVER=https://staging.timesafari.app +VITE_DEFAULT_ENDORSER_API_SERVER=https://staging-api.endorser.ch +VITE_DEFAULT_IMAGE_API_SERVER=https://staging-image-api.timesafari.app +VITE_DEFAULT_PARTNER_API_SERVER=https://staging-partner-api.endorser.ch +VITE_DEFAULT_PUSH_SERVER=https://staging.timesafari.app +VITE_PASSKEYS_ENABLED=true + +# .env.production +VITE_APP_SERVER=https://timesafari.app +VITE_DEFAULT_ENDORSER_API_SERVER=https://api.endorser.ch +VITE_DEFAULT_IMAGE_API_SERVER=https://image-api.timesafari.app +VITE_DEFAULT_PARTNER_API_SERVER=https://partner-api.endorser.ch +VITE_DEFAULT_PUSH_SERVER=https://timesafari.app +VITE_PASSKEYS_ENABLED=true +``` + +## Build Modes + +### Development Mode +- **Target**: `development` +- **Features**: Hot reloading, development server +- **Port**: 5173 +- **Caching**: Disabled +- **Use Case**: Local development + +```bash +./docker/run.sh dev +# or +docker build --target development -t timesafari:dev . +``` + +### Staging Mode +- **Target**: `staging` +- **Features**: Production build with relaxed caching +- **Port**: 8080 (mapped from 80) +- **Caching**: Short-term (1 hour) +- **Use Case**: Testing and QA + +```bash +./docker/run.sh staging +# or +docker build --build-arg BUILD_MODE=staging -t timesafari:staging . +``` + +### Production Mode +- **Target**: `production` +- **Features**: Optimized production build +- **Port**: 80 +- **Caching**: Long-term (1 year for assets) +- **Use Case**: Live deployment + +```bash +./docker/run.sh production +# or +docker build -t timesafari:latest . +``` + +### Custom Mode +- **Target**: Configurable via `BUILD_TARGET` +- **Features**: Fully configurable +- **Port**: Configurable via `CUSTOM_PORT` +- **Use Case**: Special deployments + +```bash +BUILD_MODE=staging NODE_ENV=staging ./docker/run.sh custom +``` + +## Advanced Usage + +### Custom Build Configuration + +```bash +# Build with specific environment +docker build \ + --build-arg BUILD_MODE=staging \ + --build-arg NODE_ENV=staging \ + --build-arg VITE_PWA_ENABLED=false \ + -t timesafari:staging-no-pwa . + +# Run with custom configuration +docker run -d -p 9000:80 \ + -e VITE_APP_SERVER=https://test.example.com \ + timesafari:staging-no-pwa +``` + +### Docker Compose with Custom Variables + +```bash +# Set environment variables +export BUILD_MODE=staging +export NODE_ENV=staging +export STAGING_PORT=9000 +export STAGING_ENV_FILE=.env.test + +# Run staging with custom config +docker-compose up staging +``` + +### Multi-Environment Deployment + +```bash +# Development +./docker/run.sh dev + +# Staging in another terminal +./docker/run.sh staging --port 8081 + +# Production in another terminal +./docker/run.sh production --port 8082 +``` + +## Security Features + +### Built-in Security +- **Non-root user execution**: All containers run as non-root users +- **Security headers**: XSS protection, content type options, frame options +- **Rate limiting**: API request rate limiting +- **File access restrictions**: Hidden files and backup files blocked +- **Minimal attack surface**: Alpine Linux base images + +### Security Headers +- `X-Frame-Options: SAMEORIGIN` +- `X-Content-Type-Options: nosniff` +- `X-XSS-Protection: 1; mode=block` +- `Referrer-Policy: strict-origin-when-cross-origin` +- `Content-Security-Policy`: Comprehensive CSP policy + +## Performance Optimizations + +### Caching Strategy +- **Static assets**: 1 year cache with immutable flag (production) +- **HTML files**: 1 hour cache (production) / no cache (staging) +- **Service worker**: No cache +- **Manifest**: 1 day cache (production) / 1 hour cache (staging) + +### Compression +- **Gzip compression**: Enabled for text-based files +- **Compression level**: 6 (balanced) +- **Minimum size**: 1024 bytes + +### Nginx Optimizations +- **Sendfile**: Enabled for efficient file serving +- **TCP optimizations**: nopush and nodelay enabled +- **Keepalive**: 65 second timeout +- **Worker processes**: Auto-detected based on CPU cores + +## Health Checks + +### Built-in Health Checks +All services include health checks that: +- Check every 30 seconds +- Timeout after 10 seconds +- Retry 3 times before marking unhealthy +- Start checking after 40 seconds + +### Health Check Endpoints +- **Production/Staging**: `http://localhost/health` +- **Development**: `http://localhost:5173` + +## SSL/HTTPS Setup + +### SSL Certificates +For SSL deployment, create an `ssl` directory with certificates: + +```bash +mkdir ssl +# Copy your certificates to ssl/ directory +cp your-cert.pem ssl/ +cp your-key.pem ssl/ +``` + +### SSL Configuration +Use the `production-ssl` service in docker-compose: + +```bash +docker-compose up production-ssl +``` + +## Monitoring and Logging + +### Log Locations +- **Access logs**: `/var/log/nginx/access.log` +- **Error logs**: `/var/log/nginx/error.log` + +### Log Format +``` +$remote_addr - $remote_user [$time_local] "$request" +$status $body_bytes_sent "$http_referer" +"$http_user_agent" "$http_x_forwarded_for" +``` + +### Log Levels +- **Production**: `warn` level +- **Staging**: `debug` level +- **Development**: Full logging + +## Troubleshooting + +### Common Issues + +#### Build Failures +```bash +# Check build logs +docker build -t timesafari:latest . 2>&1 | tee build.log + +# Verify dependencies +docker run --rm timesafari:latest npm list --depth=0 + +# Check build arguments +./docker/run.sh dev --build-args +``` + +#### Container Won't Start +```bash +# Check container logs +docker logs + +# Check health status +docker inspect | grep -A 10 "Health" + +# Verify port availability +netstat -tulpn | grep :80 +``` + +#### Environment Variables Not Set +```bash +# Check environment in container +docker exec env | grep VITE_ + +# Verify .env file +cat .env.production + +# Check build arguments +./docker/run.sh production --build-args +``` + +#### Performance Issues +```bash +# Check container resources +docker stats + +# Check nginx configuration +docker exec nginx -t + +# Monitor access logs +docker exec tail -f /var/log/nginx/access.log +``` + +### Debug Commands + +#### Container Debugging +```bash +# Enter running container +docker exec -it /bin/sh + +# Check nginx status +docker exec nginx -t + +# Check file permissions +docker exec ls -la /usr/share/nginx/html +``` + +#### Network Debugging +```bash +# Check container network +docker network inspect bridge + +# Test connectivity +docker exec curl -I http://localhost + +# Check DNS resolution +docker exec nslookup google.com +``` + +## Production Deployment + +### Recommended Production Setup +1. **Use specific version tags**: `timesafari:1.0.0` +2. **Implement health checks**: Already included +3. **Configure proper logging**: Use external log aggregation +4. **Set up reverse proxy**: Use nginx-lb service +5. **Use Docker secrets**: For sensitive data + +### Production Commands +```bash +# Build with specific version +docker build -t timesafari:1.0.0 . + +# Run with production settings +docker run -d \ + --name timesafari \ + -p 80:80 \ + --restart unless-stopped \ + --env-file .env.production \ + timesafari:1.0.0 + +# Update production deployment +docker stop timesafari +docker rm timesafari +docker build -t timesafari:1.0.1 . +docker run -d --name timesafari -p 80:80 --restart unless-stopped --env-file .env.production timesafari:1.0.1 +``` + +## Development Workflow + +### Local Development +```bash +# Start development environment +./docker/run.sh dev + +# Make changes to code (hot reloading enabled) +# Access at http://localhost:5173 + +# Stop development environment +docker-compose down dev +``` + +### Testing Changes +```bash +# Build and test staging +./docker/run.sh staging + +# Test production build locally +./docker/run.sh production +``` + +### Continuous Integration +```bash +# Build and test in CI +docker build -t timesafari:test . +docker run -d --name timesafari-test -p 8080:80 timesafari:test + +# Run tests against container +curl -f http://localhost:8080/health + +# Cleanup +docker stop timesafari-test +docker rm timesafari-test +``` + +## Best Practices + +### Security +- Always use non-root users +- Keep base images updated +- Scan images for vulnerabilities +- Use secrets for sensitive data +- Implement proper access controls + +### Performance +- Use multi-stage builds +- Optimize layer caching +- Minimize image size +- Use appropriate base images +- Implement proper caching + +### Monitoring +- Use health checks +- Monitor resource usage +- Set up log aggregation +- Implement metrics collection +- Use proper error handling + +### Maintenance +- Regular security updates +- Monitor for vulnerabilities +- Keep dependencies updated +- Document configuration changes +- Test deployment procedures \ No newline at end of file diff --git a/docker/default.conf b/docker/default.conf new file mode 100644 index 00000000..eabda54b --- /dev/null +++ b/docker/default.conf @@ -0,0 +1,110 @@ +# TimeSafari Default Server Configuration +# Author: Matthew Raymer +# Description: Production server configuration for TimeSafari web application +# +# Features: +# - Vue.js SPA routing support +# - Static file caching optimization +# - Security hardening +# - Performance optimization +# - Proper error handling + +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; + + # Handle Vue.js SPA routing + location / { + try_files $uri $uri/ /index.html; + + # Cache static assets + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + add_header Vary "Accept-Encoding"; + } + + # Cache HTML files for a shorter time + location ~* \.html$ { + expires 1h; + add_header Cache-Control "public, must-revalidate"; + } + } + + # Handle service worker + location /sw.js { + expires 0; + add_header Cache-Control "no-cache, no-store, must-revalidate"; + add_header Pragma "no-cache"; + } + + # Handle manifest file + location /manifest.json { + expires 1d; + add_header Cache-Control "public"; + } + + # Handle API requests (if needed) + location /api/ { + limit_req zone=api burst=20 nodelay; + proxy_pass http://backend:3000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Handle health check + location /health { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; + } + + # Handle robots.txt + location /robots.txt { + expires 1d; + add_header Cache-Control "public"; + } + + # Handle favicon + location /favicon.ico { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # Security: Deny access to hidden files + location ~ /\. { + deny all; + access_log off; + log_not_found off; + } + + # Security: Deny access to backup files + location ~ ~$ { + deny all; + access_log off; + log_not_found off; + } + + # Error pages + error_page 404 /index.html; + error_page 500 502 503 504 /50x.html; + + location = /50x.html { + root /usr/share/nginx/html; + } + + # Logging + access_log /var/log/nginx/access.log main; + error_log /var/log/nginx/error.log warn; +} \ No newline at end of file diff --git a/docker/nginx.conf b/docker/nginx.conf new file mode 100644 index 00000000..fc0d4047 --- /dev/null +++ b/docker/nginx.conf @@ -0,0 +1,72 @@ +# TimeSafari Nginx Configuration +# Author: Matthew Raymer +# Description: Main nginx configuration for TimeSafari web application +# +# Features: +# - Security headers for web application +# - Gzip compression for better performance +# - Proper handling of Vue.js SPA routing +# - Static file caching optimization +# - Security hardening + +user nginx; +worker_processes auto; +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; + use epoll; + multi_accept on; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Logging format + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + # Performance optimizations + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + client_max_body_size 16M; + + # Gzip compression + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_proxied any; + gzip_comp_level 6; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/json + application/javascript + application/xml+rss + application/atom+xml + image/svg+xml; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https:; frame-ancestors 'self';" always; + + # Rate limiting + limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; + limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s; + + # Include server configurations + include /etc/nginx/conf.d/*.conf; +} \ No newline at end of file diff --git a/docker/run.sh b/docker/run.sh new file mode 100755 index 00000000..641b8947 --- /dev/null +++ b/docker/run.sh @@ -0,0 +1,272 @@ +#!/bin/bash +# TimeSafari Docker Run Script +# Author: Matthew Raymer +# Description: Convenient script to run TimeSafari in different Docker configurations +# +# Usage: +# ./docker/run.sh dev # Run development mode +# ./docker/run.sh staging # Run staging mode +# ./docker/run.sh production # Run production mode +# ./docker/run.sh custom # Run custom mode with environment variables +# +# Environment Variables: +# BUILD_MODE: development, staging, or production +# NODE_ENV: node environment +# VITE_PLATFORM: vite platform +# VITE_PWA_ENABLED: enable PWA +# VITE_DISABLE_PWA: disable PWA +# PORT: port to expose +# ENV_FILE: environment file to use + +set -e + +# Colors for output +readonly RED='\033[0;31m' +readonly GREEN='\033[0;32m' +readonly YELLOW='\033[1;33m' +readonly BLUE='\033[0;34m' +readonly NC='\033[0m' # No Color + +# Logging functions +log_info() { + echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] [INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] [SUCCESS]${NC} $1" +} + +log_warn() { + echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] [WARN]${NC} $1" +} + +log_error() { + echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR]${NC} $1" +} + +# Function to show usage +show_usage() { + echo "TimeSafari Docker Run Script" + echo "" + echo "Usage: $0 [options]" + echo "" + echo "Modes:" + echo " dev - Development mode with hot reloading" + echo " staging - Staging mode for testing" + echo " production - Production mode" + echo " custom - Custom mode with environment variables" + echo "" + echo "Options:" + echo " --port - Custom port (default: 5173 for dev, 8080 for staging, 80 for production)" + echo " --env - Environment file (default: .env.)" + echo " --build-args - Show build arguments for the mode" + echo " --help - Show this help message" + echo "" + echo "Examples:" + echo " $0 dev" + echo " $0 staging --port 9000" + echo " $0 production --env .env.prod" + echo " BUILD_MODE=staging $0 custom" + echo "" + echo "Environment Variables:" + echo " BUILD_MODE: development, staging, or production" + echo " NODE_ENV: node environment" + echo " VITE_PLATFORM: vite platform" + echo " VITE_PWA_ENABLED: enable PWA" + echo " VITE_DISABLE_PWA: disable PWA" + echo " PORT: port to expose" + echo " ENV_FILE: environment file to use" +} + +# Function to show build arguments for a mode +show_build_args() { + local mode=$1 + echo "Build arguments for $mode mode:" + echo "" + case $mode in + dev) + echo " BUILD_MODE: development" + echo " NODE_ENV: development" + echo " VITE_PLATFORM: web" + echo " VITE_PWA_ENABLED: true" + echo " VITE_DISABLE_PWA: false" + echo " Target: development" + echo " Port: 5173" + ;; + staging) + echo " BUILD_MODE: staging" + echo " NODE_ENV: staging" + echo " VITE_PLATFORM: web" + echo " VITE_PWA_ENABLED: true" + echo " VITE_DISABLE_PWA: false" + echo " Target: staging" + echo " Port: 80 (mapped to 8080)" + ;; + production) + echo " BUILD_MODE: production" + echo " NODE_ENV: production" + echo " VITE_PLATFORM: web" + echo " VITE_PWA_ENABLED: true" + echo " VITE_DISABLE_PWA: false" + echo " Target: production" + echo " Port: 80" + ;; + custom) + echo " BUILD_MODE: \${BUILD_MODE:-production}" + echo " NODE_ENV: \${NODE_ENV:-production}" + echo " VITE_PLATFORM: \${VITE_PLATFORM:-web}" + echo " VITE_PWA_ENABLED: \${VITE_PWA_ENABLED:-true}" + echo " VITE_DISABLE_PWA: \${VITE_DISABLE_PWA:-false}" + echo " Target: \${BUILD_TARGET:-production}" + echo " Port: \${CUSTOM_PORT:-8080}:\${CUSTOM_INTERNAL_PORT:-80}" + ;; + *) + log_error "Unknown mode: $mode" + exit 1 + ;; + esac +} + +# Function to check if Docker is running +check_docker() { + if ! docker info > /dev/null 2>&1; then + log_error "Docker is not running. Please start Docker and try again." + exit 1 + fi +} + +# Function to check if docker-compose is available +check_docker_compose() { + if ! command -v docker-compose > /dev/null 2>&1; then + log_error "docker-compose is not installed. Please install docker-compose and try again." + exit 1 + fi +} + +# Function to check if required files exist +check_files() { + local mode=$1 + local env_file=$2 + + if [ ! -f "Dockerfile" ]; then + log_error "Dockerfile not found. Please run this script from the project root." + exit 1 + fi + + if [ ! -f "docker-compose.yml" ]; then + log_error "docker-compose.yml not found. Please run this script from the project root." + exit 1 + fi + + if [ -n "$env_file" ] && [ ! -f "$env_file" ]; then + log_warn "Environment file $env_file not found. Using defaults." + fi +} + +# Function to run the container +run_container() { + local mode=$1 + local port=$2 + local env_file=$3 + + log_info "Starting TimeSafari in $mode mode..." + + # Set environment variables based on mode + case $mode in + dev) + export DEV_PORT=${port:-5173} + if [ -n "$env_file" ]; then + export DEV_ENV_FILE="$env_file" + fi + docker-compose up dev + ;; + staging) + export STAGING_PORT=${port:-8080} + if [ -n "$env_file" ]; then + export STAGING_ENV_FILE="$env_file" + fi + docker-compose up staging + ;; + production) + export PROD_PORT=${port:-80} + if [ -n "$env_file" ]; then + export PROD_ENV_FILE="$env_file" + fi + docker-compose up production + ;; + custom) + export CUSTOM_PORT=${port:-8080} + if [ -n "$env_file" ]; then + export CUSTOM_ENV_FILE="$env_file" + fi + docker-compose up custom + ;; + *) + log_error "Unknown mode: $mode" + exit 1 + ;; + esac +} + +# Main script +main() { + local mode="" + local port="" + local env_file="" + local show_args=false + + # Parse command line arguments + while [[ $# -gt 0 ]]; do + case $1 in + dev|staging|production|custom) + mode="$1" + shift + ;; + --port) + port="$2" + shift 2 + ;; + --env) + env_file="$2" + shift 2 + ;; + --build-args) + show_args=true + shift + ;; + --help|-h) + show_usage + exit 0 + ;; + *) + log_error "Unknown option: $1" + show_usage + exit 1 + ;; + esac + done + + # Check if mode is provided + if [ -z "$mode" ]; then + log_error "No mode specified." + show_usage + exit 1 + fi + + # Show build arguments if requested + if [ "$show_args" = true ]; then + show_build_args "$mode" + exit 0 + fi + + # Check prerequisites + check_docker + check_docker_compose + check_files "$mode" "$env_file" + + # Run the container + run_container "$mode" "$port" "$env_file" +} + +# Run main function with all arguments +main "$@" \ No newline at end of file diff --git a/docker/staging.conf b/docker/staging.conf new file mode 100644 index 00000000..c495ebc5 --- /dev/null +++ b/docker/staging.conf @@ -0,0 +1,110 @@ +# TimeSafari Staging Server Configuration +# Author: Matthew Raymer +# Description: Staging server configuration for TimeSafari web application +# +# Features: +# - Relaxed caching for testing +# - Debug-friendly settings +# - Same security as production +# - Development-friendly error handling + +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + # Security headers (same as production) + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; + + # Handle Vue.js SPA routing + location / { + try_files $uri $uri/ /index.html; + + # Relaxed caching for staging + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1h; + add_header Cache-Control "public, must-revalidate"; + add_header Vary "Accept-Encoding"; + } + + # No caching for HTML files in staging + location ~* \.html$ { + expires 0; + add_header Cache-Control "no-cache, no-store, must-revalidate"; + add_header Pragma "no-cache"; + } + } + + # Handle service worker (no caching) + location /sw.js { + expires 0; + add_header Cache-Control "no-cache, no-store, must-revalidate"; + add_header Pragma "no-cache"; + } + + # Handle manifest file (short cache) + location /manifest.json { + expires 1h; + add_header Cache-Control "public, must-revalidate"; + } + + # Handle API requests (if needed) + location /api/ { + limit_req zone=api burst=20 nodelay; + proxy_pass http://backend:3000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Handle health check + location /health { + access_log off; + return 200 "healthy-staging\n"; + add_header Content-Type text/plain; + } + + # Handle robots.txt (no caching in staging) + location /robots.txt { + expires 0; + add_header Cache-Control "no-cache, no-store, must-revalidate"; + } + + # Handle favicon (short cache) + location /favicon.ico { + expires 1h; + add_header Cache-Control "public, must-revalidate"; + } + + # Security: Deny access to hidden files + location ~ /\. { + deny all; + access_log off; + log_not_found off; + } + + # Security: Deny access to backup files + location ~ ~$ { + deny all; + access_log off; + log_not_found off; + } + + # Error pages (more verbose for staging) + error_page 404 /index.html; + error_page 500 502 503 504 /50x.html; + + location = /50x.html { + root /usr/share/nginx/html; + } + + # Enhanced logging for staging + access_log /var/log/nginx/access.log main; + error_log /var/log/nginx/error.log debug; +} \ No newline at end of file diff --git a/experiment.sh b/experiment.sh new file mode 100755 index 00000000..8e1d9814 --- /dev/null +++ b/experiment.sh @@ -0,0 +1,155 @@ +#!/bin/bash +# experiment.sh +# Author: Matthew Raymer +# Description: Build script for TimeSafari Electron application +# This script handles the complete build process for the TimeSafari Electron app, +# including web asset compilation and Capacitor sync. +# +# Build Process: +# 1. Environment setup and dependency checks +# 2. Web asset compilation (Vite) +# 3. Capacitor sync +# 4. Electron start +# +# Dependencies: +# - Node.js and npm +# - TypeScript +# - Vite +# - @capacitor-community/electron +# +# Usage: ./experiment.sh +# +# Exit Codes: +# 1 - Required command not found +# 2 - TypeScript installation failed +# 3 - Build process failed +# 4 - Capacitor sync failed +# 5 - Electron start failed + +# Exit on any error +set -e + +# ANSI color codes for better output formatting +readonly RED='\033[0;31m' +readonly GREEN='\033[0;32m' +readonly YELLOW='\033[1;33m' +readonly BLUE='\033[0;34m' +readonly NC='\033[0m' # No Color + +# Logging functions +log_info() { + echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] [INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] [SUCCESS]${NC} $1" +} + +log_warn() { + echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] [WARN]${NC} $1" +} + +log_error() { + echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR]${NC} $1" +} + +# Function to check if a command exists +check_command() { + if ! command -v "$1" &> /dev/null; then + log_error "$1 is required but not installed." + exit 1 + fi + log_info "Found $1: $(command -v "$1")" +} + +# Function to measure and log execution time +measure_time() { + local start_time=$(date +%s) + "$@" + local end_time=$(date +%s) + local duration=$((end_time - start_time)) + log_success "Completed in ${duration} seconds" +} + +# Print build header +echo -e "\n${BLUE}=== TimeSafari Electron Build Process ===${NC}\n" +log_info "Starting build process at $(date)" + +# Check required commands +log_info "Checking required dependencies..." +check_command node +check_command npm +check_command git + +# Create application data directory +log_info "Setting up application directories..." +mkdir -p ~/.local/share/TimeSafari/timesafari + +# Clean up previous builds +log_info "Cleaning previous builds..." +rm -rf dist* || log_warn "No previous builds to clean" + +# Set environment variables for the build +log_info "Configuring build environment..." +export VITE_PLATFORM=electron +export VITE_PWA_ENABLED=false +export VITE_DISABLE_PWA=true +export DEBUG_MIGRATIONS=0 + +# Ensure TypeScript is installed +log_info "Verifying TypeScript installation..." +if [ ! -f "./node_modules/.bin/tsc" ]; then + log_info "Installing TypeScript..." + if ! npm install --save-dev typescript@~5.2.2; then + log_error "TypeScript installation failed!" + exit 2 + fi + # Verify installation + if [ ! -f "./node_modules/.bin/tsc" ]; then + log_error "TypeScript installation verification failed!" + exit 2 + fi + log_success "TypeScript installed successfully" +else + log_info "TypeScript already installed" +fi + +# Get git hash for versioning +GIT_HASH=$(git log -1 --pretty=format:%h) +log_info "Using git hash: ${GIT_HASH}" + +# Build web assets +log_info "Building web assets with Vite..." +if ! measure_time env VITE_GIT_HASH="$GIT_HASH" npx vite build --config vite.config.app.electron.mts --mode electron; then + log_error "Web asset build failed!" + exit 3 +fi + +# Sync with Capacitor +log_info "Syncing with Capacitor..." +if ! measure_time npx cap sync electron; then + log_error "Capacitor sync failed!" + exit 4 +fi + +# Restore capacitor config +log_info "Restoring capacitor config..." +if ! git checkout electron/capacitor.config.json; then + log_error "Failed to restore capacitor config!" + exit 4 +fi + +# Start Electron +log_info "Starting Electron..." +cd electron/ +if ! measure_time npm run electron:start; then + log_error "Electron start failed!" + exit 5 +fi + +# Print build summary +log_success "Build and start completed successfully!" +echo -e "\n${GREEN}=== End of Build Process ===${NC}\n" + +# Exit with success +exit 0 \ No newline at end of file diff --git a/index.html b/index.html index ea64cda3..1b928e13 100644 --- a/index.html +++ b/index.html @@ -21,11 +21,10 @@ case 'electron': import('./src/main.electron.ts'); break; - case 'pywebview': - import('./src/main.pywebview.ts'); - break; + case 'web': default: import('./src/main.web.ts'); + break; } diff --git a/package.json b/package.json index a2152348..a58f6672 100644 --- a/package.json +++ b/package.json @@ -12,38 +12,32 @@ "lint": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src", "lint-fix": "eslint --ext .js,.ts,.vue --ignore-path .gitignore --fix src", "prebuild": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src && node sw_combine.js && node scripts/copy-wasm.js", - "test:all": "npm run test:prerequisites && npm run build && npm run test:web && npm run test:mobile", + "test:all": "./scripts/test-all.sh", "test:prerequisites": "node scripts/check-prerequisites.js", "test:web": "npx playwright test -c playwright.config-local.ts --trace on", - "test:mobile": "npm run build:capacitor && npm run test:android && npm run test:ios", + "test:mobile": "./scripts/test-mobile.sh", "test:android": "node scripts/test-android.js", "test:ios": "node scripts/test-ios.js", "check:android-device": "adb devices | grep -w 'device' || (echo 'No Android device connected' && exit 1)", "check:ios-device": "xcrun xctrace list devices 2>&1 | grep -w 'Booted' || (echo 'No iOS simulator running' && exit 1)", "clean:electron": "rimraf dist-electron", - "build:pywebview": "vite build --config vite.config.pywebview.mts", - "build:electron": "npm run clean:electron && tsc -p tsconfig.electron.json && vite build --config vite.config.electron.mts && node scripts/build-electron.js", + "build:electron": "./scripts/build-electron.sh", "build:capacitor": "vite build --mode capacitor --config vite.config.capacitor.mts", "build:web": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build --config vite.config.web.mts", - "electron:dev": "npm run build && electron .", + "electron:dev": "./scripts/electron-dev.sh", "electron:start": "electron .", "clean:android": "adb uninstall app.timesafari.app || true", - "build:android": "npm run clean:android && rm -rf dist && npm run build:web && npm run build:capacitor && cd android && ./gradlew clean && ./gradlew assembleDebug && cd .. && npx cap sync android && npx capacitor-assets generate --android && npx cap open android", - "electron:build-linux": "npm run build:electron && electron-builder --linux AppImage", - "electron:build-linux-deb": "npm run build:electron && electron-builder --linux deb", - "electron:build-linux-prod": "NODE_ENV=production npm run build:electron && electron-builder --linux AppImage", + "build:android": "./scripts/build-android.sh", + "electron:build-linux": "./scripts/build-electron-linux.sh", + "electron:build-linux-deb": "./scripts/build-electron-linux.sh deb", + "electron:build-linux-prod": "./scripts/build-electron-linux.sh prod", "build:electron-prod": "NODE_ENV=production npm run build:electron", - "pywebview:dev": "vite build --config vite.config.pywebview.mts && .venv/bin/python src/pywebview/main.py", - "pywebview:build": "vite build --config vite.config.pywebview.mts && .venv/bin/python src/pywebview/main.py", - "pywebview:package-linux": "vite build --mode pywebview --config vite.config.pywebview.mts && .venv/bin/python -m PyInstaller --name TimeSafari --add-data 'dist:www' src/pywebview/main.py", - "pywebview:package-win": "vite build --mode pywebview --config vite.config.pywebview.mts && .venv/Scripts/python -m PyInstaller --name TimeSafari --add-data 'dist;www' src/pywebview/main.py", - "pywebview:package-mac": "vite build --mode pywebview --config vite.config.pywebview.mts && .venv/bin/python -m PyInstaller --name TimeSafari --add-data 'dist:www' src/pywebview/main.py", "fastlane:ios:beta": "cd ios && fastlane beta", "fastlane:ios:release": "cd ios && fastlane release", "fastlane:android:beta": "cd android && fastlane beta", "fastlane:android:release": "cd android && fastlane release", - "electron:build-mac": "npm run build:electron-prod && electron-builder --mac", - "electron:build-mac-universal": "npm run build:electron-prod && electron-builder --mac --universal" + "electron:build-mac": "./scripts/build-electron-mac.sh", + "electron:build-mac-universal": "./scripts/build-electron-mac.sh universal" }, "dependencies": { "@capacitor-community/sqlite": "6.0.2", diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 8f8e8554..00000000 --- a/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -eth_keys -pywebview -pyinstaller>=6.12.0 -setuptools>=69.0.0 # Required for distutils for electron-builder on macOS -# For development -watchdog>=3.0.0 # For file watching support \ No newline at end of file diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 00000000..e67d21e1 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,285 @@ +# TimeSafari Build Scripts + +This directory contains unified build and test scripts for the TimeSafari application. All scripts use a common utilities library to eliminate redundancy and provide consistent logging, error handling, timing, and environment variable management. + +## Architecture + +### Common Utilities (`common.sh`) + +The `common.sh` script provides shared functionality used by all build scripts: + +- **Logging Functions**: `log_info`, `log_success`, `log_warn`, `log_error`, `log_debug`, `log_step` +- **Timing**: `measure_time` for execution time tracking +- **Headers/Footers**: `print_header`, `print_footer` for consistent output formatting +- **Validation**: `check_command`, `check_directory`, `check_file`, `check_venv` +- **Execution**: `safe_execute` for error-handled command execution +- **Utilities**: `get_git_hash`, `clean_build_artifacts`, `validate_env_vars` +- **Environment Management**: `setup_build_env`, `setup_app_directories`, `load_env_file`, `print_env_vars` +- **CLI**: `parse_args`, `print_usage` for command-line argument handling + +### Environment Variable Management + +All scripts automatically handle environment variables for different build types: + +#### Build Types and Environment Variables + +| Platform | Mode | PWA Enabled | Native Features | Build Script | +|----------|------|-------------|-----------------|--------------| +| `web` | web | true | false | `build-web.sh` | +| `capacitor` | capacitor | false | true | `build-capacitor.sh` | +| `electron` | electron | false | true | `build-electron.sh` | + +#### Automatic Environment Setup + +Each script automatically: +1. **Sets platform-specific variables** based on build type +2. **Gets git hash** for versioning (`VITE_GIT_HASH`) +3. **Creates application directories** (`~/.local/share/TimeSafari/timesafari`) +4. **Loads .env file** if it exists +5. **Validates required variables** when needed + +#### Environment Functions + +- `setup_build_env(build_type, production)` - Sets environment for specific build type +- `setup_app_directories()` - Creates necessary application directories +- `load_env_file(filename)` - Loads variables from .env file +- `print_env_vars(prefix)` - Displays current environment variables +- `validate_env_vars(var1, var2, ...)` - Validates required variables exist + +### Script Structure + +All scripts follow this unified pattern: + +```bash +#!/bin/bash +# script-name.sh +# Author: Matthew Raymer +# Description: Brief description of what the script does +# +# Exit Codes: List of exit codes and their meanings +# Usage: ./scripts/script-name.sh [options] + +# Exit on any error +set -e + +# Source common utilities +source "$(dirname "$0")/common.sh" + +# Parse command line arguments +parse_args "$@" + +# Print header +print_header "Script Title" +log_info "Starting process at $(date)" + +# Setup environment (automatic) +setup_build_env "build_type" +setup_app_directories +load_env_file ".env" + +# Execute steps with safe_execute +safe_execute "Step description" "command to execute" || exit 1 + +# Print footer +print_footer "Script Title" +exit 0 +``` + +## Available Scripts + +### Test Scripts + +- **`test-all.sh`**: Comprehensive test suite (prerequisites, build, web tests, mobile tests) +- **`test-mobile.sh`**: Mobile test suite (Capacitor build, Android tests, iOS tests) +- **`test-common.sh`**: Test script to verify common utilities work correctly +- **`test-env.sh`**: Test script to verify environment variable handling + +### Build Scripts + +- **`build-electron.sh`**: Complete Electron build process +- **`build-android.sh`**: Complete Android build process +- **`build-electron-linux.sh`**: Linux Electron packaging (AppImage, .deb) +- **`build-electron-mac.sh`**: macOS Electron packaging (standard, universal) + +### Development Scripts + +- **`electron-dev.sh`**: Electron development workflow + +## Benefits of Unification + +### Before (Redundant) +```bash +# Each script had 50+ lines of duplicate code: +readonly RED='\033[0;31m' +readonly GREEN='\033[0;32m' +# ... 40+ more lines of duplicate logging functions +log_info "Step 1/4: Doing something..." +if ! measure_time some_command; then + log_error "Step failed!" + exit 1 +fi +# Manual environment variable setup +export VITE_PLATFORM=electron +export VITE_PWA_ENABLED=false +# ... more manual exports +``` + +### After (Unified) +```bash +# Each script is now ~20 lines of focused logic: +source "$(dirname "$0")/common.sh" +print_header "Script Title" +setup_build_env "electron" # Automatic environment setup +safe_execute "Step description" "some_command" || exit 1 +print_footer "Script Title" +``` + +## Usage Examples + +### Running Tests +```bash +# Run all tests +./scripts/test-all.sh + +# Run mobile tests only +./scripts/test-mobile.sh + +# Run with verbose logging +./scripts/test-all.sh --verbose + +# Show environment variables +./scripts/test-env.sh --env +``` + +### Building Applications +```bash +# Build Electron +./scripts/build-electron.sh + +# Build Android +./scripts/build-android.sh + +# Build Linux package +./scripts/build-electron-linux.sh deb + +# Build universal Mac package +./scripts/build-electron-mac.sh universal + +# Show environment variables for build +./scripts/build-electron.sh --env +``` + +### Development Workflows +```bash +# Start Electron development +./scripts/electron-dev.sh +``` + +## Environment Variable Features + +### Automatic Setup +All scripts automatically configure the correct environment variables for their build type: + +```bash +# Electron builds automatically get: +export VITE_PLATFORM=electron +export VITE_PWA_ENABLED=false +export VITE_DISABLE_PWA=true +export DEBUG_MIGRATIONS=0 +export VITE_GIT_HASH= + +# Production builds also get: +export NODE_ENV=production +``` + +### .env File Support +Scripts automatically load variables from `.env` files if they exist: + +```bash +# .env file example: +VITE_API_URL=https://api.example.com +VITE_DEBUG=true +CUSTOM_VAR=value +``` + +### Environment Validation +Required environment variables can be validated: + +```bash +# In your script +validate_env_vars "VITE_API_URL" "VITE_DEBUG" || exit 1 +``` + +### Environment Inspection +View current environment variables with the `--env` flag: + +```bash +./scripts/build-electron.sh --env +./scripts/test-env.sh --env +``` + +## Error Handling + +All scripts use consistent error handling: + +- **Exit Codes**: Each script documents specific exit codes +- **Safe Execution**: `safe_execute` provides timing and error handling +- **Graceful Failure**: Scripts stop on first error with clear messages +- **Logging**: All operations are logged with timestamps and colors +- **Environment Validation**: Required variables are checked before execution + +## Testing + +To verify the common utilities work correctly: + +```bash +# Test all common functions +./scripts/test-common.sh + +# Test environment variable handling +./scripts/test-env.sh + +# Test with verbose logging +./scripts/test-env.sh --verbose +``` + +## Maintenance + +### Adding New Scripts + +1. Create new script following the unified pattern +2. Source `common.sh` at the top +3. Use `setup_build_env()` for environment setup +4. Use `safe_execute` for command execution +5. Document exit codes and usage +6. Make executable: `chmod +x scripts/new-script.sh` + +### Modifying Common Utilities + +1. Update `common.sh` with new functions +2. Export new functions with `export -f function_name` +3. Update this README if adding new categories +4. Test with `test-common.sh` and `test-env.sh` + +### Adding New Build Types + +1. Add new case to `setup_build_env()` function +2. Define appropriate environment variables +3. Update this README with new build type +4. Test with `test-env.sh` + +## Security Considerations + +- All scripts use `set -e` for immediate failure on errors +- Commands are executed through `safe_execute` for consistent error handling +- No direct execution of user input without validation +- Environment variables are validated when required +- .env files are loaded safely with proper parsing + +## Performance + +- Common utilities are sourced once per script execution +- Timing information is automatically collected for all operations +- Build artifacts are cleaned up automatically +- No redundant command execution or file operations +- Environment variables are set efficiently with minimal overhead \ No newline at end of file diff --git a/scripts/build-android.sh b/scripts/build-android.sh new file mode 100755 index 00000000..a5527221 --- /dev/null +++ b/scripts/build-android.sh @@ -0,0 +1,71 @@ +#!/bin/bash +# build-android.sh +# Author: Matthew Raymer +# Description: Android build script for TimeSafari application +# This script handles the complete Android build process including cleanup, +# web build, Capacitor build, Gradle build, and Android Studio launch. +# +# Exit Codes: +# 1 - Android cleanup failed +# 2 - Web build failed +# 3 - Capacitor build failed +# 4 - Gradle clean failed +# 5 - Gradle assemble failed +# 6 - Capacitor sync failed +# 7 - Asset generation failed +# 8 - Android Studio launch failed + +# Exit on any error +set -e + +# Source common utilities +source "$(dirname "$0")/common.sh" + +# Parse command line arguments +parse_args "$@" + +# Print build header +print_header "TimeSafari Android Build Process" +log_info "Starting Android build process at $(date)" + +# Setup environment for Capacitor build +setup_build_env "capacitor" + +# Setup application directories +setup_app_directories + +# Load environment from .env file if it exists +load_env_file ".env" + +# Step 1: Clean Android app +safe_execute "Cleaning Android app" "npm run clean:android" || exit 1 + +# Step 2: Clean dist directory +log_info "Cleaning dist directory..." +clean_build_artifacts "dist" + +# Step 3: Build web assets +safe_execute "Building web assets" "npm run build:web" || exit 2 + +# Step 4: Build Capacitor version +safe_execute "Building Capacitor version" "npm run build:capacitor" || exit 3 + +# Step 5: Clean Gradle build +safe_execute "Cleaning Gradle build" "cd android && ./gradlew clean && cd .." || exit 4 + +# Step 6: Assemble debug build +safe_execute "Assembling debug build" "cd android && ./gradlew assembleDebug && cd .." || exit 5 + +# Step 7: Sync with Capacitor +safe_execute "Syncing with Capacitor" "npx cap sync android" || exit 6 + +# Step 8: Generate assets and open Android Studio +safe_execute "Generating assets" "npx capacitor-assets generate --android" || exit 7 +safe_execute "Opening Android Studio" "npx cap open android" || exit 8 + +# Print build summary +log_success "Android build completed successfully!" +print_footer "Android Build" + +# Exit with success +exit 0 \ No newline at end of file diff --git a/scripts/build-electron-linux.sh b/scripts/build-electron-linux.sh new file mode 100755 index 00000000..7d7cbccf --- /dev/null +++ b/scripts/build-electron-linux.sh @@ -0,0 +1,82 @@ +#!/bin/bash +# build-electron-linux.sh +# Author: Matthew Raymer +# Description: Electron Linux build script for TimeSafari application +# This script builds Electron packages for Linux with support for different formats. +# +# Usage: ./scripts/build-electron-linux.sh [deb|prod] +# - No argument: Builds AppImage +# - deb: Builds .deb package +# - prod: Builds production AppImage +# +# Exit Codes: +# 1 - Build failed +# 2 - Invalid argument + +# Exit on any error +set -e + +# Source common utilities +source "$(dirname "$0")/common.sh" + +# Parse command line arguments +parse_args "$@" + +# Parse build type argument +BUILD_TYPE=${1:-"appimage"} +PRODUCTION=false + +case $BUILD_TYPE in + "deb") + BUILD_TARGET="deb" + log_info "Building .deb package" + ;; + "prod") + BUILD_TARGET="AppImage" + PRODUCTION=true + log_info "Building production AppImage" + ;; + "appimage"|"") + BUILD_TARGET="AppImage" + log_info "Building AppImage" + ;; + *) + log_error "Invalid build type: $BUILD_TYPE" + log_error "Usage: $0 [deb|prod]" + exit 2 + ;; +esac + +# Print build header +print_header "TimeSafari Electron Linux Build" +log_info "Starting Linux build process at $(date)" +log_info "Build type: $BUILD_TYPE" +log_info "Build target: $BUILD_TARGET" +log_info "Production mode: $PRODUCTION" + +# Setup environment for Electron build +setup_build_env "electron" "$PRODUCTION" + +# Setup application directories +setup_app_directories + +# Load environment from .env file if it exists +load_env_file ".env" + +# Step 1: Build Electron application +if [ "$PRODUCTION" = true ]; then + safe_execute "Building production Electron application" "npm run build:electron-prod" || exit 1 +else + safe_execute "Building Electron application" "npm run build:electron" || exit 1 +fi + +# Step 2: Build package +safe_execute "Building Linux package" "npx electron-builder --linux $BUILD_TARGET" || exit 1 + +# Print build summary +log_success "Linux build completed successfully!" +log_info "Package type: $BUILD_TARGET" +print_footer "Linux Build" + +# Exit with success +exit 0 \ No newline at end of file diff --git a/scripts/build-electron-mac.sh b/scripts/build-electron-mac.sh new file mode 100644 index 00000000..5504ca6d --- /dev/null +++ b/scripts/build-electron-mac.sh @@ -0,0 +1,74 @@ +#!/bin/bash +# build-electron-mac.sh +# Author: Matthew Raymer +# Description: Electron Mac build script for TimeSafari application +# This script builds Electron packages for macOS with support for universal builds. +# +# Usage: ./scripts/build-electron-mac.sh [universal] +# - No argument: Builds standard Mac package +# - universal: Builds universal Mac package (Intel + Apple Silicon) +# +# Exit Codes: +# 1 - Build failed +# 2 - Invalid argument + +# Exit on any error +set -e + +# Source common utilities +source "$(dirname "$0")/common.sh" + +# Parse command line arguments +parse_args "$@" + +# Parse build type argument +BUILD_TYPE=${1:-"standard"} +UNIVERSAL=false + +case $BUILD_TYPE in + "universal") + UNIVERSAL=true + log_info "Building universal Mac package (Intel + Apple Silicon)" + ;; + "standard"|"") + log_info "Building standard Mac package" + ;; + *) + log_error "Invalid build type: $BUILD_TYPE" + log_error "Usage: $0 [universal]" + exit 2 + ;; +esac + +# Print build header +print_header "TimeSafari Electron Mac Build" +log_info "Starting Mac build process at $(date)" +log_info "Build type: $BUILD_TYPE" +log_info "Universal build: $UNIVERSAL" + +# Setup environment for Electron build (production mode for packaging) +setup_build_env "electron" "true" + +# Setup application directories +setup_app_directories + +# Load environment from .env file if it exists +load_env_file ".env" + +# Step 1: Build Electron application +safe_execute "Building Electron application" "npm run build:electron-prod" || exit 1 + +# Step 2: Build package +if [ "$UNIVERSAL" = true ]; then + safe_execute "Building universal Mac package" "npx electron-builder --mac --universal" || exit 1 +else + safe_execute "Building Mac package" "npx electron-builder --mac" || exit 1 +fi + +# Print build summary +log_success "Mac build completed successfully!" +log_info "Package type: $([ "$UNIVERSAL" = true ] && echo "Universal" || echo "Standard")" +print_footer "Mac Build" + +# Exit with success +exit 0 \ No newline at end of file diff --git a/scripts/build-electron.sh b/scripts/build-electron.sh new file mode 100755 index 00000000..5aa36ac4 --- /dev/null +++ b/scripts/build-electron.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# build-electron.sh +# Author: Matthew Raymer +# Description: Electron build script for TimeSafari application +# This script handles the complete Electron build process including cleanup, +# TypeScript compilation, Vite build, and Electron-specific setup. +# +# Exit Codes: +# 1 - Cleanup failed +# 2 - TypeScript compilation failed +# 3 - Vite build failed +# 4 - Electron build script failed + +# Exit on any error +set -e + +# Source common utilities +source "$(dirname "$0")/common.sh" + +# Parse command line arguments +parse_args "$@" + +# Print build header +print_header "TimeSafari Electron Build Process" +log_info "Starting Electron build process at $(date)" + +# Setup environment for Electron build +setup_build_env "electron" + +# Setup application directories +setup_app_directories + +# Load environment from .env file if it exists +load_env_file ".env" + +# Step 1: Clean previous builds +safe_execute "Cleaning previous builds" "npm run clean:electron" || exit 1 + +# Step 2: Compile TypeScript for Electron +safe_execute "Compiling TypeScript for Electron" "npx tsc -p tsconfig.electron.json" || exit 2 + +# Step 3: Build with Vite +safe_execute "Building with Vite" "npx vite build --config vite.config.electron.mts" || exit 3 + +# Step 4: Run Electron build script +safe_execute "Running Electron build script" "node scripts/build-electron.js" || exit 4 + +# Print build summary +log_success "Electron build completed successfully!" +print_footer "Electron Build" + +# Exit with success +exit 0 \ No newline at end of file diff --git a/scripts/common.sh b/scripts/common.sh new file mode 100755 index 00000000..ab9e6c7a --- /dev/null +++ b/scripts/common.sh @@ -0,0 +1,331 @@ +#!/bin/bash +# common.sh +# Author: Matthew Raymer +# Description: Common utilities and functions for TimeSafari build scripts +# This script provides shared logging, timing, and utility functions +# that can be sourced by other build scripts to eliminate redundancy. +# +# Usage: source ./scripts/common.sh +# +# Provides: +# - Color constants +# - Logging functions (log_info, log_success, log_warn, log_error) +# - Timing function (measure_time) +# - Common utility functions +# - Environment variable management + +# ANSI color codes for better output formatting +readonly RED='\033[0;31m' +readonly GREEN='\033[0;32m' +readonly YELLOW='\033[1;33m' +readonly BLUE='\033[0;34m' +readonly PURPLE='\033[0;35m' +readonly CYAN='\033[0;36m' +readonly NC='\033[0m' # No Color + +# Logging functions +log_info() { + echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] [INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] [SUCCESS]${NC} $1" +} + +log_warn() { + echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] [WARN]${NC} $1" +} + +log_error() { + echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR]${NC} $1" +} + +log_debug() { + echo -e "${PURPLE}[$(date '+%Y-%m-%d %H:%M:%S')] [DEBUG]${NC} $1" +} + +log_step() { + echo -e "${CYAN}[$(date '+%Y-%m-%d %H:%M:%S')] [STEP]${NC} $1" +} + +# Function to measure and log execution time +measure_time() { + local start_time=$(date +%s) + "$@" + local end_time=$(date +%s) + local duration=$((end_time - start_time)) + log_success "Completed in ${duration} seconds" +} + +# Function to print section headers +print_header() { + local title="$1" + echo -e "\n${BLUE}=== $title ===${NC}\n" +} + +print_footer() { + local title="$1" + echo -e "\n${GREEN}=== $title Complete ===${NC}\n" +} + +# Function to check if a command exists +check_command() { + if ! command -v "$1" &> /dev/null; then + log_error "$1 is required but not installed." + return 1 + fi + log_debug "Found $1: $(command -v "$1")" + return 0 +} + +# Function to check if a directory exists +check_directory() { + if [ ! -d "$1" ]; then + log_error "Directory not found: $1" + return 1 + fi + log_debug "Directory exists: $1" + return 0 +} + +# Function to check if a file exists +check_file() { + if [ ! -f "$1" ]; then + log_error "File not found: $1" + return 1 + fi + log_debug "File exists: $1" + return 0 +} + +# Function to safely execute a command with error handling +safe_execute() { + local step_name="$1" + local command="$2" + + log_step "$step_name" + if ! measure_time eval "$command"; then + log_error "$step_name failed!" + return 1 + fi + return 0 +} + +# Function to check virtual environment for Python scripts +check_venv() { + if [ ! -d ".venv" ]; then + log_error "Virtual environment not found. Please create it first:" + log_error "python -m venv .venv" + log_error "source .venv/bin/activate" + log_error "pip install -r requirements.txt" + return 1 + fi + log_debug "Virtual environment found: .venv" + return 0 +} + +# Function to get git hash for versioning +get_git_hash() { + if command -v git &> /dev/null; then + git log -1 --pretty=format:%h 2>/dev/null || echo "unknown" + else + echo "unknown" + fi +} + +# Function to clean build artifacts +clean_build_artifacts() { + local artifacts=("$@") + for artifact in "${artifacts[@]}"; do + if [ -e "$artifact" ]; then + log_info "Cleaning $artifact" + rm -rf "$artifact" + fi + done +} + +# Function to validate environment variables +validate_env_vars() { + local required_vars=("$@") + local missing_vars=() + + for var in "${required_vars[@]}"; do + if [ -z "${!var}" ]; then + missing_vars+=("$var") + fi + done + + if [ ${#missing_vars[@]} -gt 0 ]; then + log_error "Missing required environment variables: ${missing_vars[*]}" + return 1 + fi + + return 0 +} + +# Function to set environment variables for different build types +setup_build_env() { + local build_type="$1" + local production="${2:-false}" + + log_info "Setting up environment for $build_type build" + + # Get git hash for versioning + local git_hash=$(get_git_hash) + export VITE_GIT_HASH="$git_hash" + log_debug "Set VITE_GIT_HASH=$git_hash" + + case $build_type in + "electron") + export VITE_PLATFORM=electron + export VITE_PWA_ENABLED=false + export VITE_DISABLE_PWA=true + export DEBUG_MIGRATIONS=0 + if [ "$production" = true ]; then + export NODE_ENV=production + log_debug "Set production mode for Electron" + fi + ;; + "capacitor") + export VITE_PLATFORM=capacitor + export VITE_PWA_ENABLED=false + export VITE_DISABLE_PWA=true + export DEBUG_MIGRATIONS=0 + ;; + "web") + export VITE_PLATFORM=web + export VITE_PWA_ENABLED=true + export VITE_DISABLE_PWA=false + export DEBUG_MIGRATIONS=0 + ;; + *) + log_warn "Unknown build type: $build_type, using default environment" + export VITE_PLATFORM=web + export VITE_PWA_ENABLED=true + export VITE_DISABLE_PWA=false + export DEBUG_MIGRATIONS=0 + ;; + esac + + # Log environment setup + log_debug "Environment variables set:" + log_debug " VITE_PLATFORM=$VITE_PLATFORM" + log_debug " VITE_PWA_ENABLED=$VITE_PWA_ENABLED" + log_debug " VITE_DISABLE_PWA=$VITE_DISABLE_PWA" + log_debug " DEBUG_MIGRATIONS=$DEBUG_MIGRATIONS" + if [ -n "$NODE_ENV" ]; then + log_debug " NODE_ENV=$NODE_ENV" + fi +} + +# Function to create application directories +setup_app_directories() { + log_info "Setting up application directories..." + + # Create TimeSafari data directory + mkdir -p ~/.local/share/TimeSafari/timesafari + + # Create build directories if they don't exist + mkdir -p dist + mkdir -p dist-electron + + log_debug "Application directories created" +} + +# Function to load environment from .env file if it exists +load_env_file() { + local env_file="$1" + + if [ -f "$env_file" ]; then + log_info "Loading environment from $env_file" + # Export variables from .env file (simple key=value format) + while IFS='=' read -r key value; do + # Skip comments and empty lines + [[ $key =~ ^#.*$ ]] && continue + [[ -z $key ]] && continue + + # Remove quotes from value if present + value=$(echo "$value" | sed 's/^["'\'']//;s/["'\'']$//') + + export "$key=$value" + log_debug "Loaded: $key=$value" + done < "$env_file" + else + log_debug "No $env_file file found" + fi +} + +# Function to print current environment variables +print_env_vars() { + local prefix="$1" + + if [ -n "$prefix" ]; then + log_info "Environment variables with prefix '$prefix':" + env | grep "^$prefix" | sort | while read -r line; do + log_debug " $line" + done + else + log_info "Current environment variables:" + env | sort | while read -r line; do + log_debug " $line" + done + fi +} + +# Function to print script usage +print_usage() { + local script_name="$1" + local usage_text="$2" + + echo "Usage: $script_name $usage_text" + echo "" + echo "Options:" + echo " -h, --help Show this help message" + echo " -v, --verbose Enable verbose logging" + echo " -e, --env Show environment variables" + echo "" +} + +# Function to parse command line arguments +parse_args() { + local args=("$@") + local verbose=false + local show_env=false + + for arg in "${args[@]}"; do + case $arg in + -h|--help) + print_usage "$0" "[options]" + exit 0 + ;; + -v|--verbose) + verbose=true + ;; + -e|--env) + show_env=true + ;; + *) + # Handle other arguments in child scripts + ;; + esac + done + + if [ "$verbose" = true ]; then + # Enable debug logging + set -x + fi + + if [ "$show_env" = true ]; then + print_env_vars "VITE_" + exit 0 + fi +} + +# Export functions for use in child scripts +export -f log_info log_success log_warn log_error log_debug log_step +export -f measure_time print_header print_footer +export -f check_command check_directory check_file +export -f safe_execute check_venv get_git_hash +export -f clean_build_artifacts validate_env_vars +export -f setup_build_env setup_app_directories load_env_file print_env_vars +export -f print_usage parse_args \ No newline at end of file diff --git a/scripts/electron-dev.sh b/scripts/electron-dev.sh new file mode 100755 index 00000000..2bd76e02 --- /dev/null +++ b/scripts/electron-dev.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# electron-dev.sh +# Author: Matthew Raymer +# Description: Electron development script for TimeSafari application +# This script builds the application and starts Electron for development. +# +# Exit Codes: +# 1 - Build failed +# 2 - Electron start failed + +# Exit on any error +set -e + +# Source common utilities +source "$(dirname "$0")/common.sh" + +# Parse command line arguments +parse_args "$@" + +# Print dev header +print_header "TimeSafari Electron Development" +log_info "Starting Electron development at $(date)" + +# Setup environment for Electron development +setup_build_env "electron" + +# Setup application directories +setup_app_directories + +# Load environment from .env file if it exists +load_env_file ".env" + +# Step 1: Build the application +safe_execute "Building application" "npm run build" || exit 1 + +# Step 2: Start Electron +safe_execute "Starting Electron" "electron ." || exit 2 + +# Print dev summary +log_success "Electron development session ended" +print_footer "Electron Development" + +# Exit with success +exit 0 \ No newline at end of file diff --git a/scripts/test-all.sh b/scripts/test-all.sh new file mode 100755 index 00000000..d7b78e98 --- /dev/null +++ b/scripts/test-all.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# test-all.sh +# Author: Matthew Raymer +# Description: Comprehensive test suite for TimeSafari application +# This script runs all tests including prerequisites, web tests, and mobile tests +# with proper error handling and logging. +# +# Exit Codes: +# 1 - Prerequisites check failed +# 2 - Build failed +# 3 - Web tests failed +# 4 - Mobile tests failed + +# Exit on any error +set -e + +# Source common utilities +source "$(dirname "$0")/common.sh" + +# Parse command line arguments +parse_args "$@" + +# Print test header +print_header "TimeSafari Test Suite" +log_info "Starting comprehensive test suite at $(date)" + +# Step 1: Check prerequisites +safe_execute "Checking prerequisites" "npm run test:prerequisites" || exit 1 + +# Step 2: Build the application +safe_execute "Building application" "npm run build" || exit 2 + +# Step 3: Run web tests +safe_execute "Running web tests" "npm run test:web" || exit 3 + +# Step 4: Run mobile tests +safe_execute "Running mobile tests" "npm run test:mobile" || exit 4 + +# Print test summary +log_success "All tests completed successfully!" +print_footer "Test Suite" + +# Exit with success +exit 0 \ No newline at end of file diff --git a/scripts/test-common.sh b/scripts/test-common.sh new file mode 100755 index 00000000..270ec750 --- /dev/null +++ b/scripts/test-common.sh @@ -0,0 +1,74 @@ +#!/bin/bash +# test-common.sh +# Author: Matthew Raymer +# Description: Test script to verify common utilities work correctly +# This script tests the common.sh utilities to ensure they function properly. + +# Exit on any error +set -e + +# Source common utilities +source "$(dirname "$0")/common.sh" + +# Parse command line arguments +parse_args "$@" + +# Print test header +print_header "Common Utilities Test" +log_info "Testing common utilities at $(date)" + +# Test logging functions +log_info "Testing info logging" +log_success "Testing success logging" +log_warn "Testing warning logging" +log_error "Testing error logging (this is expected)" +log_debug "Testing debug logging" +log_step "Testing step logging" + +# Test timing function +log_info "Testing timing function..." +measure_time sleep 1 + +# Test command checking +log_info "Testing command checking..." +if check_command "echo"; then + log_success "echo command found" +else + log_error "echo command not found" +fi + +# Test directory checking +log_info "Testing directory checking..." +if check_directory "scripts"; then + log_success "scripts directory found" +else + log_error "scripts directory not found" +fi + +# Test file checking +log_info "Testing file checking..." +if check_file "scripts/common.sh"; then + log_success "common.sh file found" +else + log_error "common.sh file not found" +fi + +# Test git hash function +log_info "Testing git hash function..." +GIT_HASH=$(get_git_hash) +log_info "Git hash: $GIT_HASH" + +# Test safe execute +log_info "Testing safe execute..." +safe_execute "Testing safe execute" "echo 'Hello from safe_execute'" + +# Test build artifact cleaning +log_info "Testing build artifact cleaning..." +clean_build_artifacts "test-file-1" "test-file-2" + +# Print test summary +log_success "All common utilities tests completed successfully!" +print_footer "Common Utilities Test" + +# Exit with success +exit 0 \ No newline at end of file diff --git a/scripts/test-env.sh b/scripts/test-env.sh new file mode 100755 index 00000000..5220d871 --- /dev/null +++ b/scripts/test-env.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# test-env.sh +# Author: Matthew Raymer +# Description: Test script to verify environment variable handling +# This script tests the environment variable setup functions. + +# Exit on any error +set -e + +# Source common utilities +source "$(dirname "$0")/common.sh" + +# Parse command line arguments +parse_args "$@" + +# Print test header +print_header "Environment Variable Test" +log_info "Testing environment variable handling at $(date)" + +# Test 1: Electron environment +log_info "Test 1: Setting up Electron environment..." +setup_build_env "electron" +print_env_vars "VITE_" + +# Test 2: Capacitor environment +log_info "Test 2: Setting up Capacitor environment..." +setup_build_env "capacitor" +print_env_vars "VITE_" + +# Test 3: Web environment +log_info "Test 3: Setting up Web environment..." +setup_build_env "web" +print_env_vars "VITE_" + +# Test 4: Production Electron environment +log_info "Test 4: Setting up Production Electron environment..." +setup_build_env "electron" "true" +print_env_vars "VITE_" +print_env_vars "NODE_ENV" + +# Test 5: Application directories +log_info "Test 5: Setting up application directories..." +setup_app_directories + +# Test 6: Load .env file (if it exists) +log_info "Test 6: Loading .env file..." +load_env_file ".env" + +# Test 7: Git hash +log_info "Test 7: Getting git hash..." +GIT_HASH=$(get_git_hash) +log_info "Git hash: $GIT_HASH" + +# Print test summary +log_success "All environment variable tests completed successfully!" +print_footer "Environment Variable Test" + +# Exit with success +exit 0 \ No newline at end of file diff --git a/scripts/test-mobile.sh b/scripts/test-mobile.sh new file mode 100755 index 00000000..41ad11c8 --- /dev/null +++ b/scripts/test-mobile.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# test-mobile.sh +# Author: Matthew Raymer +# Description: Mobile test suite for TimeSafari application +# This script builds the Capacitor version and runs Android and iOS tests +# with proper error handling and logging. +# +# Exit Codes: +# 1 - Capacitor build failed +# 2 - Android tests failed +# 3 - iOS tests failed + +# Exit on any error +set -e + +# Source common utilities +source "$(dirname "$0")/common.sh" + +# Parse command line arguments +parse_args "$@" + +# Print test header +print_header "TimeSafari Mobile Test Suite" +log_info "Starting mobile test suite at $(date)" + +# Step 1: Build Capacitor version +safe_execute "Building Capacitor version" "npm run build:capacitor" || exit 1 + +# Step 2: Run Android tests +safe_execute "Running Android tests" "npm run test:android" || exit 2 + +# Step 3: Run iOS tests +safe_execute "Running iOS tests" "npm run test:ios" || exit 3 + +# Print test summary +log_success "Mobile test suite completed successfully!" +print_footer "Mobile Test Suite" + +# Exit with success +exit 0 \ No newline at end of file diff --git a/src/main.pywebview.ts b/src/main.pywebview.ts deleted file mode 100644 index 3e1e492f..00000000 --- a/src/main.pywebview.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { initializeApp } from "./main.common"; - -const app = initializeApp(); -app.mount("#app"); diff --git a/src/pywebview/main.py b/src/pywebview/main.py deleted file mode 100644 index 007d8c9d..00000000 --- a/src/pywebview/main.py +++ /dev/null @@ -1,59 +0,0 @@ -import webview -import os -import sys -from http.server import HTTPServer, SimpleHTTPRequestHandler -import threading - -def get_dist_path(): - if getattr(sys, 'frozen', False): - base_path = sys._MEIPASS - else: - base_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - - dist_path = os.path.join(base_path, 'dist') - print(f"Dist path: {dist_path}") - return dist_path - -class CustomHandler(SimpleHTTPRequestHandler): - def __init__(self, *args, **kwargs): - dist_dir = get_dist_path() - print(f"Serving from directory: {dist_dir}") - super().__init__(*args, directory=dist_dir, **kwargs) - - def log_message(self, format, *args): - # Override to show more detailed logging - print(f"Request: {format%args}") - if hasattr(self, 'path'): - print(f"Requested path: {self.path}") - full_path = os.path.join(self.directory, self.path.lstrip('/')) - print(f"Full path: {full_path}") - print(f"File exists: {os.path.exists(full_path)}") - if self.path.endswith('.html'): - print(f"HTML content: {open(full_path).read()[:200]}...") - -def run_server(port=8000): - server_address = ('localhost', port) - httpd = HTTPServer(server_address, CustomHandler) - print(f"Serving files from {get_dist_path()} at http://localhost:{port}") - httpd.serve_forever() - -def main(): - dist_path = get_dist_path() - - # Start local server - server_thread = threading.Thread(target=run_server) - server_thread.daemon = True - server_thread.start() - - # Create window using local server - window = webview.create_window( - 'TimeSafari', - url='http://localhost:8000', - width=1200, - height=800 - ) - - webview.start(debug=True) - -if __name__ == '__main__': - main() \ No newline at end of file diff --git a/src/services/PlatformServiceFactory.ts b/src/services/PlatformServiceFactory.ts index f5e34fa2..12af421c 100644 --- a/src/services/PlatformServiceFactory.ts +++ b/src/services/PlatformServiceFactory.ts @@ -2,7 +2,6 @@ import { PlatformService } from "./PlatformService"; import { WebPlatformService } from "./platforms/WebPlatformService"; import { CapacitorPlatformService } from "./platforms/CapacitorPlatformService"; import { ElectronPlatformService } from "./platforms/ElectronPlatformService"; -import { PyWebViewPlatformService } from "./platforms/PyWebViewPlatformService"; /** * Factory class for creating platform-specific service implementations. @@ -12,7 +11,6 @@ import { PyWebViewPlatformService } from "./platforms/PyWebViewPlatformService"; * environment variable. Supported platforms are: * - capacitor: Mobile platform using Capacitor * - electron: Desktop platform using Electron - * - pywebview: Python WebView implementation * - web: Default web platform (fallback) * * @example @@ -44,9 +42,6 @@ export class PlatformServiceFactory { case "electron": PlatformServiceFactory.instance = new ElectronPlatformService(); break; - case "pywebview": - PlatformServiceFactory.instance = new PyWebViewPlatformService(); - break; case "web": default: PlatformServiceFactory.instance = new WebPlatformService(); diff --git a/src/services/platforms/PyWebViewPlatformService.ts b/src/services/platforms/PyWebViewPlatformService.ts deleted file mode 100644 index 5caf7b3a..00000000 --- a/src/services/platforms/PyWebViewPlatformService.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { - ImageResult, - PlatformService, - PlatformCapabilities, -} from "../PlatformService"; -import { logger } from "../../utils/logger"; -import { QueryExecResult } from "@/interfaces/database"; - -/** - * Platform service implementation for PyWebView platform. - * Note: This is a placeholder implementation with most methods currently unimplemented. - * Implements the PlatformService interface but throws "Not implemented" errors for most operations. - * - * @remarks - * This service is intended for Python-based desktop applications using pywebview. - * Future implementations should provide: - * - Integration with Python backend file operations - * - System camera access through Python - * - Native system dialogs via pywebview - * - Python-JavaScript bridge functionality - */ -export class PyWebViewPlatformService implements PlatformService { - /** - * Gets the capabilities of the PyWebView platform - * @returns Platform capabilities object - */ - getCapabilities(): PlatformCapabilities { - return { - hasFileSystem: false, // Not implemented yet - hasCamera: false, // Not implemented yet - isMobile: false, - isIOS: false, - hasFileDownload: false, // Not implemented yet - needsFileHandlingInstructions: false, - }; - } - - /** - * Reads a file using the Python backend. - * @param _path - Path to the file to read - * @returns Promise that should resolve to file contents - * @throws Error with "Not implemented" message - * @todo Implement file reading through pywebview's Python-JavaScript bridge - */ - async readFile(_path: string): Promise { - throw new Error("Not implemented"); - } - - /** - * Writes content to a file using the Python backend. - * @param _path - Path where to write the file - * @param _content - Content to write to the file - * @throws Error with "Not implemented" message - * @todo Implement file writing through pywebview's Python-JavaScript bridge - */ - async writeFile(_path: string, _content: string): Promise { - throw new Error("Not implemented"); - } - - /** - * Deletes a file using the Python backend. - * @param _path - Path to the file to delete - * @throws Error with "Not implemented" message - * @todo Implement file deletion through pywebview's Python-JavaScript bridge - */ - async deleteFile(_path: string): Promise { - throw new Error("Not implemented"); - } - - /** - * Lists files in the specified directory using the Python backend. - * @param _directory - Path to the directory to list - * @returns Promise that should resolve to array of filenames - * @throws Error with "Not implemented" message - * @todo Implement directory listing through pywebview's Python-JavaScript bridge - */ - async listFiles(_directory: string): Promise { - throw new Error("Not implemented"); - } - - /** - * Should open system camera through Python backend. - * @returns Promise that should resolve to captured image data - * @throws Error with "Not implemented" message - * @todo Implement camera access using Python's camera libraries - */ - async takePicture(): Promise { - logger.error("takePicture not implemented in PyWebView platform"); - throw new Error("Not implemented"); - } - - /** - * Should open system file picker through pywebview. - * @returns Promise that should resolve to selected image data - * @throws Error with "Not implemented" message - * @todo Implement file picker using pywebview's file dialog API - */ - async pickImage(): Promise { - logger.error("pickImage not implemented in PyWebView platform"); - throw new Error("Not implemented"); - } - - /** - * Should handle deep link URLs through the Python backend. - * @param _url - The deep link URL to handle - * @throws Error with "Not implemented" message - * @todo Implement deep link handling using Python's URL handling capabilities - */ - async handleDeepLink(_url: string): Promise { - logger.error("handleDeepLink not implemented in PyWebView platform"); - throw new Error("Not implemented"); - } - - dbQuery(sql: string, params?: unknown[]): Promise { - throw new Error("Not implemented for " + sql + " with params " + params); - } - dbExec( - sql: string, - params?: unknown[], - ): Promise<{ changes: number; lastId?: number }> { - throw new Error("Not implemented for " + sql + " with params " + params); - } - - /** - * Should write and share a file using the Python backend. - * @param _fileName - Name of the file to write and share - * @param _content - Content to write to the file - * @throws Error with "Not implemented" message - * @todo Implement file writing and sharing through pywebview's Python-JavaScript bridge - */ - async writeAndShareFile(_fileName: string, _content: string): Promise { - logger.error("writeAndShareFile not implemented in PyWebView platform"); - throw new Error("Not implemented"); - } -} diff --git a/src/services/platforms/WebPlatformService.ts b/src/services/platforms/WebPlatformService.ts index 6f66367d..51ce5238 100644 --- a/src/services/platforms/WebPlatformService.ts +++ b/src/services/platforms/WebPlatformService.ts @@ -324,14 +324,6 @@ export class WebPlatformService implements PlatformService { return false; } - /** - * Checks if running on PyWebView platform. - * @returns false, as this is not PyWebView - */ - isPyWebView(): boolean { - return false; - } - /** * Checks if running on web platform. * @returns true, as this is the web implementation diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index bae70eeb..3507591e 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -255,7 +255,7 @@ Raymer * @version 1.0.0 */ - +
{{ apiServer }}
    { const appConfig = await loadAppConfig(); const isElectron = mode === "electron"; const isCapacitor = mode === "capacitor"; - const isPyWebView = mode === "pywebview"; + const isNative = isElectron || isCapacitor; // Explicitly set platform and disable PWA for Electron process.env.VITE_PLATFORM = mode; process.env.VITE_PWA_ENABLED = isElectron ? 'false' : 'true'; process.env.VITE_DISABLE_PWA = isElectron ? 'true' : 'false'; - if (isElectron || isPyWebView || isCapacitor) { + if (isElectron || isCapacitor) { process.env.VITE_PWA_ENABLED = 'false'; } return { - base: isElectron || isPyWebView ? "./" : "/", + base: isElectron ? "./" : "/", plugins: [vue()], server: { port: parseInt(process.env.VITE_PORT || "8080"), diff --git a/vite.config.pywebview.mts b/vite.config.pywebview.mts deleted file mode 100644 index 81892137..00000000 --- a/vite.config.pywebview.mts +++ /dev/null @@ -1,4 +0,0 @@ -import { defineConfig } from "vite"; -import { createBuildConfig } from "./vite.config.common.mts"; - -export default defineConfig(async () => createBuildConfig('pywebview')); \ No newline at end of file