From 2628a1c714ad27c20c05442ef37d71a3cd7cf350 Mon Sep 17 00:00:00 2001 From: Michal Urbanek Date: Thu, 8 Jan 2026 14:23:56 +0100 Subject: [PATCH 1/2] chore: Secrets handling --- .github/CODEOWNERS | 2 +- .../android_firebase_app_distribution_all.yml | 90 ------ ...roid_firebase_app_distribution_develop.yml | 27 +- ...d_firebase_app_distribution_production.yml | 28 +- ...roid_firebase_app_distribution_staging.yml | 78 +++++ .../android_play_store_distribution.yml | 23 +- .../flutter_project_codecheck_android.yml | 19 +- .gitignore | 6 +- .sops.yaml | 14 + README.md | 42 ++- android/.gitignore | 5 + android/app/build.gradle | 12 +- android/app/google-services.json | 274 ----------------- android/extras/keystore/README.md | 18 -- android/extras/keystore/release.keystore | Bin 2762 -> 0 bytes android/extras/keystore/release.properties | 4 - android/extras/keystores/README.md | 34 +++ .../{keystore => keystores}/debug.keystore | Bin .../{keystore => keystores}/debug.properties | 2 +- android/settings.gradle | 2 - .../GoogleService-Info-develop.plist.enc | 14 + .../GoogleService-Info-production.plist.enc | 14 + .../GoogleService-Info-staging.plist.enc | 14 + extras/secrets/age_key.txt | 3 + extras/secrets/env-development.enc | 12 + extras/secrets/env-production.enc | 12 + extras/secrets/env-staging.enc | 12 + extras/secrets/env.enc | 10 + extras/secrets/google-services.json.enc | 286 ++++++++++++++++++ extras/secrets/release.keystore.enc | 14 + extras/secrets/release.properties.enc | 14 + extras/secrets/tools/clean-secrets.sh | 16 + extras/secrets/tools/decode-secrets.sh | 36 +++ extras/secrets/tools/decrypt-secrets.sh | 57 ++++ extras/secrets/tools/encrypt-secrets.sh | 28 ++ extras/secrets/tools/load-secrets.sh | 30 ++ flutter.code-workspace | 10 +- ios/.gitignore | 4 + ios/Flutter/Debug.xcconfig | 1 + ios/Flutter/DevelopDebug.xcconfig | 2 + ios/Flutter/DevelopRelease.xcconfig | 2 + ios/Flutter/ProductionDebug.xcconfig | 2 + ios/Flutter/ProductionRelease.xcconfig | 2 + ios/Flutter/Release.xcconfig | 1 + ios/Flutter/StagingDebug.xcconfig | 2 + ios/Flutter/StagingRelease.xcconfig | 2 + ios/Runner.xcodeproj/project.pbxproj | 34 ++- .../xcshareddata/xcschemes/develop.xcscheme | 6 +- .../xcschemes/production.xcscheme | 6 +- .../xcshareddata/xcschemes/staging.xcscheme | 6 +- ios/Runner/Info.plist | 6 +- ios/config/develop/GoogleService-Info.plist | 34 --- .../production/GoogleService-Info.plist | 34 --- ios/config/staging/GoogleService-Info.plist | 34 --- lib/app/configuration/configuration.dart | 39 +-- .../configuration/develop/config_develop.dart | 10 - .../production/config_production.dart | 10 - .../configuration/staging/config_staging.dart | 10 - lib/app/setup/setup_app.dart | 45 +-- .../provider/firebase_messaging_service.dart | 4 +- .../landing/force_update_page_content.dart | 5 +- makefile | 15 +- pubspec.lock | 24 +- pubspec.yaml | 5 + 64 files changed, 960 insertions(+), 647 deletions(-) delete mode 100644 .github/workflows/android_firebase_app_distribution_all.yml create mode 100644 .github/workflows/android_firebase_app_distribution_staging.yml create mode 100644 .sops.yaml delete mode 100644 android/app/google-services.json delete mode 100644 android/extras/keystore/README.md delete mode 100644 android/extras/keystore/release.keystore delete mode 100644 android/extras/keystore/release.properties create mode 100644 android/extras/keystores/README.md rename android/extras/{keystore => keystores}/debug.keystore (100%) rename android/extras/{keystore => keystores}/debug.properties (55%) create mode 100644 extras/secrets/GoogleService-Info-develop.plist.enc create mode 100644 extras/secrets/GoogleService-Info-production.plist.enc create mode 100644 extras/secrets/GoogleService-Info-staging.plist.enc create mode 100644 extras/secrets/age_key.txt create mode 100644 extras/secrets/env-development.enc create mode 100644 extras/secrets/env-production.enc create mode 100644 extras/secrets/env-staging.enc create mode 100644 extras/secrets/env.enc create mode 100644 extras/secrets/google-services.json.enc create mode 100644 extras/secrets/release.keystore.enc create mode 100644 extras/secrets/release.properties.enc create mode 100644 extras/secrets/tools/clean-secrets.sh create mode 100755 extras/secrets/tools/decode-secrets.sh create mode 100755 extras/secrets/tools/decrypt-secrets.sh create mode 100755 extras/secrets/tools/encrypt-secrets.sh create mode 100755 extras/secrets/tools/load-secrets.sh create mode 100644 ios/Flutter/DevelopDebug.xcconfig create mode 100644 ios/Flutter/DevelopRelease.xcconfig create mode 100644 ios/Flutter/ProductionDebug.xcconfig create mode 100644 ios/Flutter/ProductionRelease.xcconfig create mode 100644 ios/Flutter/StagingDebug.xcconfig create mode 100644 ios/Flutter/StagingRelease.xcconfig delete mode 100644 ios/config/develop/GoogleService-Info.plist delete mode 100644 ios/config/production/GoogleService-Info.plist delete mode 100644 ios/config/staging/GoogleService-Info.plist delete mode 100644 lib/app/configuration/develop/config_develop.dart delete mode 100644 lib/app/configuration/production/config_production.dart delete mode 100644 lib/app/configuration/staging/config_staging.dart diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7c57bf5..0be81a2 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -6,4 +6,4 @@ #* @strvcom/Android # More info here - https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners -* @HE-LU @michalurbanek @robha141 @Lonchi78 @HsiaoAi \ No newline at end of file +@michalurbanek @robha141 @Lonchi78 @HsiaoAi \ No newline at end of file diff --git a/.github/workflows/android_firebase_app_distribution_all.yml b/.github/workflows/android_firebase_app_distribution_all.yml deleted file mode 100644 index 6ca6204..0000000 --- a/.github/workflows/android_firebase_app_distribution_all.yml +++ /dev/null @@ -1,90 +0,0 @@ -name: "[Release] Android - FirebaseAppDistribution - All" - -on: - push: - tags: - - 'v*-firebase-all' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -# You need to uncomment [Setup - Flutter Actions with Cache] when you want to use -# different runner then our internal M1 flutter-builds runner. -env: - flutter_version: "3.27.1" - java_version: "17" - -jobs: - firebase-app-distribution-build: - name: Firebase App Distribution Build - - runs-on: ubuntu-latest - # runs-on: [self-hosted, macOS, flutter-builds] # use this for self-hosted runners - timeout-minutes: 15 - - steps: - - name: '[Setup - Checkout]' - uses: actions/checkout@v4.2.1 - - - name: '[Setup - Java]' - uses: actions/setup-java@v4.4.0 - with: - distribution: 'zulu' - java-version: ${{ env.java_version }} - - - name: '[Setup - Flutter Actions with Cache]' - uses: subosito/flutter-action@v2 - with: - channel: 'stable' - flutter-version: ${{ env.flutter_version }} - cache: true - architecture: x64 - cache-key: 'flutter-:os:-:channel:-:version:-:arch:' - cache-path: '${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:' - - # Make sure we have latest FVM - - name: '[FVM - Install]' - run: dart pub global activate fvm - - # Install correct fvm version - - name: '[FVM - Install version]' - run: fvm install ${{ env.flutter_version }} - - # Make gen - - name: '[Flutter - Generate code]' - run: make gen - - # Develop - - name: '[Flutter - Build Develop APK]' - run: fvm flutter build apk -t lib/main_develop.dart --flavor develop --obfuscate --split-debug-info=build/app/outputs/symbols - - - name: '[Create Firebase App Distribution service account file]' - run: echo "${{ secrets.FIREBASE_DEV_CREDENTIAL_FILE_CONTENT }}" | base64 -d > ./android/firebase_app_distribution_service_account.json - - - name: '[Flutter - Upload to Develop Firebase App Distribution]' - run: cd android; ./gradlew appDistributionUploadDevelopRelease; cd .. - - # TODO: Add correct AppId from the Firebase Project - - name: '[Flutter - Upload Crashlytics Mapping Files]' - run: firebase crashlytics:symbols:upload --app=XXX build/app/outputs/symbols - - # Production - - name: '[Flutter - Build Production APK]' - run: fvm flutter build apk -t lib/main_production.dart --flavor production --obfuscate --split-debug-info=build/app/outputs/symbols - - - name: '[Create Firebase App Distribution service account file]' - run: echo "${{ secrets.FIREBASE_PROD_CREDENTIAL_FILE_CONTENT }}" | base64 -d > ./android/firebase_app_distribution_service_account.json - - - name: '[Flutter - Upload to Production Firebase App Distribution]' - run: cd android; ./gradlew appDistributionUploadProductionRelease; cd .. - - # TODO: Add correct AppId from the Firebase Project - - name: '[Flutter - Upload Crashlytics Mapping Files]' - run: firebase crashlytics:symbols:upload --app=XXX build/app/outputs/symbols - - # Only on self hosted. Cleanup the files after the build - # - name: '[Finish - Cleanup]' - # run: | - # rm -rf ./* || true - # rm -rf ./.??* || true \ No newline at end of file diff --git a/.github/workflows/android_firebase_app_distribution_develop.yml b/.github/workflows/android_firebase_app_distribution_develop.yml index 3196d04..cd39ca2 100644 --- a/.github/workflows/android_firebase_app_distribution_develop.yml +++ b/.github/workflows/android_firebase_app_distribution_develop.yml @@ -12,7 +12,7 @@ concurrency: # You need to uncomment [Setup - Flutter Actions with Cache] when you want to use # different runner then our internal M1 flutter-builds runner. env: - flutter_version: "3.27.1" + flutter_version: "3.38.5" java_version: "17" jobs: @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest # runs-on: [self-hosted, macOS, flutter-builds] # use this for self-hosted runners - timeout-minutes: 15 + timeout-minutes: 25 steps: - name: '[Setup - Checkout]' @@ -51,6 +51,19 @@ jobs: - name: '[FVM - Install version]' run: fvm install ${{ env.flutter_version }} + # Install sops + - name: '[Secrets - Setup SOPS]' + uses: nhedger/setup-sops@v2 + + # Load secrets + - name: '[Secrets - Generate keystore and properties]' + env: + SECRETS_ENCRYPT_KEY: ${{ secrets.SECRETS_ENCRYPT_KEY }} + RELEASE_KEYSTORE: ${{ secrets.RELEASE_KEYSTORE }} + RELEASE_PROPERTIES: ${{ secrets.RELEASE_PROPERTIES }} + FIREBASE_APP_DISTRIBUTION_SERVICE_ACCOUNT: ${{ secrets.FIREBASE_DEV_APP_DISTRIBUTION_SERVICE_ACCOUNT }} + run: ./extras/secrets/tools/load-secrets.sh + # Make gen - name: '[Flutter - Generate code]' run: make gen @@ -58,15 +71,15 @@ jobs: - name: '[Flutter - Build APK]' run: fvm flutter build apk -t lib/main_develop.dart --flavor develop --obfuscate --split-debug-info=build/app/outputs/symbols - - name: '[Create Firebase App Distribution service account file]' - run: echo "${{ secrets.FIREBASE_DEV_CREDENTIAL_FILE_CONTENT }}" | base64 -d > ./android/firebase_app_distribution_service_account.json - - name: '[Flutter - Upload to Firebase App Distribution]' run: cd android; ./gradlew appDistributionUploadDevelopRelease; cd .. - # TODO: Add correct AppId from the Firebase Project - name: '[Flutter - Upload Crashlytics Mapping Files]' - run: firebase crashlytics:symbols:upload --app=XXX build/app/outputs/symbols + run: cd android; + ./gradlew uploadCrashlyticsSymbolFileDevelopRelease; + ./gradlew uploadCrashlyticsMappingFileDevelopRelease; + cd .. + # Only on self hosted. Cleanup the files after the build # - name: '[Finish - Cleanup]' diff --git a/.github/workflows/android_firebase_app_distribution_production.yml b/.github/workflows/android_firebase_app_distribution_production.yml index 5b9fb23..4208036 100644 --- a/.github/workflows/android_firebase_app_distribution_production.yml +++ b/.github/workflows/android_firebase_app_distribution_production.yml @@ -12,7 +12,7 @@ concurrency: # You need to uncomment [Setup - Flutter Actions with Cache] when you want to use # different runner then our internal M1 flutter-builds runner. env: - flutter_version: "3.27.1" + flutter_version: "3.38.5" java_version: "17" jobs: @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest # runs-on: [self-hosted, macOS, flutter-builds] # use this for self-hosted runners - timeout-minutes: 15 + timeout-minutes: 25 steps: - name: '[Setup - Checkout]' @@ -50,7 +50,20 @@ jobs: # Install correct fvm version - name: '[FVM - Install version]' run: fvm install ${{ env.flutter_version }} - + + # Install sops + - name: '[Secrets - Setup SOPS]' + uses: nhedger/setup-sops@v2 + + # Load secrets + - name: '[Secrets - Generate keystore and properties]' + env: + SECRETS_ENCRYPT_KEY: ${{ secrets.SECRETS_ENCRYPT_KEY }} + RELEASE_KEYSTORE: ${{ secrets.RELEASE_KEYSTORE }} + RELEASE_PROPERTIES: ${{ secrets.RELEASE_PROPERTIES }} + FIREBASE_APP_DISTRIBUTION_SERVICE_ACCOUNT: ${{ secrets.FIREBASE_PROD_APP_DISTRIBUTION_SERVICE_ACCOUNT }} + run: ./extras/secrets/tools/load-secrets.sh + # Make gen - name: '[Flutter - Generate code]' run: make gen @@ -58,15 +71,14 @@ jobs: - name: '[Flutter - Build APK]' run: fvm flutter build apk -t lib/main_production.dart --flavor production --obfuscate --split-debug-info=build/app/outputs/symbols - - name: '[Create Firebase App Distribution service account file]' - run: echo "${{ secrets.FIREBASE_PROD_CREDENTIAL_FILE_CONTENT }}" | base64 -d > ./android/firebase_app_distribution_service_account.json - - name: '[Flutter - Upload to Firebase App Distribution]' run: cd android; ./gradlew appDistributionUploadProductionRelease; cd .. - # TODO: Add correct AppId from the Firebase Project - name: '[Flutter - Upload Crashlytics Mapping Files]' - run: firebase crashlytics:symbols:upload --app=XXX build/app/outputs/symbols + run: cd android; + ./gradlew uploadCrashlyticsSymbolFileProductionRelease; + ./gradlew uploadCrashlyticsMappingFileProductionRelease; + cd .. # Only on self hosted. Cleanup the files after the build # - name: '[Finish - Cleanup]' diff --git a/.github/workflows/android_firebase_app_distribution_staging.yml b/.github/workflows/android_firebase_app_distribution_staging.yml new file mode 100644 index 0000000..3cdb030 --- /dev/null +++ b/.github/workflows/android_firebase_app_distribution_staging.yml @@ -0,0 +1,78 @@ +name: "[Release] Android - FirebaseAppDistribution - Staging" + +on: + push: + tags: + - 'v*-staging' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +# You need to uncomment [Setup - Flutter Actions with Cache] when you want to use +# different runner then our internal M1 flutter-builds runner. +env: + flutter_version: "3.38.5" + java_version: "17" + +jobs: + firebase-app-distribution-build: + name: Firebase App Distribution Build + + runs-on: ubuntu-latest + # runs-on: [self-hosted, macOS, flutter-builds] # use this for self-hosted runners + timeout-minutes: 25 + + steps: + - name: '[Setup - Checkout]' + uses: actions/checkout@v4.2.1 + + - name: '[Setup - Java]' + uses: actions/setup-java@v4.4.0 + with: + distribution: 'zulu' + java-version: ${{ env.java_version }} + + - name: '[Setup - Flutter Actions with Cache]' + uses: subosito/flutter-action@v2 + with: + channel: 'stable' + flutter-version: ${{ env.flutter_version }} + architecture: x64 + + # Make sure we have latest FVM + - name: '[FVM - Install]' + run: dart pub global activate fvm + + # Install correct fvm version + - name: '[FVM - Install version]' + run: fvm install ${{ env.flutter_version }} + + # Install sops + - name: '[Secrets - Setup SOPS]' + uses: nhedger/setup-sops@v2 + + # Load secrets + - name: '[Secrets - Generate keystore and properties]' + env: + SECRETS_ENCRYPT_KEY: ${{ secrets.SECRETS_ENCRYPT_KEY }} + RELEASE_KEYSTORE: ${{ secrets.RELEASE_KEYSTORE }} + RELEASE_PROPERTIES: ${{ secrets.RELEASE_PROPERTIES }} + FIREBASE_APP_DISTRIBUTION_SERVICE_ACCOUNT: ${{ secrets.FIREBASE_STG_APP_DISTRIBUTION_SERVICE_ACCOUNT }} + run: ./extras/secrets/tools/load-secrets.sh + + # Make gen + - name: '[Flutter - Generate code]' + run: make gen + + - name: '[Flutter - Build APK]' + run: fvm flutter build apk -t lib/main_staging.dart --flavor staging --obfuscate --split-debug-info=build/app/outputs/symbols + + - name: '[Flutter - Upload to Firebase App Distribution]' + run: cd android; ./gradlew appDistributionUploadStagingRelease; cd .. + + - name: '[Flutter - Upload Crashlytics Mapping Files]' + run: cd android; + ./gradlew uploadCrashlyticsSymbolFileStagingRelease; + ./gradlew uploadCrashlyticsMappingFileStagingRelease; + cd .. diff --git a/.github/workflows/android_play_store_distribution.yml b/.github/workflows/android_play_store_distribution.yml index 57a5981..5b4b6f7 100644 --- a/.github/workflows/android_play_store_distribution.yml +++ b/.github/workflows/android_play_store_distribution.yml @@ -12,7 +12,7 @@ concurrency: # You need to uncomment [Setup - Flutter Actions with Cache] when you want to use # different runner then our internal M1 flutter-builds runner. env: - flutter_version: "3.27.1" + flutter_version: "3.38.5" java_version: "17" jobs: @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest # runs-on: [self-hosted, macOS, flutter-builds] # use this for self-hosted runners - timeout-minutes: 15 + timeout-minutes: 25 steps: - name: '[Setup - Checkout]' @@ -50,7 +50,20 @@ jobs: # Install correct fvm version - name: '[FVM - Install version]' run: fvm install ${{ env.flutter_version }} + + # Install sops + - name: '[Secrets - Setup SOPS]' + uses: nhedger/setup-sops@v2 + # Load secrets + - name: '[Secrets - Generate keystore and properties]' + env: + SECRETS_ENCRYPT_KEY: ${{ secrets.SECRETS_ENCRYPT_KEY }} + RELEASE_KEYSTORE: ${{ secrets.RELEASE_KEYSTORE }} + RELEASE_PROPERTIES: ${{ secrets.RELEASE_PROPERTIES }} + FIREBASE_APP_DISTRIBUTION_SERVICE_ACCOUNT: ${{ secrets.FIREBASE_PROD_APP_DISTRIBUTION_SERVICE_ACCOUNT }} + run: ./extras/secrets/tools/load-secrets.sh + # Make gen - name: '[Flutter - Generate code]' run: make gen @@ -74,9 +87,11 @@ jobs: #status: draft # Upload it as draft release if you want to manually publish the release from GooglePlay console #userFraction: 0.1 # Rollout of the release <0;1> - # TODO: Add correct AppId from the Firebase Project - name: '[Flutter - Upload Crashlytics Mapping Files]' - run: firebase crashlytics:symbols:upload --app=XXX build/app/outputs/symbols + run: cd android; + ./gradlew uploadCrashlyticsSymbolFileProductionRelease; + ./gradlew uploadCrashlyticsMappingFileProductionRelease; + cd .. # Only on self hosted. Cleanup the files after the build # - name: '[Finish - Cleanup]' diff --git a/.github/workflows/flutter_project_codecheck_android.yml b/.github/workflows/flutter_project_codecheck_android.yml index 35c2c49..f08d74b 100644 --- a/.github/workflows/flutter_project_codecheck_android.yml +++ b/.github/workflows/flutter_project_codecheck_android.yml @@ -11,7 +11,7 @@ concurrency: # You need to uncomment [Setup - Flutter Actions with Cache] when you want to use # different runner then our internal M1 flutter-builds runner. env: - flutter_version: "3.27.1" + flutter_version: "3.38.5" java_version: "17" jobs: @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest # runs-on: [self-hosted, macOS, flutter-builds] # use this for self-hosted runners - timeout-minutes: 15 + timeout-minutes: 25 steps: - name: '[Setup - Checkout]' @@ -50,6 +50,19 @@ jobs: - name: '[FVM - Install version]' run: fvm install ${{ env.flutter_version }} + # Install sops + - name: '[Secrets - Setup SOPS]' + uses: nhedger/setup-sops@v2 + + # Load secrets + - name: '[Secrets - Generate keystore and properties]' + env: + SECRETS_ENCRYPT_KEY: ${{ secrets.SECRETS_ENCRYPT_KEY }} + RELEASE_KEYSTORE: ${{ secrets.RELEASE_KEYSTORE }} + RELEASE_PROPERTIES: ${{ secrets.RELEASE_PROPERTIES }} + FIREBASE_APP_DISTRIBUTION_SERVICE_ACCOUNT: ${{ secrets.FIREBASE_STG_APP_DISTRIBUTION_SERVICE_ACCOUNT }} + run: ./extras/secrets/tools/load-secrets.sh + # Make gen - name: '[Flutter - Generate code]' run: make gen @@ -60,7 +73,7 @@ jobs: # Build is slightly faster when targeting single platform, debug version and only apk instead of appbundle - name: '[Flutter - Build APK]' - run: fvm flutter build apk -t lib/main_develop.dart --flavor develop --target-platform android-arm64 --debug + run: fvm flutter build apk -t lib/main_staging.dart --flavor staging --target-platform android-arm64 --obfuscate --split-debug-info=build/app/outputs/symbols --debug # Only on self hosted. Cleanup the files after the build # - name: '[Finish - Cleanup]' diff --git a/.gitignore b/.gitignore index c980304..157f4b9 100644 --- a/.gitignore +++ b/.gitignore @@ -62,4 +62,8 @@ app.*.map.json integration_test/test_bundle.dart # FVM Version Cache -.fvm/ \ No newline at end of file +.fvm/ + +# Secrets +/.env* +/extras/secrets/age_key.txt diff --git a/.sops.yaml b/.sops.yaml new file mode 100644 index 0000000..7a846fa --- /dev/null +++ b/.sops.yaml @@ -0,0 +1,14 @@ +creation_rules: + - path_regex: \.env.*$ + input_type: dotenv + age: age1e8mlg8wgp5rszgr9h9axu50mpr7v4020du5ry8s7wspwp4wftgmsfh4uxj + - path_regex: .plist(\.enc)?$ + age: age1e8mlg8wgp5rszgr9h9axu50mpr7v4020du5ry8s7wspwp4wftgmsfh4uxj + - path_regex: .properties(\.enc)?$ + age: age1e8mlg8wgp5rszgr9h9axu50mpr7v4020du5ry8s7wspwp4wftgmsfh4uxj + - path_regex: .keystore(\.enc)?$ + input_type: binary + age: age1e8mlg8wgp5rszgr9h9axu50mpr7v4020du5ry8s7wspwp4wftgmsfh4uxj + - path_regex: 'google-services\.json(\.enc)?$' + input_type: json + age: age1e8mlg8wgp5rszgr9h9axu50mpr7v4020du5ry8s7wspwp4wftgmsfh4uxj \ No newline at end of file diff --git a/README.md b/README.md index 3003fd7..037b756 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ - [Project run](#project-run) - [Linux](#linux) - [Flavors](#flavors) + - [Secrets](#secrets) - [Build and Distribution](#build-and-distribution) - [Assembling the App](#assembling-the-app) @@ -98,7 +99,8 @@ These are our four core values: 6. - [ ] Change the app icon. This process is described in the [Icons generation](./project_setup/README.md#app-icon-generation) section. 7. - [ ] Change the app splash screen. This process is described in the [Splash screen generation](./project_setup/README.md#splash-screen-generation) section. 8. - [ ] Set up Firebase or remove it completely. This process is described in the [Firebase setup](#firebase-setup) section. -9. - [ ] Go through all the ToDo's inside the project, and react to them. +9. - [ ] Add your own secrets. This process is described in the [Secrets handling](#secrets) +10. - [ ] Go through all the ToDo's inside the project, and react to them. @@ -187,6 +189,35 @@ For Google sign in: - for iOS, you have to copy value from `REVERSED_CLIENT_ID` from `GoogleService-Info.plist` into `GOOGLE_REVERSED_CLIENT_ID` in build settings. +## Secrets +API keys, APP IDs, service accounts and even release keystore are stored as secrets in this project. We are using `age` + `sops` libraries for this purpose. + +Each environment has it's own secrets file `.env-production`, `.env-staging` and `.env-develop` which overrides the default one `.env`. +Those files are not placed in version control, rather are kept in secret and generated using encrypted (`.enc`) files. + +Secrets from encrypted files are also decrypted into xcode property files `.env.*.xcconfig`. Then properties like `$(APPLE_TEAM_ID)` can be used in `Info.plist` or `project.pbxproj`. +They are also overrides the same as project ones - e.g. `.env.xcconfig` is overridden by `.env.production.xcconfig` in production flavor of the iOS app. + +### Decrypt default values to see how it works: +- Export path to private key file `export SOPS_AGE_KEY_FILE="/flutter-template/extras/secrets/age_key.txt"` +- Run `make secretsDecrypt` command, it automatically creates default decrypted files +- Check generated files with decrypted values to get an idea of how it works + +### To use your own secrets: +- First [decrypt](#Decrypt-default-values-to-see-how-it-works) default secrets +- Remove current `extras/secrets/age_key.txt` file +- Generate a new private age key file with `age-keygen -o age_key.txt` and keep this secret safe! +- Export path to that file with `export SOPS_AGE_KEY_FILE="/age_key.txt"` +- Set public key property in `.sops.yaml` file to public key from private key file - e.g. `age: age1e8mlg8wgp5rszgr9h9axu50mpr7v4020du5ry8s7wspwp4wftgmsfh4uxj` +- Update `.env`, `google-service.json` files and `keystore` secrets +- Run `make secretsEncrypt` to regenerate secrets with your private key + +If you need to +- add another API key, just place it into the proper `.env` file and use `make secretsEncrypt` to regenerate encrypted files `.enc`. +- encrypt another file, modify `extras/secrets/tools/encrypt-secrets.sh` and `extras/secrets/tools/decrypt-secrets.sh` files to include it + +Keystore works the same as env secrets, more info can be found in the [Keystore Readme](./android/extras/keystores/README.md). + @@ -212,7 +243,7 @@ flutter build appbundle \ ### Android #### Manual To deploy the app manually, you have to: -1. Make sure you have your keystore configured and set up correctly. Check the [Keystore Readme](./android/extras/keystore/README.md) +1. Make sure you have your keystore configured and set up correctly. Check the [Keystore Readme](./android/extras/keystores/README.md) 2. In the terminal run `flutter build appbundle -t lib/main_production.dart --flavor production --obfuscate --split-debug-info=build/app/outputs/symbols` for production release. 3. After creating an app bundle, the mapping file will be included automatically, but the native symbols has to be added manually - it can be found in `build\app\intermediates\merged_native_libs\productionRelease\out`. Grab those generated folders, zip them and upload into Google Play console under the new build. @@ -227,9 +258,7 @@ base64 -i input.json > output.json.b64 Now you need to set the Base64 string as a secret inside your GitHub. You should name it something like `FIREBASE_DEV_CREDENTIAL_FILE_CONTENT` and check that it has the same name inside the script. Also, do not forget to uncomment `apply plugin: 'com.google.gms.google-services'` at the end of the build.gradle file inside the Android app folder. -Now you can use one of the actions we have. There should be at least two actions to build for the develop and for the production environment. You can launch those two by creating a tag on a commit with the name `*-develop` or `*-production`. - -As an alternative, you can use `*-firebase-all` which will deploy both at the same time. +Now you can use one of the actions we have. There should be at least two actions to build for the develop and for the production environment. You can launch those two by creating a tag on a commit with the name `*-develop`, `*-staging` or `*-production`. #### Google Play Distribution using GitHub Actions We also have a GitHub Action script for deploying to Google Play automatically. The tag requirement is listed in `.github\workflows\android_play_store_distribution.yml`. @@ -321,7 +350,7 @@ We have three main branches. `develop`, `staging`, and `master`. The most import We also have a strict naming convention for branches, commit names, and PR names. Jira ticket is used only when there is any. We are using underscores, except in the Jira ticket name so the Jira automation works well. For Branches: `{GithubUsername}/{chore/feat/fix/refactor}/{JiraTicketNumber}/{SomeBranchDetails}` -Example: `helu/feat/MN-1726/sign_up` +Example: `john/feat/MN-1726/sign_up` For Commit and PR names: `{chore/feat/fix/refactor}: {Description}` Example: `feat: Sign up logic implementation` @@ -511,6 +540,7 @@ AppName (Web) # Security + ## FreeRASP It is recommended to setup FreeRASP on the project. More info about the library [here](https://pub.dev/packages/freerasp). diff --git a/android/.gitignore b/android/.gitignore index bc2100d..917e4a9 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -5,3 +5,8 @@ gradle-wrapper.jar /gradlew.bat /local.properties GeneratedPluginRegistrant.java + +app/google-services.json + +extras/keystores/release.keystore +extras/keystores/release.properties \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index 151e131..007ee2a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -32,10 +32,10 @@ def targetAndroidSdkVersion = 36 // Initialize a new Properties() object called keystoreProperties. def debugKeystoreProperties = new Properties() -debugKeystoreProperties.load(new FileInputStream(rootProject.file("extras/keystore/debug.properties"))) +debugKeystoreProperties.load(new FileInputStream(rootProject.file("extras/keystores/debug.properties"))) def releaseKeystoreProperties = new Properties() -releaseKeystoreProperties.load(new FileInputStream(rootProject.file("extras/keystore/release.properties"))) +releaseKeystoreProperties.load(new FileInputStream(rootProject.file("extras/keystores/release.properties"))) android { compileSdkVersion compileAndroidSdkVersion @@ -90,14 +90,17 @@ android { dimension "env" applicationIdSuffix ".develop" versionNameSuffix "-develop" + flutter.target = "lib/main_develop.dart" } staging { dimension "env" applicationIdSuffix ".staging" versionNameSuffix "-staging" + flutter.target = "lib/main_staging.dart" } production { dimension "env" + flutter.target = "lib/main_production.dart" } } @@ -142,6 +145,11 @@ android { serviceCredentialsFile="firebase_app_distribution_service_account.json" groups="developers" } + + firebaseCrashlytics { + nativeSymbolUploadEnabled true + mappingFileUploadEnabled true + } } } diff --git a/android/app/google-services.json b/android/app/google-services.json deleted file mode 100644 index 47872d5..0000000 --- a/android/app/google-services.json +++ /dev/null @@ -1,274 +0,0 @@ -{ - "project_info": { - "project_number": "907121519909", - "project_id": "strv-flutter-template", - "storage_bucket": "strv-flutter-template.appspot.com" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "android_client_info": { - "package_name": "com.strv.flutter.template" - } - }, - "oauth_client": [ - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 1, - "android_info": { - "package_name": "com.strv.flutter.template", - "certificate_hash": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - } - }, - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 3 - }, - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 2, - "ios_info": { - "bundle_id": "com.strv.flutter.template.staging" - } - } - ] - } - } - }, - { - "client_info": { - "mobilesdk_app_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "android_client_info": { - "package_name": "com.strv.flutter.template.debug" - } - }, - "oauth_client": [ - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 1, - "android_info": { - "package_name": "com.strv.flutter.template.debug", - "certificate_hash": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - } - }, - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 3 - }, - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 2, - "ios_info": { - "bundle_id": "com.strv.flutter.template.staging" - } - } - ] - } - } - }, - { - "client_info": { - "mobilesdk_app_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "android_client_info": { - "package_name": "com.strv.flutter.template.develop" - } - }, - "oauth_client": [ - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 1, - "android_info": { - "package_name": "com.strv.flutter.template.develop", - "certificate_hash": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - } - }, - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 3 - }, - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 2, - "ios_info": { - "bundle_id": "com.strv.flutter.template.staging" - } - } - ] - } - } - }, - { - "client_info": { - "mobilesdk_app_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "android_client_info": { - "package_name": "com.strv.flutter.template.develop.debug" - } - }, - "oauth_client": [ - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 1, - "android_info": { - "package_name": "com.strv.flutter.template.develop.debug", - "certificate_hash": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - } - }, - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 3 - }, - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 2, - "ios_info": { - "bundle_id": "com.strv.flutter.template.staging" - } - } - ] - } - } - }, - { - "client_info": { - "mobilesdk_app_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "android_client_info": { - "package_name": "com.strv.flutter.template.staging" - } - }, - "oauth_client": [ - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 1, - "android_info": { - "package_name": "com.strv.flutter.template.staging", - "certificate_hash": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - } - }, - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 3 - }, - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 2, - "ios_info": { - "bundle_id": "com.strv.flutter.template.staging" - } - } - ] - } - } - }, - { - "client_info": { - "mobilesdk_app_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "android_client_info": { - "package_name": "com.strv.flutter.template.staging.debug" - } - }, - "oauth_client": [ - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 1, - "android_info": { - "package_name": "com.strv.flutter.template.staging.debug", - "certificate_hash": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - } - }, - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 3 - }, - { - "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - "client_type": 2, - "ios_info": { - "bundle_id": "com.strv.flutter.template.staging" - } - } - ] - } - } - } - ], - "configuration_version": "1" -} \ No newline at end of file diff --git a/android/extras/keystore/README.md b/android/extras/keystore/README.md deleted file mode 100644 index eacce0b..0000000 --- a/android/extras/keystore/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# Generating new key -To generate new `release.keystore` file, use following command: - -``` -keytool -genkey -v -keystore release.keystore -keyalg RSA -keysize 2048 -validity 20000 -storetype PKCS12 -alias exampleKeyAlias -storepass examplePassword -keypass examplePassword -``` -Note: The password for store and key must be the same for `PKCS12` storetype. - -Here is an example for organizational information that you need to fill during generating of a new key: -``` -CN=Lukas Hermann, OU=STRV, O=STRV, L=Prague, ST=Czechia, C=CZ -``` - -To get the SHA1 Certificate of the keystore (for example for Firebase purpose) you can use following commands: -``` -keytool -list -v -alias android -keystore debug.keystore -keytool -list -v -alias exampleKeyAlias -keystore release.keystore -``` \ No newline at end of file diff --git a/android/extras/keystore/release.keystore b/android/extras/keystore/release.keystore deleted file mode 100644 index b8b2a44ad738df69aa5491c296745ef18d951355..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2762 zcma)8Wmppo*WO?w#^{o6k)Cu*2qLBMAmIS%Aq?q~-hfHBba#VD#{dZdK|*SDsE;(# zBJlEE-+TF7@2~g$aq2$zIX~}nU?|7{9uOafg18V6^M`AMpOFFyfq5v1B?tvEyM;|) zC~)_Gw7~Ts6u9~pF2A*60+N4EK_NiAJQP^z7LpvV{!E*DFL{G_*NG8ekb9P$cM@bys^c3r5lxQTc8 zy|W7aAsoOwX^^cOCS$f^us8keae2He8l=y4VGzX1QMvi#6p~W9F82l(Joc&7#x*tR zrCB%G?*xkDfrhb|5Cv^#-GC+p^vX^K`Ihm}B=q!SC(fKR;C|T#qVB?KRMtx-~%gvOULIOL- zp#aaR*`9d?dWq%|*{Td4Bp&ISgsyRBLi!XxKd=XWSM6`UI;%GMeY~SK)4r-uuLPSu z1tf0}3!LZ-T>s59CBxJ2WwwzpKX+a?`!Zcm-l6$p?&ZlRc*}cjXX}4q<}!Hp_s-iM z4F&3FFFKDYVJmlcYhOlC|fOkx7NSY|Mx_QyAu& zz-N8hmd4i^yI?jdc)%=I62xjqLkuKY+H$u(?GS)mTdp%ykQ1cvD`yuueD*#7SC@@u z_-)AUv3Zc~20Ima<@}A6a8**yei*H{IHbN~0L4R@q6=?7lIG#vp$n5YZPO3f?CRm^*H28%)Z9=Q}OT|9M4ylIOw20bQ zj&o5mz}<($N;??`JA>x81V75lCQn`ZXEi0f1IMR3FKq^C8nRggYT(Dbf*X|RXBGFtNuUnIQL_l3ddHx5hG%xwW z$~*-;5*FWfi~QyuCb!zWAaAf34dTksbe5Z>BKOd9)9bfgV;)zq6j$YRgDx!A1p|_W z3i;4CyhMp^VXv!(F}ZJZg8(_U{vynKEL!nj6I2ezt4ce3whB`o&hL_GA4%wRjNaFW z3H^~vVw(HxtgHZAfDgbD-~@01Aa8TWTl2lGk${)C-W|sA51sZt4T#F%rIRiDeMu<^ zF;Qulw4|h%Bn$;g_(LKj$U}ipw@@G+5O5p3|LFk#n{an}j?3rzo>qmyR8qYR&%}F= z1RMVs;jRT}o`OTXj-?8L>ax_*i@_)mfTt^+H>a%CP|&bgHMmb@Cq8uwA1UE>{xsU2 z`)h&)V?f=P{8JMZKB^>Z5Fwcm&n9~i{w$|= zMoU^ssnZ&xk^)rbcH9J`%0sKCB%>2IsfX_n&N)S8t_4>`Kf=eJ;Qi3a zKBLdznSGE-BoG{N%-X30@)mXfB;2@To0mUIPozo|nHpaIZ2lc( zxkQFP#pc=QfhVdaC9^I(?!9Se;5e^~xZ*sbhFP-x4dz>5(9s2*ArhqHAXnZmagQdPlnCCLT++J zS1TjszU`WObd1>6_qtZ2`yOkF4kZaklDtzjOj5^=_9_w}iML~aaD~3*0T zW4!60LX}I;137vuuDlM|`fCwB^XuIj=W`8Hv-c_`T6P#^ua#aWLy@N9KGye?upt8l~sSWn<`GgNj7ZbGMLDm(46w zuGf9+1 zOr#ST4#pQ%>fwdpx0g~&EuCt+wdMyO@0t_vpNM24xqGcJi?X~Y8D@;?NSxKtq$3jS zV0&&3ndTjU>A>#8NdNqIct9`!#7^$0grrwG0x~^EvcvK<5ZDuYHfvGc6tcZUe&_UN eO%RP1F4eI5{2s;<5<6Q;Vv=)v?T`LXg8l`EVe@YQ diff --git a/android/extras/keystore/release.properties b/android/extras/keystore/release.properties deleted file mode 100644 index 4298b74..0000000 --- a/android/extras/keystore/release.properties +++ /dev/null @@ -1,4 +0,0 @@ -storePassword=examplePassword -storeFile=./../extras/keystore/release.keystore -keyPassword=examplePassword -keyAlias=exampleKeyAlias \ No newline at end of file diff --git a/android/extras/keystores/README.md b/android/extras/keystores/README.md new file mode 100644 index 0000000..69781d2 --- /dev/null +++ b/android/extras/keystores/README.md @@ -0,0 +1,34 @@ +# Generating new key +To generate new `release.keystore` file, use following command: + +``` +keytool -genkey -v -keystore release.keystore -keyalg RSA -keysize 2048 -validity 20000 -storetype PKCS12 -alias exampleKeyAlias -storepass examplePassword -keypass examplePassword +``` +Note: The password for store and key must be the same for `PKCS12` storetype. + +Here is an example for organizational information that you need to fill during generating of a new key: +``` +CN=Joe Doe, OU=STRV, O=STRV, L=Prague, ST=Czechia, C=CZ +``` + +To get the SHA1 Certificate of the keystore (for example for Firebase purpose) you can use following commands: +``` +keytool -list -v -alias android -keystore debug.keystore +keytool -list -v -alias exampleKeyAlias -keystore release.keystore +``` + +# Release keystore + +This repository does not contains release signing keystore in open form. +Instead there is an encrypted file `extras/secrets/release.keystore.enc` containing release keystore and properties. +Therefore if you want to build a release build there is necessary to load those secrets. + +## Load/decrypt secrets to local properties + +1) Ask developers to share `age_key.txt` secret and keep this secret safe! +2) Export path to that file with `export SOPS_AGE_KEY_FILE="/age_key.txt"` +3) From the root of the project run the script `make secretsDecrypt` (`sh ./extras/secrets/tools/load-secrets.sh`) which + will produce proper files with secrets +4) In the folders `./extras/secrets/` and `./extras/keystores/` check files are there and set. The build script will read + them accordingly during the build. +5) After making changes in decrypted files, run `make secretsEncrypt` (`sh ./extras/secrets/tools/encrypt-secrets.sh`) for encryption. diff --git a/android/extras/keystore/debug.keystore b/android/extras/keystores/debug.keystore similarity index 100% rename from android/extras/keystore/debug.keystore rename to android/extras/keystores/debug.keystore diff --git a/android/extras/keystore/debug.properties b/android/extras/keystores/debug.properties similarity index 55% rename from android/extras/keystore/debug.properties rename to android/extras/keystores/debug.properties index 17ce26f..a7667af 100644 --- a/android/extras/keystore/debug.properties +++ b/android/extras/keystores/debug.properties @@ -1,4 +1,4 @@ storePassword=android -storeFile=./../extras/keystore/debug.keystore +storeFile=./../extras/keystores/debug.keystore keyPassword=android keyAlias=android \ No newline at end of file diff --git a/android/settings.gradle b/android/settings.gradle index 0ef5b6d..a9b9e78 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -21,10 +21,8 @@ plugins { id "com.android.application" version "8.6.1" apply false id "org.jetbrains.kotlin.android" version "2.1.20" apply false - // START: FlutterFire Configuration id "com.google.gms.google-services" version "4.4.2" apply false id "com.google.firebase.crashlytics" version "3.0.2" apply false - // END: FlutterFire Configuration // For Firebase App Distribution id "com.google.firebase.appdistribution" version "5.0.0" apply false diff --git a/extras/secrets/GoogleService-Info-develop.plist.enc b/extras/secrets/GoogleService-Info-develop.plist.enc new file mode 100644 index 0000000..1a68212 --- /dev/null +++ b/extras/secrets/GoogleService-Info-develop.plist.enc @@ -0,0 +1,14 @@ +{ + "data": "ENC[AES256_GCM,data:jjAcymSxrHMGZssMXxPSLm03K+cVWiQ7WqMVMINqCYTqCtOH0Z5ds53Of904qI6dAZ/JpuzTcsUKpGO0H3wzhuZNHurEPqmg/8POYYfEEOXjvs6Tk97mSHy/8LVJGQY3gV5K42ev79HdvFAzg9htbEn4qBw8l9PSHZzQNXVf8IWIDt93KOrvW76WsHTB9LIcgDsPudphxqeqpf3hvq0dWaWJsciYvx74/woNzd6QzOfO7Q1ZxIz2pc/KnfPyg1dRPeYhZyPfGTZrqZ254iWZeDyGjtKBbcaHumCjZ+WB0D4Y+JJyuFc7sRDv+Ib8H1w/2/jhZRwInY7BYG1TeDpPyRyQHho2fzdV/lWNtj7Q3nWz8aBGd0xZEftqei+JaAqJhH+uoCa08HN4Aa4+zJgNaUXXXSQzKlU17B/zgzm1SG4zR8v/Go/FyVw4qSf2jjgcy8qPSCWsMOZ1nt9NX0RAMjy8t1geKYcI4q9XtCKWzTZxWxXLQxs5Tl4cyJ27Y79RFCu5ObDbG7zup66d7iZOMWFhc1lUAox2X989BcatxZiIrEtXY2WzgrSK2hQViSUAZ5GWGz5qBDd0rJ7Ad7q40SLf19uodCn+cVhjcn+WoOjvbOvBnN1SU49lNP1m4a3XXDyqFTntchxz3u1oR8sPgIQK8rS5y0m5PRhgDwkUYiTojOJq4c4IlsHICYxesNkHEgAl1EcJqX7FU/g3jbbH1fbhGOoXPGejTT56hYAMo9C3Wd0t/XQ9QiAsiQXfvUM+TU+53bi1xnqpBMZFK3X/LMfY17+YcQZIb1KA3NBoeQcapmNS2En14nx8YKRL23lwo+LHqx1tPZpfFiKEEnOF7M3JZjuZyherT7IYY1wvC4wTn497zUHtMiVB11ZNIPOq6AIrTnFXR46X6mhYRbBHseLsn3fUDu+jj00GnWOn0DnFdmdMP9E8loZfqwRi7GddyosO9cOt7uMLx01P/LVnQigGdvb93IjBDCfJmVJEGYFtIOSXNatvj41Is9mv4nXN9THJTLa44jAJLJVfQAj+SJY0kKJLTNaNzecCAE5Sc7ps/PtBAkutbOfUdwVJw21iMrttumdWozoUyqtzALIRoqy4iTJPGJrfcb506gTRfCYi6tPe6dfW/XVPgJd57KQTVMQgTTHGzx5x4PqG4Nlh1kh2UCr8zD6H78+Kl0FpVQCwuzMd6ck2KkQNlzG2Mos1VsqXZhOAQMictPTu33J9OdoEzWZfZWfLojtJBgdNQHKcyVa76Ph/073aeQbqydsQd1Wwv3PN2+NO2mT+JQ0tGGs9LKVpNDwOF/0giprwlxJS6HmCLC74i/6NO5aznpFoBC8j1sTNqxzZ6v4o9gAszMuiEKxPHkFXMDwKWbu5rJeb5qZyTqi7ACIK32cL/BRXr/b69YbrlXZAqJW8DiJdLxhvlJkUbHX5Z4ite2+dW2ZOYLHRszhl7gLXmIcFuy5V4jxaNtF3VKYfWP0Z4Y+GeXJgkkCrsLYdFXHU9a1KgMot1IZjcEhJJicfJ+Kn7oEKQWjSje82G/ezOeiFcXEaoNEIlUOINdWLHra966QwsTOx/gqnvRRvXnQOsg4rf+LKkGRrAqH9hmjWZTrtKqAvi3NJKFUpxNgJ7qLfO0xek7NpqW63IKnMSVywAX2scLSB1Eyzxay7T11ghQ==,iv:L/I5VbBGpgkYxaSP1691TC67Hy6HltUW7ru7t1034qw=,tag:INESDuZajIM3rJLhhUIfXA==,type:str]", + "sops": { + "age": [ + { + "recipient": "age1e8mlg8wgp5rszgr9h9axu50mpr7v4020du5ry8s7wspwp4wftgmsfh4uxj", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3L0VXUVE4ZWVvVmEyOVBy\nTTd2MlJsbkZ3aUNCVVNLTkFsRXRBUHA2T0RNCkdhTjFTZEVvUXBKZnNid3R5OW9q\nYlJXdWVwdllKbU1HbmxiVFhXR3NpbG8KLS0tIEh4U05hbU82emgxdmZkYmpTejcx\nRDhKRnlEckFjMHUxeEoxbkxqaURRYlUKd1him6uNS6jYPqnFg4VvgfyzoMD32myM\n+FQUz7pGM1mLHu8/S8xpJSVxjBFzy+K1SkcyhWgI03tCQ/F/K6FKVA==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2026-01-14T13:54:18Z", + "mac": "ENC[AES256_GCM,data:r3Gc0AZ+Bgyg2Mj9PnR49qTjWDV6hkHCQ4VoIEQhF9x23539itX799Q6SKy0XtMteF9KhIem2+F+eN+Jx+z7NMlfxSF893SeS5AYlTiUTyF1YaIjyjw1+9zQ+k7cRlyF21xUOTjOivnUfUh1eSF7p+hi/jHmVddZ2s7LhY9PNhM=,iv:/TRTkIAP2SGvJ3wcFGB2WTrpUO+l+cCcMgpEx9z9/bc=,tag:gM3Vw1ubA1dVYbyaL/pyeg==,type:str]", + "version": "3.11.0" + } +} diff --git a/extras/secrets/GoogleService-Info-production.plist.enc b/extras/secrets/GoogleService-Info-production.plist.enc new file mode 100644 index 0000000..0ee8140 --- /dev/null +++ b/extras/secrets/GoogleService-Info-production.plist.enc @@ -0,0 +1,14 @@ +{ + "data": "ENC[AES256_GCM,data:622MUd5cvoqA40Stphcc+F8bv8fa+6lJmYqHhWsYl8iFqHWSJNpS0rHvp05JSUBeTixYWIYGpK+Gl+ZMbimvAzJz/1aZAudfPj2s04tn2BYl0oAL2Ptg0I8rQbjWLlgtZNHkx0ZYyd3lCBgU/WiUvbnmUjq2xJXdejPS3HHJRDL1zivItAQQRS/ieLtsD88QjqLKbLY5F/Nq4Zpi8PsvtnUAW5o82OT+43NHaNwOu0YCPhgEXCufr3YWc2Mp2RfDikDg/xBP+vzbwGDiHDc/cXuP9aty/ES1BNyK2EIrQ6ttH9wNvmyz/q/DoHOabpuMjGguj+7znuAFsMCW8fLqhKUb/qI4lk/xTBFOTQdJnRZsoNbllBT7zFKdUXndScTmeCOce06V3zeFhV/3hJBPMULiBO3hxuFA1CxW9THh99qjRIAch8uhoaDq0JkmBlveZzh4z2FQu08dp2YwYJgunKsNEyrW1Dxw7V9R6iU3f9bmtHGK4pjvjNP2aRGubyZpEbtqKel9SXOcf6KZFHPee2jTGrVg7QL44UdM6kbJOTKFRthT23PmUGO4852zB/VK1JFP70H6tUYzBIe4msfOU34pYivIjf5Fl9wWTWpuJ1bMIxhw7nWtxDiv5ItVkYzuiaCtqqYCr7W8KiQSZOXaWs7G0thG64jBrhmUEsL63J5k9ashsqblVR84LQYfUxxlGKkZr39VereJq93Qu0EEdjqPkAXun+Z/PaaxKBbLLp+Tw6Gi31uHWKOxT5tBPGLmsr33MpOseOTH2en6NED9G8MpntyGQdgKZOSUslYEFYTXTaxlcfUWKWyUxEyljP27xWnbw1W2gxSmXTSiv2BpOmQR+PWU81eeCeziyxkYDmGFzXLmliRnUVaOMhB3JtKBqQAR9jQ2qDu5nUP/tocF02yfTSxH1BCgVQ7KQ6W4h149YIy+tqAVf7SvgNauUo5wcQwQOGuceAdAecWsOakFKFECfPdVLpmtMKqft4FjFN4uOVRlf3ksh/nJTh36YsDrJwQU+QeRqBdkkcbNMLbCuMBf5pFvWPd6ZNEgHVF5+dUNtNGAZq4RH/9rukDDkXDmJSqgGFyvDqIUbpCpTmPom9buNQxQko4a/sErXrN4VGqCaHR1/5Pd0IphUB6em7ynlemO0/SDbIMuIw+tFLw0lcCq+Zz6PKlpoXbolaCXcpEU3xNCboRFT2jFKuPpKIJstsh9vOgSyHxoxKrb94VH8NWvO/8ATdh5mngdX1Z43qHmsyGkfFzM9w1J707HAycMRF19WdGJPgYPwUiNCSuNwO3rleZ6ncJOO5r9jikKr7SVEL5EzM+TZwexNv371bjGDZtkPKS6va1irKuB9JIum7lkK1x4MtzrprvHhC0LqH1eR061UPGm1QHRyHP1+7H5D/tNHmQuCX13GCqzcXosBi+MPK66E+0GjbbqJl3K9bJR/LS2dmsxWCHppY/MPCoL3P9esCjCTPpwqkn5x57LHCxQLSP+2EbHRmxhEEN1hYyqh3xhXX7RJppGQgKLt+R67QX5jxoaAMB1YQZfzQ9h/lUG6IHk6YMRTD4fcsJLxUMf2J/pnr4mAAX5q8cuzapxyYWO2xoHopg1Un2eH36RaCWf9x7EZZQx6cwHAt93LOdyqN8UP0T/OcDHzBwBQ50MaaQ=,iv:UZJ9unqxTIZEOKMB8tNfeyyTvm2F3R7Ti1mS8wHUVqk=,tag:HND6iOAjZoTNF0z58QlLgQ==,type:str]", + "sops": { + "age": [ + { + "recipient": "age1e8mlg8wgp5rszgr9h9axu50mpr7v4020du5ry8s7wspwp4wftgmsfh4uxj", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzLzMyK0diaTdIbmN4MDZO\nMlBSUy9mS1JvM3F6THVqei85OXJlNVQ5dDNzCmNuaVVGTkFsUmNrS0JaMXVBREZU\nWEpCZC92VnI5YlROTXp4S3NmY05jOVkKLS0tIEx4bW5HRGY2T3VpNkJtSnJwazN0\nTkJ0d0tucWhnc0FRS3JabjZlN3RXencKtp9BKoUXfqSrPzqyhPy1k8yBJlKLAEdP\nwOo46W19gkLGQSjjPEePaQI52jY7uHigazeyKq0pEd3bhOduSOBmJw==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2026-01-14T13:54:18Z", + "mac": "ENC[AES256_GCM,data:F4rh9dYp+3RyLoKfPWwaj7WUSqTGSUgL4xbXJR8/jCEgErRjRXJ/nQEiOvF9laqT9k6ztDj0UxlfwluLqIXDw8qDBJcRSaDHCeoFOxXcfOMSv5ekTRJMOdXB+SQ6XgRymOYVoi7Aik0O28/SYPpVbGGGHzClJeab6LPf/bW+/MA=,iv:LTVzQwDHjHsGmlUYiupnbOnVSjlA7n90taiGq1heSZo=,tag:rvVlyPeRpoadF6xgsVKCUQ==,type:str]", + "version": "3.11.0" + } +} diff --git a/extras/secrets/GoogleService-Info-staging.plist.enc b/extras/secrets/GoogleService-Info-staging.plist.enc new file mode 100644 index 0000000..d4e6a0d --- /dev/null +++ b/extras/secrets/GoogleService-Info-staging.plist.enc @@ -0,0 +1,14 @@ +{ + "data": "ENC[AES256_GCM,data:c5OLpvR5UrCiruiGUa3SDRkE0JPx3tyaHjPQD4e/hCIgfjxDO0mm34ek8B9NDmf5zYd8WSK3OkNJq8YyEEzYHUks2a6yEesbKMiK6n8kan4FHQBMP9DNO64e5A9r91cROnF8qVIge9d4cJglZkFWpjwcPb7asesyZce9solv195cebazY0DWMvcaoH2cjDwgncg1OixqsK9wdGT8NSxgPjI2h7sounmHRMIRs/YDZlZPOBKT+nwC0jZ5UYm0Gen3kys0ZZipWAzUynQiKRQ97r/pL6vecBXFCYjTAe1oPKgwXPFBnOHe5daybdi/pgK3ZZoD+d06BFppLALhtApN9XiWurDIQBN/p2DX0M9bresdWiulgOjGav58I7PtOym7YznBLghBDo3bIx19k9nV6M2gxsyAUTCyb1bK4Pts/BDYwPC+nzDDp622ODnr0zxEvd4lMPeH/42eFwBH+y9ZUicJpFewGWGGGdU41YxV1lMI5k01DCRPHFNX1lbpRcjvB/uZaNe0vuoD96gQA+Y+lkrBJ9SK7ynoCfMEYVLG0d6vvqapzUCF6Y2IG4K8VPKcin8ELk7Zbbc+V990zUriFLi4ZY1lI8XMjCU2uadrlgiGTMVqkxqP8XGxtLV12xP0g/vIKyp6v3g2BatlUOsu3ZPCC+8Xnx8tldfXWfLQjtJyj1fgahAuBatJuVNea06jX+MisLJdP5J7llNFfg0BoF37NgMhyyuDXkewKutQge0qzxMY1jWWgY2/5Ot7KNbeL3nNfK/PcShpcErH8PNAANfh7mtpRPUg1pE7USd1NzeHWfUa3FWlJ2vyc/Y74VwALlUegX1l1lOeuRcYS9AGf12miMgTbVwOxKmoZhpz3dqsPQHPZ7QU1PAROhjNgppu5kJldr7snxHLS20eRnYJFLFOL5E0nnBB1BW3JEUHqYqBHGRv/juQxH45uiVXtN9/zMXX5vvcWJ/roNzGrpM7mK3Owj1NMv/LKkLHRNR7HkmcZB3pMyltMmgKvCs8fsZuFk+KvXjS5T5pVRf2zHH+r/CoAzo/WuTpGQZrJ6k6Lic8DWHQi0gwaeQPvUkTWE30WjUTiWkueS3NO7Loul/QuGbuk32kA/tBTd8zuXrvR3oeU5IaBKbpMzp/rH70Cdubp8ic/L55HzsRLOY1ae8NsapD2FRjPHxxyWdJ7tyooxtIoFcKeZhdGidpRASU35XKdCfyo8iQjtu33SU+cM8lcbUaUE0EXdXxTQ/cyKwcUWcRoyApRkKHtjqd0eFlaJopxLG5zXvyzmdy72z9cOkHn5Our4+qU8TNNcbYtiCS9/+RN+YcZTP6jVbkEjRDiJMroltB71+MF/G6LEgqmnPAyvcBaCdlW/YHQSfOEalkF5skJuZ7Za5jO2CGtE9XbGDN5besA6BYvzh2QaD4VHTrSfT5T8PUXtbEAKMpfDDFW4gpZ6/M6BCBQRCA9rqCOae9Uo+8dFMKzAFqQvqzEMQ6nGKF6BjcvSCunTOzlpurhm/J8pgfmB0hQs9hfmaRKaA3kieqNF+PBIZ7mPH7ZXD1KRwq0cI8zBux69UrhepxkwKcK3kbXnY1lFIU3DuL+INiMscmJcrXZ1EEx78704Tce8Noz9zWtvr0AtiZyiPnqzWs47FUCKQl7njnDtdw9LW17y44jrNLLYWVJA==,iv:BAb+JxRIp8UNEF9BfTybMZelVGaDUtGnih9gpN8j6cs=,tag:B/RRqphlcfrtW66ffAaWCQ==,type:str]", + "sops": { + "age": [ + { + "recipient": "age1e8mlg8wgp5rszgr9h9axu50mpr7v4020du5ry8s7wspwp4wftgmsfh4uxj", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoVTZjY0RCZHJPQmk4eVhy\nNGxlTFZDT2tRbURrTmRNY3NwaHVMcWZhbkQwCmw4MmpOdDZ5YU5qdi9DQUlvTFlS\nR3BYS0dMajVpOWtBWjRRTkpWUGxhRzAKLS0tIG84eUprdVl4b2g0ZW9YRytJVWZ2\najRYV2tsNVBXTS9vWERHQmh0c0trSGsKTjYktFeDaiQ+8OuMAk7YSWe8EB80mxA+\ngQnlcvxDpeNJlyDtmGy1q0EkgJEKGX4jYAl/NX2C/us7Yb2AGRUulg==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2026-01-14T13:54:18Z", + "mac": "ENC[AES256_GCM,data:ejKsf15aeW96KBfK+2OvSwCoMXytfTrDv21r6ZxI2B97g0XVAzKMX4xnX7lcurqpPod/M+Ewv84x/0OOVUw96xmIzq0YkN5gVNDFt7tHGFS5KhqXgMJy1twmtbzS7z19poLMrGqsW5sLB1BFs2K+zJXfaQSEHPdztJjcRvPaFn4=,iv:AeLPbGLvKR6iDg/5BExnOLRNo1Sn0a2OUYCYO459Sf0=,tag:3DYEHnFVGWL5U7rhXaTryQ==,type:str]", + "version": "3.11.0" + } +} diff --git a/extras/secrets/age_key.txt b/extras/secrets/age_key.txt new file mode 100644 index 0000000..ff8f29a --- /dev/null +++ b/extras/secrets/age_key.txt @@ -0,0 +1,3 @@ +# created: 2026-01-13T11:31:30+01:00 +# public key: age1e8mlg8wgp5rszgr9h9axu50mpr7v4020du5ry8s7wspwp4wftgmsfh4uxj +AGE-SECRET-KEY-1J5TXCNK4HKT8UAWJXYH3MWV6UMP02XXUCSCM95TWRPPK9PVSQQUS48CGEX diff --git a/extras/secrets/env-development.enc b/extras/secrets/env-development.enc new file mode 100644 index 0000000..a94671e --- /dev/null +++ b/extras/secrets/env-development.enc @@ -0,0 +1,12 @@ +API_HOST_URL=ENC[AES256_GCM,data:tvM1FysNU2kiw8aX30tZKA==,iv:bxx3/8rfBVATn1TnJscQsxvqSQSf+/NMs9oTTnIjFHc=,tag:1nhSoxNWirj0O5V0/OP0jg==,type:str] +APP_ID=ENC[AES256_GCM,data:/1mIXcEkK62vKSKWXHEmLC2s3KvhwiiDjlpuGv8nJs3r0yN3IA==,iv:Z6/X56m8j4Eq22IMkte0+s8dDetFPjSQ2nF5a/JQvYs=,tag:smwSOmYpdFIPnSOYI8g46w==,type:str] +APPLE_ID=ENC[AES256_GCM,data:L5qxOUvr2Ht6,iv:GhBQEfHxUb4oIabsGu3fxD+r/3tZaJY7SUKqS63ogik=,tag:t4+XTX4vX24bKmxVSUadWg==,type:str] +VAPID_KEY=ENC[AES256_GCM,data:Hy7qqSyhoqc0aGc30egXXbCSw4X0lo1F8fg=,iv:eDb7xt+yEFTPgffLfW+yMdPmLGQ7tFTtSiStdAFXEXw=,tag:i9VROdqluknWyM+tTki57g==,type:str] +GOOGLE_CLIENT_ID=ENC[AES256_GCM,data:tPd4OGe6R89oZR+Wr6BC7L6CDSN8NUhTd6NMHSOr0rFHkHr7N5rfT7iHQ/h+wshqPVJnmSgyfZf5jGfSTfD4NGRGIJi7Ww==,iv:D1HB3RIWrxCI9w24pAZogAWySjyNmTe6wVecacnYJoY=,tag:IM59jxUQllPdQprNNMp1Zw==,type:str] +REVERSED_GOOGLE_CLIENT_ID=ENC[AES256_GCM,data:HhtFMOoR7s3SykOceBRZQ6hOeyL74yTpdtcm/BErGF1ULfRjjAn4GjIWWvuTAqtf6Cn3zaRkmvzlPUI4DitCW6g2lo7YbQ==,iv:iZRw+9QHHYdJPjjnt+AfYdNcy4mx9MPZsAP+vOL1b4w=,tag:4OS5reDgoR7NUKHKnugo0w==,type:str] +sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxZ3hwUXVneTRYcHFEbWZ2\nZGQ2VFpwS2pZQWhNSkhSYStVZmpHMGIvdEhzCkVQM1luc3NyT1F4NWFSZndSdjNT\ndmE2T216VEV4RXN6MnF3dmlWL0NMYVUKLS0tIEIwRnZhclIrMXpJZWEwRHlQUTFK\nbGRxYlkvZmRUYWlWNEk1Q3RQWXh2LzgKR4b6x0f+UDRgI5gkl/2IPB06wS6r31gk\nhIKOY1It5raXosiWvAvVMEEsscv+tWeSMIxNYqd24xOMFuBg4o/NXQ==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_0__map_recipient=age1e8mlg8wgp5rszgr9h9axu50mpr7v4020du5ry8s7wspwp4wftgmsfh4uxj +sops_lastmodified=2026-01-14T13:54:18Z +sops_mac=ENC[AES256_GCM,data:WogcS2Zc0EK5zZ6P28I7UahzTpOKEyE9xba/l6WJ4fSk7beZQIfTYUacxT76GX8sqFQNk9ZeGytT5sGLC2ZPJ2hEvKUvKBSVCIOELEA0AHbpzf9XgwELYHV+O+qpZLUIedvBjL+unAv6p1qJ6GeJwvfyzpZ4+wdfvnOrkLxgCzI=,iv:a6dNIOFKXi93dUuckIyaA2y2dvPE6PKNexwg4Gl8sRE=,tag:z+U1etrg1mTVImQs4rL2Pg==,type:str] +sops_unencrypted_suffix=_unencrypted +sops_version=3.11.0 diff --git a/extras/secrets/env-production.enc b/extras/secrets/env-production.enc new file mode 100644 index 0000000..0e82d25 --- /dev/null +++ b/extras/secrets/env-production.enc @@ -0,0 +1,12 @@ +API_HOST_URL=ENC[AES256_GCM,data:h6aeXC6RrFSTaTvaWCVXdw==,iv:KsO8KHDaWLwNQ6ZXrQzn8EaDNjt/iOEQSMqqaMbyv54=,tag:q1V/x/WbGoh546NiG04fsg==,type:str] +APP_ID=ENC[AES256_GCM,data:HOtwJ0DifwYX4WJf4fBzFK/vRVCIED1NBg==,iv:I4SC8kcjBmw1BYLXVL/zQ7WoyC3dg4Iy63nWD2gicBc=,tag:hreuCCgAGE1sWyBpHF/oOA==,type:str] +APPLE_ID=ENC[AES256_GCM,data:rBrdMZ7aAVFV,iv:AkOkgUP9U9n7HXNbTA1TLY0XKrVyONECF17NPIlPfyU=,tag:2lG1RHFT0nchsk5V+NoUZQ==,type:str] +VAPID_KEY=ENC[AES256_GCM,data:vJPL/UU0jCgZwvmfxfgaZka4Vn2HfoYumzk=,iv:VkXNqZDQVT8u2v+SBhfl0Cwqmv8uM3D5s/HJXIzh++g=,tag:am97/Ul9VBmNnqD2TlBpkQ==,type:str] +GOOGLE_CLIENT_ID=ENC[AES256_GCM,data:KGMx70ddhhn3EQBrPnQkRTjKJncBbESLUyA/W+QR7iA/HTdaN8mXw6N1ka4CDPDmaPXsujq1WHvTeTNEWHVcQM3ESDZQJTjy,iv:770n8q2A0cj4+YfxOM8utIuIqyqn5IPcUQKL8MpwD5o=,tag:y+fS165AuOpr0XJ4dahxTg==,type:str] +REVERSED_GOOGLE_CLIENT_ID=ENC[AES256_GCM,data:4pqBjmB2ccj1BGh5/8QXR+R6BcTizoeASJw+foxFFxxq+ctDutvrznxOOu+ba03uZh71MaoI2hzuYQhgubQaMksMhXSPX1cv,iv:WU+Ee8eM6148Y5AnNtV+ob0T9bdjuzV12mComjxE9YQ=,tag:Jrz1n1iuSd2UJBfFOcrGYg==,type:str] +sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzYkNwS2ZTL0FEd2lNaUJP\nclZqSVY1UlVadXZaVUx1SXNCSExFUjFiRUZnCjZaN25ibGFvRVQwdzZxY2R4MnFO\nYmFLZ1Urd1dUb1NtQUd5T3l1bGlGbWcKLS0tIGpCd0ttOGNnY2E1RjFVVWhnSjVP\ncjRrdGlYeVpYYUZiWFp2NXpoL0ZiYmsKlm5r4b8aI+KmmEZb8sUPBsDJpB/kLOBj\n7sJbhJmnGlYjlAs06VlaEgoGgrFyM2qNZZZvyz+tDyfuUq0i3qk5Ug==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_0__map_recipient=age1e8mlg8wgp5rszgr9h9axu50mpr7v4020du5ry8s7wspwp4wftgmsfh4uxj +sops_lastmodified=2026-01-14T13:54:18Z +sops_mac=ENC[AES256_GCM,data:ZgNi+f4qYUqVd/MHv8MdzAWO3NKTAI0OBqBbn/m42IGYzR6o3RSOZ6+AA+D6BeALzF5HzNmI/AG2ytVt3c0j3Nqldb3PY33ApdYfDwbIK8Cuxrp/r9Bj3h+s69z09bmsa0qPll0Zt9n5i4L7iVaKe0wxg9L0dMhKtKUsl+uAj00=,iv:WV5ndzMupbgI/uWGBx0Km9Xwt2De/AR2VyNAxc3KCAQ=,tag:j2TG4nYsHrFfrAxDBuR+HA==,type:str] +sops_unencrypted_suffix=_unencrypted +sops_version=3.11.0 diff --git a/extras/secrets/env-staging.enc b/extras/secrets/env-staging.enc new file mode 100644 index 0000000..938c234 --- /dev/null +++ b/extras/secrets/env-staging.enc @@ -0,0 +1,12 @@ +API_HOST_URL=ENC[AES256_GCM,data:lJ0RN7TMFbNUch8PDvUOfQ==,iv:yQU+9bdKQ242JRQARQfSA583gsqJ+qc0lgQ5/7yMu4U=,tag:IKCR2Sd3OxsSktx9qxn+pw==,type:str] +APP_ID=ENC[AES256_GCM,data:5jI5MVKA+4C/HxaVxTNrf+JsSfOmL07FMySx7f+yOocO,iv:/ffTSIK54wEwvSg1vc8eeQjjhBDy0Uwb+ukIi3XRhMg=,tag:jSW7/QqXpj7uwVaLs9nV5w==,type:str] +APPLE_ID=ENC[AES256_GCM,data:9FD+zUWO1UVh,iv:czfvXnmFixLbTkyUWwkLUl7yNk1SUj2ptJdv4gP5yw8=,tag:yFGt+0UpztawogveqhT7Eg==,type:str] +VAPID_KEY=ENC[AES256_GCM,data:tMui34DBGkDGbOiiMevN5eOexvDyfx6WO1U=,iv:cLjR8xU/2H31aFqeKlnJj7Ai1+e2evx3GrDuKMYYaM0=,tag:2s16ajWqlzDXPemL70A+yw==,type:str] +GOOGLE_CLIENT_ID=ENC[AES256_GCM,data:uWrD3Q5QMh3xRULfQSaqA0u4w/NbSJJamkYLMP7P301lxFDIa0nA09dYeMSVeyMcEaQUkfCc1lOAJPyjiNac3VANT+mk,iv:u7yrr+3cOvFg6qzczqkZYZqW7J8Zqer/57sF5Kmusuk=,tag:EqX9errxr89Fwx8MUF9zLw==,type:str] +REVERSED_GOOGLE_CLIENT_ID=ENC[AES256_GCM,data:clF1PQaECAfVQnJXTfqJQjhsLedrT+GhbNkY+UR3ME5g5zVUNreBlyzXkLflU+aQY+uM+MzT6Ru5TYEKDQZ7Zbq/RYDx,iv:SWlqVxrCTMhwCpPdxoZ49O3d6hIRtaOn/wbn1ctyGlU=,tag:tpYs+9Yv69vHmRJKjgEjDA==,type:str] +sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqM2hveWVxOUtaZDRML2li\nR1NDeldYTkljYXVCaTNrMm4wc0xhR1BSV1N3CkYxZGszWTdvMDd4dlRYUEIxUzJT\nRHViaElvNU1MWlNhU0xhTjFPMDFneUUKLS0tIEo3V2FYZFZkZ0xXZG05dG1qcFNI\nOE80dkZmc1gvVGFKOEtiSjBZVjJNNkEKOwWjtrrMoNVbLRYEby/+wAwftfcvfBBA\nOimLunTSxucd/cwAVWKU4T7UWpor2mOQKS2w/mQN9jmAOfXm49/UDw==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_0__map_recipient=age1e8mlg8wgp5rszgr9h9axu50mpr7v4020du5ry8s7wspwp4wftgmsfh4uxj +sops_lastmodified=2026-01-14T13:54:18Z +sops_mac=ENC[AES256_GCM,data:gSMovdVpFaUBiiq4YFNCjwlQBGhmq3zta+84PRTWg/O891v/mlCt0BdqHsF674wA3xXKi8yF0j1WyLkuWpogJkwsKZFaH3tWiApRMm/Nog5pMgQp72r0vRrNcluE07Nn+UVmXfQrbxpcPNz+NQNooLwxMuhoaylAzGjrPxtMG40=,iv:zuhBs2MQe4e5gKbJCFIkwngOPXljgQDcMnU9MSGV0T4=,tag:RQAxdDnD/+hSuhglEuqNLg==,type:str] +sops_unencrypted_suffix=_unencrypted +sops_version=3.11.0 diff --git a/extras/secrets/env.enc b/extras/secrets/env.enc new file mode 100644 index 0000000..ffc18e2 --- /dev/null +++ b/extras/secrets/env.enc @@ -0,0 +1,10 @@ +SIGNING_CERT_DEBUG_BASE64=ENC[AES256_GCM,data:9SorUfurg/b4+gfhRuAeiFTCY5jnmcNASIXNC6WAGfkRXU+x2rXffew2Pzo=,iv:GvSIwkEBHzJ/kYpb+XE28Hvr7Vn04Sr71ZEoCnasklc=,tag:+X/nX2TNq0oZpKSBvjuTAg==,type:str] +SIGNING_CERT_RELEASE_BASE64=ENC[AES256_GCM,data:pjpptAAUtl7t+xTMIMMvvDs64Vwm6KKaa1my3sl19wcND68h0bzWdrueBiQ=,iv:G6gCOgOE8/QTKqJNzcUDuy2rEhmewExEWgz3rvTFYQ0=,tag:ZVGkh6rdlbo9c/pgU0cYAg==,type:str] +SIGNING_CERT_GOOGLE_PLAY_BASE64=ENC[AES256_GCM,data:tM3bNhe4Hrh/1mF4QmWXBbiEPKdEWXdf7FImuysEW3dZYzVtMhfrxoJPv/k=,iv:7YeK2Xq5XHGUlTND5FtHGCwVRGmyq/oIFjj/fgNPXGo=,tag:QioldcSrSpczDoQ2TnHo5w==,type:str] +APPLE_TEAM_ID=ENC[AES256_GCM,data:Djf06pn0kd8DVg==,iv:NgiPWzJJcZ1saXLmuva4Yx7/HyL6iuGqOSp9BGciijI=,tag:4u6f+UFKinTobn0Z+btSgg==,type:str] +sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBIYWU3OGNkRjBMemF5WS9t\nZXdVRzFDb2RkWEl4MU1sL0JSTFVCL3lVeTBJClc4NXJ4RE5ERnJuVGd5MmlMdzdY\nNk1uMUhZdUorMFdMdDBacEVCTDFzbEUKLS0tIHlHc0FBaUdoME4wVXNMTm1Seko5\nV2dkOFE0Y2ZSTnJlL2tlNjBCSHlJbW8KuKJ5mk/2CKVHIL4pyhfmXabIzyKli4x6\nUTL340h3mUTkiYCONtm3SVprZbyenluCb3k5W6XQQV+xGey07CjDyQ==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_0__map_recipient=age1e8mlg8wgp5rszgr9h9axu50mpr7v4020du5ry8s7wspwp4wftgmsfh4uxj +sops_lastmodified=2026-01-14T13:54:18Z +sops_mac=ENC[AES256_GCM,data:PELYzglfTaJc4qWegRJigkiGqTg3+ayvYnX4Hz1uV+QI7LLS7rhcwqa93g0riZo6f1MSPbXvceSsUiN/HwxjC9zSUheB6tRI/c2jhsOzUJ5qnp7wGqyDWSMqnvzWsp+v1oq7Z8mTBLQjx9bwWkhItSqm602YcvSq5cM8zW6Oi2I=,iv:ld5dVIuNTDAY5/yXqEg24bQHni1ET/QOtD7PP6yIyaw=,tag:ziobxBh9/U4HuGsN7DMZDA==,type:str] +sops_unencrypted_suffix=_unencrypted +sops_version=3.11.0 diff --git a/extras/secrets/google-services.json.enc b/extras/secrets/google-services.json.enc new file mode 100644 index 0000000..9f1c6a4 --- /dev/null +++ b/extras/secrets/google-services.json.enc @@ -0,0 +1,286 @@ +{ + "project_info": { + "project_number": "ENC[AES256_GCM,data:BsKmI9/ptsuKCloJ,iv:FQwxRJe/HYdNXGIZR1JutkPyFVk8SfLl4EJKp9H41Bc=,tag:9ZCdMDuBQwssJd746QY6EA==,type:str]", + "project_id": "ENC[AES256_GCM,data:JBEzFSKRvgnOK4Rb+UJ/dCLSeFbe,iv:X9AZks1hpTet18JYYTv5lU/IPLhQHUwMyZanyb5JRTQ=,tag:SGWHgW7N/dBm3pjqQY/1Gw==,type:str]", + "storage_bucket": "ENC[AES256_GCM,data:GErmWsaUYqsWsK2wnukXcNxTdCquDhqB/IryTKgw7aqP,iv:oRObfc3iVdUi49Xxzk+N4uLXjpuwa2c/Uj5L4nhJPqg=,tag:niBkLn7zBCog5EajCgmIJg==,type:str]" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "ENC[AES256_GCM,data:Hz+40oHb+3weOT+p0TgsuD29/MmLBtQn5rmw28jOVeX8+e6NJwDu+LZLbmkL82/MKWnMGVdIbtY4c5DvH+LGJPb+MDtYU7/v,iv:4cYJH4+OgUqIicKYO3OYlxR2h/zdhtidqF1skNJ3grA=,tag:ZKOJC4C/dPzGCnBvCxE3/Q==,type:str]", + "android_client_info": { + "package_name": "ENC[AES256_GCM,data:UoSOo7LmuahJ1yyZTGBWEsdEXlyD8JWmtw==,iv:jBjwsr31rm9i0J1BA/tyVrShSTztOvyC7tXAXepJ2lc=,tag:5AvjjSRe7GMZyxSohISoRA==,type:str]" + } + }, + "oauth_client": [ + { + "client_id": "ENC[AES256_GCM,data:YOI6QVPwpdIGy/B4+l0S4YsLIyf89rDar3vAMMSHI/MFydnGVI+XTWhvP85kypLzYjv1bU9jY0HcCtOIb4T1zyLyY43xVhjf,iv:PoeuQ7lbH/xXBcfVd6rNme3d9UFstRDKFNwQcUg84r4=,tag:5gQWeGSKyoc1cR0qqszPNA==,type:str]", + "client_type": "ENC[AES256_GCM,data:cw==,iv:hm8eBG8zn6poL19PlSMPc9gaeZd0GhEN9zWa/HIq55I=,tag:luR8acd49kDDHWSUwoBlRg==,type:float]", + "android_info": { + "package_name": "ENC[AES256_GCM,data:Vq4FK+gM4+YXroV4QWQMdldifcIcaUFBFg==,iv:VwSUIyx34OdgIiv7CeuXsFYTRd9UaOxaCECCrYKqx9Y=,tag:LuQKHy6HVr55XZ3Hd9fpSw==,type:str]", + "certificate_hash": "ENC[AES256_GCM,data:FJJRdeZeWUK4rXKUsGU9F8kS9pumt/hpvpypUzGqXl1meCaW0hPdQRU0twwhAWho7z9jHtnqMIR6a8roidyqTD80jCiNveLu,iv:ZMp4gxsz4Ce61ryDYZ6l8JMC+O5DT6g9Odi4i0jg89Q=,tag:y7WrKkM274wIBEoJR+HkwA==,type:str]" + } + }, + { + "client_id": "ENC[AES256_GCM,data:qgB5aGEdjQoRkSz6Q2D3ySGrbwYHeUNKLwMrJ6M7j8PffUCoiSly+hK6ImKkmGm/ze27MQIvhJYSNrgwcGrgzAORao1A8ZaL,iv:S5EU0qyV2/q1fUiwtc17gmSLWWzXf/1ScZ8k3BYjgrQ=,tag:SCX+TKx3tsJgAPxGzuwxZw==,type:str]", + "client_type": "ENC[AES256_GCM,data:/w==,iv:yGg6Nc1KI//yP0KntKxaiRS6EQAbdP0T5OvnOm/rvXU=,tag:SJr8nhfJU/OtqlGOSshZSQ==,type:float]" + } + ], + "api_key": [ + { + "current_key": "ENC[AES256_GCM,data:arsk5Kz51SNqdMKaJkryeMbx2dy9Ul93dEFAj+ZY9U8ddPN/g56KxxG3s6lnpZRchQZpDhWTbT12FLJ/pXKPYKozE/eia/LW,iv:itWpp8G7HeBJFp6g1tNGJsz3PatVeh8mn2s9lniBajY=,tag:IVC32cHchn8N8PArM0Xqtg==,type:str]" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "ENC[AES256_GCM,data:4WfXMcnU4fYm6geG/+ayQ5KJr3LrpYuH0hTUipvKXAmXsAl5xgABtM7FY6Seg2t9LU83xtTLOZlXZ4k1wja8OWKNUtDKUQbz,iv:2zSPjN3FLfZ2vhswsh+mbaUDFZsXttPSLRKfHRBedn0=,tag:uubKbKjUhDETOOIA9xcdhw==,type:str]", + "client_type": "ENC[AES256_GCM,data:sg==,iv:Z3druPSCz8/dgyRwFB4ZHdwsOqIhDAEsSAjkfcxpfZA=,tag:Hi2InGfWurbB3x7FUYkpDA==,type:float]" + }, + { + "client_id": "ENC[AES256_GCM,data:kEjTXNaZ3E+1DAr2ahybRk1KHBvFa9BLfT/jtHuON2pb83X+0b1VCjpZPwyZ41cZ4DX0l90NmieHJfiAJUeCUGWtpr+OEHpY,iv:lB8E2jDRYrolcAtwZ9IdRunghIc+jSZCwHx0SadH7Cs=,tag:qoEQjEmP/2s5+ynaI+AMqQ==,type:str]", + "client_type": "ENC[AES256_GCM,data:8Q==,iv:DMd9I9dofZ/sNu+0mOPPO6ZTM1tqIg2KOgEn9EerpFc=,tag:EdJNYxUJ+KcLUa98cr6TFg==,type:float]", + "ios_info": { + "bundle_id": "ENC[AES256_GCM,data:qXe9XlC9HEoZ1NKqOFyd882T+t6WczIyFaTaPLxdq8dD,iv:e5Qu7wS4DMcPZPrVQbJcfupZyx2clT5s/w/EZrTNtko=,tag:ULxOA3I6PeQYP5KPQ0aCjQ==,type:str]" + } + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "ENC[AES256_GCM,data:LbCcmduaR0IR394vkf/NsD3ztpBz0d6PzFPO6IhvhjlDg6VNEIJd8bqSmEWjNUagyWoKyCbQdydYXj6P4A4zUdlNzLXKT0sC,iv:mm/6Z0NyKjToPrRqIdk0V/ZcUo8eMHIGzWgfcwsTJBQ=,tag:W7PARyuhYGibUDfuGlpDIA==,type:str]", + "android_client_info": { + "package_name": "ENC[AES256_GCM,data:Ljt0euhldX5id8/98h+jcMlqHR5yX5KsSoF6uG9NtQ==,iv:R69qvOUVIhGOFFfCxvtDOdgX4hes5uc5Lj3HVubUItg=,tag:iIJxbjQeOwbobfT9BrsWWg==,type:str]" + } + }, + "oauth_client": [ + { + "client_id": "ENC[AES256_GCM,data:d51zcHZxUO8QnM7YGWNEKcTtC+FvY7dN5o2Zx/krPHr7I0C3CnJwGpGxby9diA+HR+0bsXfh4nWf65viDSzpEsQtypP33HCr,iv:XxVBQSV/a5qCPt6E1iUuoLkx5f6VLcszUm7MI/13QHU=,tag:f0Kjdg7uz1030LOgJtsUHg==,type:str]", + "client_type": "ENC[AES256_GCM,data:oQ==,iv:4Z9PduyLdq7C+C4cZittc+2wATlvjdQT/jSoa66nRZM=,tag:juyLUbF8fy6NdbY/nml74g==,type:float]", + "android_info": { + "package_name": "ENC[AES256_GCM,data:OMc//RMWz+mxNgCbNUPhK7erW5l+alS30mGmIyyPeQ==,iv:YnNXZDbhosi9zGZCBinZcL+8tRqT7Zjy9PsuuDM5FZQ=,tag:5r5NacNYHHtltqJFBMzvTg==,type:str]", + "certificate_hash": "ENC[AES256_GCM,data:W8QxPNI9DLrCGLaQxFRLnso3g4z75gfMyo3onSJXaxx4N2hya0QCyCra2uJjGXnz5KhisE+3Yd6LK2ph2mJsAWX0kw/FjckZ,iv:SdK7xIOxrre6sYyhYCIq9v6DZcyiKkA5Phl9fgzw/SM=,tag:86+KIDXSfDbOUdwdtAZ0Fg==,type:str]" + } + }, + { + "client_id": "ENC[AES256_GCM,data:fKWYQVQqK3LyIWPpD9YH14/3RT9A2YFKC4sxbEEJWx8IuOH738zXb5vQ5kQ2P8oF5adkpWDgVzc6sebbogBK7/kubfWzkvFj,iv:9lh49hL59g+EsJabyNgXinXOsCDQKFTphIW0x9LkYFA=,tag:+WTZ/MMzHLXzIjbqopdwKg==,type:str]", + "client_type": "ENC[AES256_GCM,data:8Q==,iv:BTZJhxcseZswEzHgnM26Bbi5xhxufHszzD2JcxEgtg8=,tag:etcDJFB54GD+PZp7phP9dQ==,type:float]" + } + ], + "api_key": [ + { + "current_key": "ENC[AES256_GCM,data:M+VR4ZhYOx/2mr/DVO8xvhhdHVhYOZ7YrqHkP7FmyUmJpEjnbmospfF4zjgAi2+Xeyiqnf5HOZkiP7ET4t3o3Ym6TPkpy+QR,iv:wMEUsfSbyuavUZMNmoxyYBq49b6CfRVQ1K8BaxKqgd8=,tag:HmCbjtzy2Uh+I4MiugWecA==,type:str]" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "ENC[AES256_GCM,data:4YYIc6j0ZTwslLxySmjAgAkJhUQXfVtep7vl5ZXdn/4vupICvcw+xltpjJB4NHCzzXtQEDjV+r0McyYrquzrcoVQyV/FawzC,iv:NaYcA68ckJT84EwsEgs06ab1iAElnolE+l/NUar8hL0=,tag:CvtrfVoIKzs4BqdPmQN2ow==,type:str]", + "client_type": "ENC[AES256_GCM,data:nQ==,iv:p7cs3GOrAJD6DXB4a+neO+aS/ka82/OEHPgXacMZH3g=,tag:PriQpppShNnuryEvqYQv6g==,type:float]" + }, + { + "client_id": "ENC[AES256_GCM,data:vr4PWqDByhzwQN3KXGIqgY5UYqBc1AQoivxKKAR3i2uGeFEnB80/clTfoPyuUZysZt2hgKVKhqqVePRxUCw6/UR+MFKxVLOp,iv:sWeWRGUYh1GZMzvgcmSphopOdWbidhS43al3vvNt43k=,tag:/eeISY04IAXUmVJ2nCTz/A==,type:str]", + "client_type": "ENC[AES256_GCM,data:kQ==,iv:2psX9sccDn13Vta4lxVIBvJP169zKaHEjGqzqiCY5ns=,tag:uD8PfBoDApb3jmllrps+gA==,type:float]", + "ios_info": { + "bundle_id": "ENC[AES256_GCM,data:rpTZ/ezIxIZB+0JTEwVJ/SY7Pyz2EvU3EbH2Hp5qCWeJ,iv:254qn7+fiRmNFF8ibi1BCtqULw56BZK3VNKqUQUPmTU=,tag:v2BSgOXmGW3D0otihWu0VA==,type:str]" + } + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "ENC[AES256_GCM,data:u3+n6uGaiOeouOjai0Kzhk3TMafHOu1fIxgeRQCiMOsbG0gC+3IN5pkV8/oaHEULKO0B1LvoErEHF9DQ2tRB1IzoOxQSbsQC,iv:4wN82qcsUzq7bJHtzZ422mmG8PLM7w0qBFur09tKMvk=,tag:v8cel49NfbD/GLEq20q4Dg==,type:str]", + "android_client_info": { + "package_name": "ENC[AES256_GCM,data:7fgzIBFCzI/0g+aDtBjOkqGGGF/5PFmV2XWHNbBY2snr,iv:zciCfQ06q0X8e9vaxRNd5loo/kip2QWNF7oVzexMHlo=,tag:UCmT81zeJAHQJ/zu9la64Q==,type:str]" + } + }, + "oauth_client": [ + { + "client_id": "ENC[AES256_GCM,data:KSZt8Xdl6QvrkPxESKsLa6ccz2vGhDArWlTqkgGsJa6qC3GWzUgIHkKoAohbKDrx8ZG5wM71V+lf7grX5HIylFt9izDxWg/Y,iv:xhLfkTTFUWHnCMVEOo1SJ9OXtfCnWxJZUYY2epzHA5Q=,tag:w+XC4VDf8g0n6cX/5waIFg==,type:str]", + "client_type": "ENC[AES256_GCM,data:lQ==,iv:X3KebZqbNrHBweP8Omr45kMAyIRde0CN4fPw6MdHtjM=,tag:Kk7V3pFAhnlDwb5h5z8IqQ==,type:float]", + "android_info": { + "package_name": "ENC[AES256_GCM,data:E6/xdJ70JZfCo40dqhVK4kQSMFJavabEmnSxgouADzeS,iv:VMiCXG4sVEjkMKp0ThaCxk+akXBagF5MpiS2y6AX2nc=,tag:LMA8Djy1xeM1JL+z7HWT2w==,type:str]", + "certificate_hash": "ENC[AES256_GCM,data:dm9gFNNz6F1esJlFIsxPlQ3RK84fEP7KpsGP/3e0massLju4powzTUmVVyLQI/52R7H+tCEGXez1W+vTS5i3mCzPHtgWDUgb,iv:PvSKN4lKpSSN3CBw6w5Z4sUl0/zdzGSoc4kCB/iSr7c=,tag:azXwo8Q2aHsBBhq8wmwCDg==,type:str]" + } + }, + { + "client_id": "ENC[AES256_GCM,data:AdYDGHzehGf8Fj310em/vALwrMtNUTG4d77q18fmftPJtGQT1XQFqx2TIpuUFSzEeZBXOc3eVq+S5H/aWKsYDIg6jysUt5Nj,iv:/x1IB+dmgvdnhLQuJJa0xI31D0uv4JinvGfdnw/Vwl8=,tag:TIEEsxuAixmBo7VUHQq2tQ==,type:str]", + "client_type": "ENC[AES256_GCM,data:5g==,iv:s7Ww7So+UEB75JJYP5r8zvpW1EAwgxx6Q1X5cGJ8we8=,tag:3R0nAcQSXkARmQpeyaC7kw==,type:float]" + } + ], + "api_key": [ + { + "current_key": "ENC[AES256_GCM,data:HgFj1AOhl2nVdAmPYVCL6qKCsWZcjfWbaOvfpFYYrGGwaBJyASI265jC34Qy+MG9aDBY0C+YUIflKwHObtSN7rRfekGok/UA,iv:BugcuaOJke5HOLDcDzKBv0wcqVgliPU9zcFYKsZ0XgI=,tag:aFv5Wjm63q0UsPmQ86ewFg==,type:str]" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "ENC[AES256_GCM,data:Kl5Wez9uKTQZlhIZm82K0CnJFHw23x670eFvS6umicJd5Q7plCZxMSokL7yyg93P7IER2RUvcyGr51yPre+4qEIgVYS8Kdu7,iv:ThXM3jIyxSgVq8LB9XLwkjGgPhO3HBUBYahwbgIEKhg=,tag:BFHkb2clTyWEmkGuHiiU+g==,type:str]", + "client_type": "ENC[AES256_GCM,data:8Q==,iv:YEc5eQ4BkCaVZNXAIYIFPXqQ3pntAt4cqW/GpB1KOkk=,tag:OSFvi830ITj5u4ambKtKqw==,type:float]" + }, + { + "client_id": "ENC[AES256_GCM,data:aRqLMyiHu96wZM5QZ+3fD/8WUoD9mA9jOgv/GN8L3NrrO7PU7kjgNYXtCDopClqz7MfgikYt/DZpaPrnPcERCE5gCSYMsp4B,iv:3MJ4mG4NGtGyqFMK2IFgLY+3CHk8IYXVNFDPsvKdJ0s=,tag:i2394XJ/xfs1svOAL1xEBA==,type:str]", + "client_type": "ENC[AES256_GCM,data:YA==,iv:yVFkrsMwry6DsRE4XuJbfL7XYzsiMD4YYwEmGCJQh4I=,tag:y2LLLyAWpE7WPnWYIOdPeQ==,type:float]", + "ios_info": { + "bundle_id": "ENC[AES256_GCM,data:OTrn6jjdxvaVMYRBogWFHhh24Z3XtJY4ML36SaNjF71j,iv:Xe1Sf7g4wdAiOLZMu7wrY7VyRnRw0iAMoh9G6SuESYI=,tag:3d2ZPFy/3hmq0Ym1uPENYw==,type:str]" + } + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "ENC[AES256_GCM,data:Hqwil+Bx9uc55ARP89ysMvBF3d1Y3jQcf04kH9fpBaKsrGaNjFVb54mZr9181z7I13Ro0h762AUPJI8RmIImB7op4trvMKQ+,iv:N6LRi4gnnTNEvxZcOgZ3KaGfgk1xVbyuAWVfUq47qyE=,tag:2UnilAza8c8aB8CMaATBIA==,type:str]", + "android_client_info": { + "package_name": "ENC[AES256_GCM,data:7X1excox45oabII8fARKo45jdP+osrCHjwlRQryP32za5z9ZEJVP,iv:xHo3E41XRUbHSRy9Pkr8YvFvBb6WNPcbgSBpIiiUdjs=,tag:5CD9nIG2wrA0gKjtoMt76w==,type:str]" + } + }, + "oauth_client": [ + { + "client_id": "ENC[AES256_GCM,data:1M8isuYrzOlKL7GlmUNUyOr0PW1onKmTuR1/Fed++8DgE6LJkX3G7/VT/8QnvxCW3sGDpEsw4MMpahpj3cgUo7qylkAX4pOV,iv:7V0Qd23i+QrZ1HcZifafbiOFXPE/fDNU4LuhtDp7bh8=,tag:IuX282NE8ezwS+nclfk3Mg==,type:str]", + "client_type": "ENC[AES256_GCM,data:yw==,iv:WCjqnh3aOBEoshOtUnRxcMdmVpHTgJdayj4yqws7PWM=,tag:ynXYfGE20ozdxHTjx7uFog==,type:float]", + "android_info": { + "package_name": "ENC[AES256_GCM,data:S0M1nC9KFg7+gkwyl0UUMXK83lZ8plM6vgrx/oV7kUx8JtXneUrg,iv:gjhDJdjN9EhelkxWcIzYTXPM1GXfUuRWpgK53mBqbdY=,tag:bO4IJBFDG01uFdC2t/opvg==,type:str]", + "certificate_hash": "ENC[AES256_GCM,data:QliekQ75VLgnnPRKGFqRKjalXiPR3/Nnpzu9tWz37ZLDeZkylRVwcJH7PfCFudOjtkgFTpmdHIFzLtAXNeMlR78SHItJN6yl,iv:WdUCojYbZsUx2ogNfS36cfn9LArf8zMRO4Kt1Ip3MMA=,tag:1OAVKukjnKs8zqfbx8KbaA==,type:str]" + } + }, + { + "client_id": "ENC[AES256_GCM,data:GYo3SjfCcrZCAaQh6EwTdl9pXroL3l4+5RSdfCvCkQVmpbZN1Wp/FbRJvo4cP9LlMKylNqw6cwxhfBHfiDj9+REkveISQlqR,iv:15Gi80kS39ZmaYaSKkPatHe/E4N4ouPlskJaSzYLhMw=,tag:/sYvpxaWByGPFcWljCH3lA==,type:str]", + "client_type": "ENC[AES256_GCM,data:+Q==,iv:RYJVYaZ0yO2HNPX+ElRb2gLuQDp5vLI3G4GjhK7HO+w=,tag:rmwdot9nwdSi9IabGdUtSA==,type:float]" + } + ], + "api_key": [ + { + "current_key": "ENC[AES256_GCM,data:1CZiBs+10i/Tg7h1w6pxTv7N0vR11qQegSTGqt/I10Gl4DHQIYSfVUxmKHifppnw/VtMhYTzH3joIGw7IagFWD2lfwg12uqL,iv:Mq/sle8Y73Z0Z1TW02pWkt2nDn/gO4UWcPWiT2qaViE=,tag:hNZ1bgpU9eeRtvrPc4Fb9g==,type:str]" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "ENC[AES256_GCM,data:vBPet/4f9m761dk02u34661V/Nep4i1tDrCVMCI+SVXHAjGOglJlHh4Drb3PM+iU9RXnVf5KsiC7md+9k8tSdFNB1gx7L4+H,iv:3WFnP5ksbXA4WubFfjWDkH7yVx/jqgvthXlYN+1pBj8=,tag:cIRLt0kDcjaWqnnZrzb9sA==,type:str]", + "client_type": "ENC[AES256_GCM,data:cA==,iv:9juhJYJjaE02Jy6+zEQh8/NojThiuqCEe5aOdjA6HcI=,tag:2+dVP2Dx0xRPQcRnFZvOIw==,type:float]" + }, + { + "client_id": "ENC[AES256_GCM,data:LZvnfyac8vqcPDNplYvMf0cCnGdWZQ6//M9z1SSr8/gMqvmbJoEMc0ADbpRYk8QOlhcr6G2PyL8kZ5Dizx8YiWyH1snmB4l1,iv:QeUghc8FQnkwU6sDINWAC+S1N1nR7Th9dek6SFSpw2E=,tag:YrAOLkzsSMIg/bKgKcRfSQ==,type:str]", + "client_type": "ENC[AES256_GCM,data:Rg==,iv:HcwhdTCkkDwmF1XufCU5Y71VYWGMf7VTgMjNgDFhVTU=,tag:gfMSlAOMSGnhrnhlAwahPA==,type:float]", + "ios_info": { + "bundle_id": "ENC[AES256_GCM,data:jIbUwQj4d+/G86eWekj02FpLPXU5D8t3d/fuFmIXRtbz,iv:YOq2gjMkpIyyV5+wTVmmIhXKbtUvk97cwT6X0aoKSqY=,tag:w/pRKaNKRgaFJ90pmLBroQ==,type:str]" + } + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "ENC[AES256_GCM,data:cmSeYOuk7T8Kbb0skR8cbI8aXXLJLFoYs+qDLuHnynpqqJvYku2WtOKpud944+hYkdWIBCjEd2BNUdeG8qleaj8gjv7zwz3O,iv:Uuv2a4GfaAgBAwbKtM1UXstarOETviUa7lXGl24z6a8=,tag:fgNJb9ZlkphZSmnMqh3NvA==,type:str]", + "android_client_info": { + "package_name": "ENC[AES256_GCM,data:0UWZqWc4kQCp8X++kxSkCFGSC66APOX2ERWMELgyeU7J,iv:ungqbGBN0PSRFrkWmZZK0HoesJVXG1HmFwuQcKCWlzc=,tag:4BoJ6iU/fZ65BZwAzy+lmA==,type:str]" + } + }, + "oauth_client": [ + { + "client_id": "ENC[AES256_GCM,data:z26lmtur8uev9S8nydodY2zkeyCBhvYVPvVg9+BB36+MvzPes+K+mgSsbwPUNkrS+EPHg/jrCsjFldtmXz4WSl+do4M0P0NA,iv:wkEr8Gd+/EWWu9s7GZb5g1hoz+A4L8L0jCL47QiQmnM=,tag:3DkuD5+d86Y2WCZQgaYxhQ==,type:str]", + "client_type": "ENC[AES256_GCM,data:Dw==,iv:rtpG0WSW7ZeLeX0z9hQYo8AISoup7ZVS7bLHBJJZ7DI=,tag:9svN1y0IkZoag3BN+OnV5w==,type:float]", + "android_info": { + "package_name": "ENC[AES256_GCM,data:S77ti7Y7LE0lWN7u9+N65O19M03auSNqcvNwrawvUGH/,iv:o7qZ1Uv+gMUJq8s7GzlZIGF4q/+WmUhueRu2kkwaEJk=,tag:Bl32QF0YluGeEyQ6a1IJ4Q==,type:str]", + "certificate_hash": "ENC[AES256_GCM,data:AOnJdGcoJgQW475NB6d+I9qoTwvCCFbrTCKj+cDt8iZDvPPPfrInMIUnpeEGDjnk/AHi5aYUTaalp1vCpJvfkVUUF7diYXzw,iv:r0bcfi+CW2yYqDcQRVVZN0mOtH9VLZWlvXTrm3xhZcI=,tag:ShjP8Xhqc8h5xxcd7AAbKw==,type:str]" + } + }, + { + "client_id": "ENC[AES256_GCM,data:Ah4v1+koZdWl+p1aS9qEa55WDuN+5/nt8izwnQgIbQsf1UJA+LfnUrkbfJ7TXr30YamY7MwXR4oxaSqM2imxqy1/UVWaOc5Z,iv:errZhvYQIivvxVKRGpUShDmd805+bVIKorzOsx9oyT0=,tag:WqLSoAddcGJHtkEzsl+4KA==,type:str]", + "client_type": "ENC[AES256_GCM,data:hw==,iv:kzGfWUy1UR+872lDGlgm0qIpwTt5x0dHv1j12XGkAug=,tag:5fjacP/dHHis7HzxaNhkHw==,type:float]" + } + ], + "api_key": [ + { + "current_key": "ENC[AES256_GCM,data:RFKi2MTEzlVWdast/YHxCztcQNBVrTI//jajF5Z9PHRSUmOVfH3WFBJKI160NyOtM68EpeZwvOix1UdcZhbcnOpSBtorkxYT,iv:X7eA1jy3w7DU0HWgeKhB4QDH+nbiTXDz7t1pyZOYoOw=,tag:aO2CgLtS1u3xIIK7DqKXqw==,type:str]" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "ENC[AES256_GCM,data:w+gcQqpUQ7NOkq9Y1tBvIFyy7GKe2g/5Mv/FYRdSRq1BmQ2Xuwr0tmhPp0G4QP6KvT4OsjKUYTax5qkJbhGz9NEXRRWw6Ob4,iv:iagjgOhd4cAmrzCg5WSyIysVi2aq0FRG1uoFdzSvOt0=,tag:ZwxWoYneoLeejllZNdIfYQ==,type:str]", + "client_type": "ENC[AES256_GCM,data:/w==,iv:dL2CGiJ/eQhORE+Gb525Na8AndMu8J9caEHyPj+HhYk=,tag:F4S8eRZlD2JKFkIBFAJ+mQ==,type:float]" + }, + { + "client_id": "ENC[AES256_GCM,data:EcGwXqdXIdUgXg9/KNhuTQ6Ib2e1Ie5y6uXKnfC3Tl/iaklt6BmWClsgipaeeannyasZjF7GWsUmkOYCfAmhXaJiqC+gUHtA,iv:WejZJvSf6KvfFMPU1sI70CaOyLWxivDKBbmmhUxCsVk=,tag:QGQFW8NrYWuX7jfdvCWFoQ==,type:str]", + "client_type": "ENC[AES256_GCM,data:Hg==,iv:ehAHJkgTlvp/qwopjZuyO6B+w3TPmMM6msJdMtWRjeE=,tag:AfMsveCKY3Lx+6ciLPe1eA==,type:float]", + "ios_info": { + "bundle_id": "ENC[AES256_GCM,data:wXFQ706udiP6S/6OuRz1/Og6PyEN/qBBcoMx/lu/Po01,iv:fIpXIEHrspZr0CgKArsbLpCqagqb4jg2YClknCJUEGE=,tag:2fb/gP7+2INGrzAg49e5KA==,type:str]" + } + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "ENC[AES256_GCM,data:d/UJ7Q2U1cHlgKzGgzaFJb5oigujtACdyBDbSEck3OnrYMDX6BtZ3az5ujzbHe9ZMghkS4JbrK7Xr4SKtP08OiL7+RFX/uHe,iv:pnnDFAPD6omBXVCDZhgPAPJNrRVPo3KStKM2I9uH6bc=,tag:+IqyMm/dYcAyhB7ZxHLQcQ==,type:str]", + "android_client_info": { + "package_name": "ENC[AES256_GCM,data:We3PNtT4w2wwj81aXQ2n373Xk4Tif9JsLev0n4cwir4Z2y/ABO+O,iv:yt4p2H/tUtVvcgyMs27NENwtqGQVz4u1cVpE4vRNoQQ=,tag:5FWHbZ1b3RLGcpPlQs8A+A==,type:str]" + } + }, + "oauth_client": [ + { + "client_id": "ENC[AES256_GCM,data:hM1TOLbEYy7NE+tePupYl8X7ZgvTTMCWxcpye8CBzuKcR/H2Lq9jb1tU8AodDN25WtUR7j2F9qlUYtOU3yArspsOwZm4fxjH,iv:PC7ecgipqNEwGpj2V44CFpJMhIJNnhple05RDArXXy8=,tag:8kam3xsQSFKsKtXwAQSM6g==,type:str]", + "client_type": "ENC[AES256_GCM,data:3w==,iv:oJuumB/x+IfVHRx7WLHL/yY+meJ3ycfRYh0gN79kVYo=,tag:CzEtSXZjCyquw7VyjKk0FQ==,type:float]", + "android_info": { + "package_name": "ENC[AES256_GCM,data:sbFjKtbfMNBHqYnYmzer85si4wlv/OYFzZHneL2jPW7rP01qQhvq,iv:De8nBjSnQUa6+8xh/HTwDATKKLnsy7AHZeTi7nJwvxQ=,tag:66H6xoVGD2PZqql1aeInPg==,type:str]", + "certificate_hash": "ENC[AES256_GCM,data:06MLFOl+U67gjAvQv6xdAmvermEAwM2E6vxsErmTBZS9d07olr5TlPtOl/L/uJwJFv4YEKs+xAydUWvuljmT8nlp69x+SgMr,iv:aXmRFxkEAUvtZSjVKCSFtgGDHimjbCBZ/NXQMlZm7LE=,tag:Rs/sVNB5e75kL0Z0oDfPQQ==,type:str]" + } + }, + { + "client_id": "ENC[AES256_GCM,data:+Y9G8f1B4MKi7j6kqcXSCR93EY5ngbNmWhmwic68rw/LwSZhxZSMofWTz/MLqJBihMpgsqtOP74YXtRZZVWVYZKLBLhQ71yZ,iv:7qiVPWkLMQE3r3tRD7htq4+qVMtbQst3aYRLMaJO0Io=,tag:EwZzrq8/zvJEAfOfwDXr8w==,type:str]", + "client_type": "ENC[AES256_GCM,data:Kg==,iv:m88cyBR7xIo+LjCPtM/KNWEWhfymWDJIE+CxgeYLYmE=,tag:CSRcELlWIdGWY9zHkDrZ/Q==,type:float]" + } + ], + "api_key": [ + { + "current_key": "ENC[AES256_GCM,data:X2apuj3Sqbl6ijuRulRSFQrwZbl/8fTZT4Ur+5aDObHBDzJomBp8vbmoZQL/HM+ufVUSNxAk3DirAKRd3Nvt7Ch0/yqdm3GO,iv:sC315L09NceFUMBDCmPxyE+5yBeAFpycmnq5ITNaLUc=,tag:SoGxwqFId+ZsRH4BQ9XIiQ==,type:str]" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "ENC[AES256_GCM,data:RJJzPzZ5kxOaHgPko7WLkK3myp2D2u0HWdTGA3ZB955OrWmke0KboPZuEMJpTEGdwlCEZyyVTawmvcNt49gxVpL9HS+w8Xc3,iv:PQjuixjO2OyT8ZL/H6N0RIfi5QdXtlU/LUQTi+d0PTU=,tag:uuY1TJzJeKsXvXBeBFMMgw==,type:str]", + "client_type": "ENC[AES256_GCM,data:1Q==,iv:U6FtdUbwuEbWDRls/iRiQI0tiovwoUyBGnqHCipSCds=,tag:G3i903JsSeqU/l2oIEcSCA==,type:float]" + }, + { + "client_id": "ENC[AES256_GCM,data:hSwClXT0VvcqLvK/s1cd9z3S8zvjLvfk6NmaaG4pSYnnKe4441xfXp7n3b0GymNxZIR+YvjqYXA7A/+VAdocxFPE+9hGGy8j,iv:sTlWJFIFoMTGZaUyIAFQzNrGbGmcuANGpfZfrOrY2cI=,tag:UYzt0t3TtaAibYyINYsHgw==,type:str]", + "client_type": "ENC[AES256_GCM,data:QA==,iv:icURMQolQQbt924RUJu4027zubevbl4af3J4VSYT4zU=,tag:Lk6mXOAjmYiJuvqiTALDPw==,type:float]", + "ios_info": { + "bundle_id": "ENC[AES256_GCM,data:YN+iqKInuqB0V9x0xptqQ9h+l34PPLXZkswfSy26dVpX,iv:fs2X+ZXnYh7r+moGMjqnUJSJVyWwHTEhuW6VLhiMxU8=,tag:+PY+6HjeejvTd/Ja7169wQ==,type:str]" + } + } + ] + } + } + } + ], + "configuration_version": "ENC[AES256_GCM,data:gg==,iv:Abv56CVUTtigZ5eqplTPahxWS/RtUYIG8BrUOa9jAig=,tag:Nu97VT9xrAF7643cdkdtRg==,type:str]", + "sops": { + "age": [ + { + "recipient": "age1e8mlg8wgp5rszgr9h9axu50mpr7v4020du5ry8s7wspwp4wftgmsfh4uxj", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjL3g5SzluM21pUS9scDho\nV2F5M3ZiNHNZemJDYlJobXZPQVo5Yy9NSUhnClQwaUNadHU4YmFtditLelVmQkNU\ndXRxYTFxZC9tYTVqOG9FTTY1NERiVzQKLS0tIEFIaEY5bS9hc0Uxb0VjMTVIaTJS\nTVJwWjEvN29nMmlJYUUwM2JZWWJRQkkKZ7zEnozw7uDlTUGBFE6Wvg/1Lkr7+r9p\n8w+I7H4bPQMI+XQGT6twV6FgUpVKpeMWNhrX9BLi1QDq2wVutKarOg==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2026-01-14T13:54:18Z", + "mac": "ENC[AES256_GCM,data:YKICFsYKGVJws7wDCF2/3PAwg6t6xp7iDQw+oDDfqls457ZKms10fQo0Bq6mYbfqNjfz8fR4QUT256VPa5Jy6HMjntevHBltpKPv1drV8q+YrseX3lFxZaRWhe904D7ifoekrf3tbN3ibidCgKGa+ib5IKNOWPuNIrSXGUZzLGU=,iv:Wj/ndj16L+N2ttuUkUy+5vp9u6TfCJzjpG6nriDq6Vw=,tag:SV2qxUra1C/KNW9lKcB+kw==,type:str]", + "unencrypted_suffix": "_unencrypted", + "version": "3.11.0" + } +} diff --git a/extras/secrets/release.keystore.enc b/extras/secrets/release.keystore.enc new file mode 100644 index 0000000..bc4b0ec --- /dev/null +++ b/extras/secrets/release.keystore.enc @@ -0,0 +1,14 @@ +{ + "data": "ENC[AES256_GCM,data:QctAWs2h1UgwB6DBNraIrzPFKXpjqYrOlGNwH5743E0UZmQUmC5AWCx43DBMTGa9BkbIviVSG6f5Gy9aBc6GW5mFnaVjCFdxIv+sJ21io4mC6hzzU4ulnEEbD7wkHuwE+IIuNp/hJ1UMIu9gNKzJdbw5uxyMwak0Xk8UPEPpJgmMdnTow7yQlMs4VC5jpeYT6ixtRPUiACiHdvdBaOusGmVzfyYO3eRCMtbaXnY7KL+qzp/ENlDF8g7P4UgDM2RyJljmwZ4RtmBFO1z3JqEkPXX+P+mzZUgdwQUqFxlFac2Gg7FzUEpvlvovqw4EbfA7QnvLe44nAt4FaDNWxQkCqnyNkookY/fTAQtEvNBq2f38vUIyJSDOrttU51SKwCRCJds3og/zn8QbTCpZnpvQZKNvPaJgfwvZZpwYP/os6e/yrSBaqsv3DD0Ctt5qWQ0c2qhnxOhmrY/Hbfp3u0dEMqydkJAOYMGdo0gNBsBC3VfzNVSVLQC9nUZ23I/IPRtXN0ptnBNbWiegY8Yya8uF6OYxY8oHMHFZsVH3SOb4Th72A/CJf69Ipwl9MDpRiuZ56QvA47Xigtu9SfsMqWv82RqNKdZrhSmICVIOTF5YDO24Lu6Ij1s1qWPqjGHb0bLEtKrYOvRvesJfcS8ylE5fmrYFHdSLGjdS3MwmfiuOsEgbmKMocLYIcW2LtxBhlRj6L8Sg6d1X8Hk14HhitszSE43Je4/2DTet0waLHd9nf/q28dAsBSQ+Szew/M8EpFkxksriEZwEb/cXeDDkC317XHPy/uckQgRHdiAP6Ctsy2NUUo/Hs2U6/twxnCzZ17rZVbJ//D5xi4rnDbguAoP4CpIBDg9y5kD31zTBFnkLSYwIFxgP0gks3dVYORuIJEWo91jKUA4dGjr0PIXRJPYwBa1N7J994PvJ4S5HElnJb55h745ME91mrle0niiKrhWn5B8K7+QdP4xt4SUmEE/AxNTwmkyex/C6MyV/5DYpnNlevo+JqgKh6YdIKpe+ZUp4VurODuTUS6OcW5k9vT3WALgJcbVnIg/2NgXWovHD68bxcBhxZeomoFBJkFWC8306jzRcthFUPbsh8Oa6I6zoyMyMV9vHBFV7UsDR7bU3nZ9XiYwy2c3vKeb0yTrLHndljUdJF3PHQSWbgc2rIJM6FdaWqD69vKqAJXvUq9PQ0zFbH3yOARt6Yov81jxs+ixReXsaV7yvp+evKmWy0SaLThMyBHjGigtP1XWw2NgY+Rw+S55/QPjE9VUI6vVwq5esXAG0zfKIJdkNT1KQWzY08Wh3inXJ56/or/Sq9dlVBXa6Jj7KpXV97SviliE2L86NukXBUOd5/A8woNjVKKvgYuKaTLCcQ+mwQ/Sk/4YdZdS2nf53VNsGmOUSEXTUkfpuPBC61/uZUqQS9t4F1TzoP7rwT6Und9NNsS9Aa7XIsJzsLUyeSfpVrSySTcJ1w5eEyy9eht5k+DG5ho/+wpu4FnW29+8Dx+v3R268WM/OT7QyjjriVvBT7TvpfBTI7hx/OMA1ixSk8ulcFdY5xBvveG7mvvNsBEqQzqFMdYMQ7iuXh537bi8/Xzyr190qCtgiBBYyP4A+dQs2vL418t/5IBo+BIiJkeJ1EeH1cuWQAf53NVxHcNyA2+Vue25aMDAH9iK814oowTiBS1wghgKDSxyvqUo3IGqdWyLV50UCZKhOzt+MY6JCLO9zWvNtna8hPu6ZbWeQlY8lMH8K2OnNY8gf21uSyRXo1UngWkzaFZWWCo2OQJENdA1vWejW5Z2iCW413GBlBPZ2G6ku1fSNikkQ1y33BLVg/W7/6pRwVA5PrZffVqcdmhfJ9o1i/n614BRoM2HUqv3t4IxUmjQmgsM24V3TM4jr4UqXN/XDBGqjR5n1RNnFzdwyq5hBEyhDvLd9p9zI43E1UFcGexkswfxwEXbp/amwg++3Xem32syCzxDh56gx5MBzptjj/5HuAzXtXTL+8hEF+yyOuECALnGMfnvwaQ6drKVI3O5te/5ABroYoS9OQoaq8rgZ11R+7yshqM85k/SM9jjbc/w7GDK5DcQSEIon7hLEXlMW0Xt6sswZz1lleTIGayGYNcWR7j8u2F+5XrtjpXQr6iXoKRoN0FyzWIgXfdUvx6cDo2oAcKcQ7DdgXsvyKPmrF89FVfvYe7QYHWDDpEKM4UlJFVrOwjg1QcwPbDovuVYCoY176xwzxuEvEAKW/q8sbkOG2DdR4I6VfPblUngzpmTFsW3XbZKJV8CVrYRLRw9SLJnyiNB+sk7BahxmRHHJAZMBfwKZbYcxxgmFCLz6Zmw6R0WaiomOpdxdQ8fy7OgNl9qetRlPkWng0GfCLXV0iUE86TbBJjA3TnxIzb/OfWCzM0HBj2dfRjmT0fz0jefvlVx8w8M+zk0OhV699xatIYXVOWqZp7EMYR9GbuRXfq47TOlka42VdvVr1pNhQ3at35G6qgGruxPC4ywpB4KN8Uc/9T7kq57m2KDR9UZCxfqYKoxxV2WWSS7y8fk0GmFZk3ucJBZBKyS+VNhDrALHbSNnG9LVrS9m7dxRZko7yrruCb1DoNMpQ5OXAemYJaKE45fOBy8eDgyS5eQxZ7WVG5165SrRMzYTsSPMBEU5mWSv1z9Z/YBHFTfOsi/epcvcjnnsSy2CLJn/M82tM7qoFLDvSGFsc0zXmgE/GIXjtqCTHhqocbo0gh3vnplUgZICdJOiVplNgSY3gdhCxMrH+OTqhwIQqOTv+/DzRA3BFfmJY7ooXqnSfR+/rP1oWNLq5ps1N7cDZZROqj/POJu40leuSbKaHwLEpyHXgbwCYThDcVuIeH6Gb8HcH815cDSofQA6FgIbVtUkgYkqCpIv1aprNXSXOd+zk7Tyx+6vjy5nzeMUzauugVZfYwwvmHcbSEOOyXcMwX8u/IWdEu12LsWe7ivumBjIroVIMoZ8G7MgGRph7VP1MJpmrW5Uz+w9cHJ7FTxJH3DDbZvAodkvZoaT4WgRZ6G1ciY4sAXTZs3vmfCQjkhqcOxR3YSHzeZsNJnY4aKEZrdNFsN9XHIXb+uFK9ab4s4xyH8ojf/lCzB/7YYPjIDUevTPHUu3oRHxQH3F7CYbQOtZNW+LVDNZrPwrgpG/zvAlpaOrp4IUcWFHWRy84y28Sza9yqfiFhdSc75wOb7hB/rBvs4Eof6mG44MP9aeqIeDr2QM5uaz/IkqRxT1YNc+2jli13PyPBdbLLTRBxLmvo/riF/g42y5gjDzg4eUHJgAP8kIH1nKhQvyeyqG2rcyawwq6cUDsrvTbKREgI6n+jjR8myoOTjpKUL5DaspFuPeifKLReXALL2qE7tJxusBVeuVKPyo9CbIS+BYFR7bmv2jhLf1ur/mvnnjsNXeg0nVB9LjtbfMPjJqLzpEpJNw/F+sroQbQFG6HNKJj4HGK3jmp06m/vdzNvXV5t6S/wNUUnlv0XWBrURF2LaA7h2vcjxduLjN9+be46oNdZrdT3PqZXagDiXWU/b10dobfQ/Dz/O56MIa6rnjRr6w1X/RbTt0b5Xm45TN8amg+vGlCSetlclIF6Uo5Y9/d5yqd8LemODA71y1VPLnxW84ijojkIdgOz8+YL4Wx621p/R8hJmf5fbYz7YjQVvHrNoP7+l4tokyL4hkGFGLNarj7u1wPzMRdK4=,iv:xBOIwe/FWrSWs1MbXrNfQvVvHhFJtUAuwGdzwTILVv0=,tag:DGoEvsakFHZKA/chwZBbnQ==,type:str]", + "sops": { + "age": [ + { + "recipient": "age1e8mlg8wgp5rszgr9h9axu50mpr7v4020du5ry8s7wspwp4wftgmsfh4uxj", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3MG8vb08zcVJ1UzVHcmp6\ndGlUN0NROVArd0RHd0F3NjNZMVc4YzdHa0djClArNGNiOHAzT1ViQThYdXhYUVhH\nWEFaS3R4RTZYT2VBNGs5MWh0QUdxd3cKLS0tIHd5QnEwQ1VLUnlJTk93N0xIQ2wy\nQUpZS1JJWHVMWDVNL0V6QXE5b2cxYVEK31IXjzqekKlhlxPc2lqZiAghA6qWDLy8\nVK9x3zDqOj8hBit8ZYsNwXuY/ylFW5j+uXyf7mIuFuLm1dQGCY6ZIg==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2026-01-14T13:54:18Z", + "mac": "ENC[AES256_GCM,data:6EOAuPHjcAVGY6A+AF+zH/VlCpp1eEkRoQraX34Y25WNcQHstI/WtcwuvdbOJY+qxXcXxeA/DmmHCfbWazB9eJt8re02A2ehArarWw8JZg6uL9/2/h0OmgSGzfbc7EHuPL283OrV7t8JmjL7UfkCriGYXKB8lK/8+L7/sj9mZ1c=,iv:+eHMSWCUkp8lp3akrbRVoIwfnjh0o0lvQcsQjo/bqOg=,tag:Educ/JYMeVsfmCwq5aVtrQ==,type:str]", + "version": "3.11.0" + } +} diff --git a/extras/secrets/release.properties.enc b/extras/secrets/release.properties.enc new file mode 100644 index 0000000..80ec0b0 --- /dev/null +++ b/extras/secrets/release.properties.enc @@ -0,0 +1,14 @@ +{ + "data": "ENC[AES256_GCM,data:GeozAZymskg98rStQlxUFHQxrzu0gECa2NHUfwScIeGfGPi+tzkgbCqxfxa93S3Psh9qVN7tZVQNGRiiypEBJpfFW2YNQ+d4hkT8EpazWneSjDq7Puz/Biii542CTRpwxfN+yto/3DhYH+PYR5pYkeemX4CSuz/2wdiG4L9V2ior6AA=,iv:7tiSatE0kOgjbpivjZVYh9QUc9J9vPDRZUGpVvCHlqM=,tag:agIpuOwCAfe4WnKo3wDQDw==,type:str]", + "sops": { + "age": [ + { + "recipient": "age1e8mlg8wgp5rszgr9h9axu50mpr7v4020du5ry8s7wspwp4wftgmsfh4uxj", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0R2NNYmFLZldrWnhCUllw\nMjZoLzhld09hQy9tNGEzY3J6VnlKU1V2V2xrCkx3V1BtdjNmeHpyL3h4Rm9EdFUz\nSnhpMENFdXlVVmNXTCtIUzBCWE5QZTQKLS0tIG5xSGhlcGtnNC93ckNLUmFzRW9F\nOVpnb0l0S1pmRVZiTjY1dGlmVFdKdHcKbOvA/+qG9D9mMAMrTqN/YptU44klaQqX\n21Hm0IPMZ4NxrDXDiVChq4OP8Eudq0t+zgC8GJf41MVU4kvPRG55aQ==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2026-01-14T13:54:18Z", + "mac": "ENC[AES256_GCM,data:ZgGV9qy9DBvrKDzlfSicHLAXSoqvkzMW8c65g06IeUs5PZQaFohD6DSY3uOKkfJoN1SM9YZ3HSxj7+wDS2lcr6BwFGzkmtdBqKIImdCcfNNj9iq3Cc84duhcHtMb63tKiL/BzwGSdeqOdxHJR4Pcn/EpN92XGCWarRF4QuQhGKo=,iv:EpAqtiZC2lq0a4vN48ufNHFz+v/AhLcicwGzzSr263Q=,tag:D3RU+EdgMP7Nje3xQ8ofWQ==,type:str]", + "version": "3.11.0" + } +} diff --git a/extras/secrets/tools/clean-secrets.sh b/extras/secrets/tools/clean-secrets.sh new file mode 100644 index 0000000..075ea65 --- /dev/null +++ b/extras/secrets/tools/clean-secrets.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# Remove all secret files +rm -f android/extras/keystores/release.keystore +rm -f android/extras/keystores/release.properties +rm -f .env +rm -f .env-staging +rm -f .env-production +rm -f .env-development +rm -f ios/Flutter/.env.xcconfig +rm -f ios/Flutter/.env.staging.xcconfig +rm -f ios/Flutter/.env.production.xcconfig +rm -f ios/Flutter/.env.develop.xcconfig +rm -f android/app/google-services.json +rm -f ios/config/develop/GoogleService-Info.plist +rm -f ios/config/production/GoogleService-Info.plist +rm -f ios/config/staging/GoogleService-Info.plist diff --git a/extras/secrets/tools/decode-secrets.sh b/extras/secrets/tools/decode-secrets.sh new file mode 100755 index 0000000..c76a613 --- /dev/null +++ b/extras/secrets/tools/decode-secrets.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +# base64 decode the release github secrets + +decode() { + local INPUT="$1" + local OUTPUT="$2" + local OUTPUT_DIR + OUTPUT_DIR=$(dirname "$OUTPUT") + + mkdir -p "$OUTPUT_DIR" + + echo "Decoding $INPUT" + + if [[ "$(uname)" == "Darwin" ]]; then + base64 -D < "$INPUT" > "$OUTPUT" + else + base64 -d < "$INPUT" > "$OUTPUT" + fi +} + +if [[ -f extras/secrets/age_key.base64 ]]; then + decode extras/secrets/age_key.base64 extras/secrets/age_key.txt +fi + +if [[ -f extras/secrets/release.keystore.base64 ]]; then + decode extras/secrets/release.keystore.base64 extras/secrets/release.keystore.enc +fi + +if [[ -f extras/secrets/release.properties.base64 ]]; then + decode extras/secrets/release.properties.base64 extras/secrets/release.properties.enc +fi + +if [[ -f extras/secrets/firebase_app_distribution_service_account.json.base64 ]]; then + decode extras/secrets/firebase_app_distribution_service_account.json.base64 android/firebase_app_distribution_service_account.json +fi diff --git a/extras/secrets/tools/decrypt-secrets.sh b/extras/secrets/tools/decrypt-secrets.sh new file mode 100755 index 0000000..c7d95d4 --- /dev/null +++ b/extras/secrets/tools/decrypt-secrets.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash + +# decrypts ASK_ANGEL properties and signing keys if available + +decrypt() { + local INPUT="$1" + local OUTPUT="$2" + local PARAMETERS="$3" + + local OUTPUT_DIR + OUTPUT_DIR=$(dirname "$OUTPUT") + + mkdir -p "$OUTPUT_DIR" + + echo "Decrypting $INPUT with $PARAMETERS" + sops --decrypt $PARAMETERS "$INPUT" > "$OUTPUT" +} + +# these should be checked-out in git +echo "Decrypting project properties" + +decrypt extras/secrets/env.enc .env "--input-type dotenv --output-type dotenv" +decrypt extras/secrets/env-staging.enc .env-staging "--input-type dotenv --output-type dotenv" +decrypt extras/secrets/env-production.enc .env-production "--input-type dotenv --output-type dotenv" +decrypt extras/secrets/env-development.enc .env-development "--input-type dotenv --output-type dotenv" + +decrypt extras/secrets/env.enc ios/Flutter/.env.xcconfig "--input-type dotenv --output-type dotenv" +decrypt extras/secrets/env-staging.enc ios/Flutter/.env.staging.xcconfig "--input-type dotenv --output-type dotenv" +decrypt extras/secrets/env-production.enc ios/Flutter/.env.production.xcconfig "--input-type dotenv --output-type dotenv" +decrypt extras/secrets/env-development.enc ios/Flutter/.env.develop.xcconfig "--input-type dotenv --output-type dotenv" + +# Android release keystore and properties +if [[ -f extras/secrets/release.keystore.enc ]]; then + decrypt extras/secrets/release.keystore.enc android/extras/keystores/release.keystore +fi + +if [[ -f extras/secrets/release.properties.enc ]]; then + decrypt extras/secrets/release.properties.enc android/extras/keystores/release.properties +fi + +# Android GoogleService-Info.plist files +if [[ -f extras/secrets/google-services.json.enc ]]; then + decrypt extras/secrets/google-services.json.enc android/app/google-services.json "--input-type json --output-type json" +fi + +# iOS GoogleService-Info.plist files +if [[ -f extras/secrets/GoogleService-Info-develop.plist.enc ]]; then + decrypt extras/secrets/GoogleService-Info-develop.plist.enc ios/config/develop/GoogleService-Info.plist +fi + +if [[ -f extras/secrets/GoogleService-Info-staging.plist.enc ]]; then + decrypt extras/secrets/GoogleService-Info-staging.plist.enc ios/config/staging/GoogleService-Info.plist +fi + +if [[ -f extras/secrets/GoogleService-Info-production.plist.enc ]]; then + decrypt extras/secrets/GoogleService-Info-production.plist.enc ios/config/production/GoogleService-Info.plist +fi diff --git a/extras/secrets/tools/encrypt-secrets.sh b/extras/secrets/tools/encrypt-secrets.sh new file mode 100755 index 0000000..6a5cfb9 --- /dev/null +++ b/extras/secrets/tools/encrypt-secrets.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +# encrypts updated project properties, so they can be stored in git + +encrypt() { + local INPUT="$1" + local OUTPUT="$2" + local PARAMETERS="$3" + + echo "Encrypting $INPUT with $PARAMETERS" + sops --encrypt $PARAMETERS "$INPUT" > "$OUTPUT" +} + +echo "Encrypting project properties" + +encrypt .env extras/secrets/env.enc "--input-type dotenv --output-type dotenv" +encrypt .env-staging extras/secrets/env-staging.enc "--input-type dotenv --output-type dotenv" +encrypt .env-production extras/secrets/env-production.enc "--input-type dotenv --output-type dotenv" +encrypt .env-development extras/secrets/env-development.enc "--input-type dotenv --output-type dotenv" + +encrypt android/extras/keystores/release.keystore extras/secrets/release.keystore.enc +encrypt android/extras/keystores/release.properties extras/secrets/release.properties.enc + +encrypt android/app/google-services.json extras/secrets/google-services.json.enc + +encrypt ios/config/develop/GoogleService-Info.plist extras/secrets/GoogleService-Info-develop.plist.enc +encrypt ios/config/staging/GoogleService-Info.plist extras/secrets/GoogleService-Info-staging.plist.enc +encrypt ios/config/production/GoogleService-Info.plist extras/secrets/GoogleService-Info-production.plist.enc \ No newline at end of file diff --git a/extras/secrets/tools/load-secrets.sh b/extras/secrets/tools/load-secrets.sh new file mode 100755 index 0000000..17aa366 --- /dev/null +++ b/extras/secrets/tools/load-secrets.sh @@ -0,0 +1,30 @@ +!/usr/bin/env bash + +if [[ ! -z "$SECRETS_ENCRYPT_KEY" ]]; then + echo "copying secret encrypt key" + echo "$SECRETS_ENCRYPT_KEY" > ./extras/secrets/age_key.base64 +fi + +if [[ ! -z "$RELEASE_KEYSTORE" ]]; then + echo "copying release keystore" + echo "$RELEASE_KEYSTORE" > ./extras/secrets/release.keystore.base64 +fi + +if [[ ! -z "$RELEASE_PROPERTIES" ]]; then + echo "copying release properties" + echo "$RELEASE_PROPERTIES" > ./extras/secrets/release.properties.base64 +fi + +if [[ ! -z "$FIREBASE_APP_DISTRIBUTION_SERVICE_ACCOUNT" ]]; then + echo "copying firebase app distribution service account" + echo "$FIREBASE_APP_DISTRIBUTION_SERVICE_ACCOUNT" > ./extras/secrets/firebase_app_distribution_service_account.json.base64 +fi + +# set SOPS_AGE_KEY_FILE environment variable +export SOPS_AGE_KEY_FILE="extras/secrets/age_key.txt" + +# decode secrets +./extras/secrets/tools/decode-secrets.sh + +# decrypt secrets +./extras/secrets/tools/decrypt-secrets.sh diff --git a/flutter.code-workspace b/flutter.code-workspace index 9d1622e..c97d3ac 100644 --- a/flutter.code-workspace +++ b/flutter.code-workspace @@ -150,7 +150,7 @@ "android/local.properties": true, "android/**/GeneratedPluginRegistrant.java": true, "android/**/key.properties": true, - "android/**/*.keystore": false, + "android/**/*.keystores": false, "android/**/*.jks": true }, // [material-icon-theme] Material Icons theme @@ -236,9 +236,15 @@ ], "cSpell.words": [ "Buildless", + "DSYM", + "lproj", + "OBJC", "Refreshable", "Riverpod", - "usecase" + "usecase", + "xcassets", + "xcconfig", + "xcodeproj" ] } } diff --git a/ios/.gitignore b/ios/.gitignore index 7a7f987..04f0916 100644 --- a/ios/.gitignore +++ b/ios/.gitignore @@ -32,3 +32,7 @@ Runner/GeneratedPluginRegistrant.* !default.mode2v3 !default.pbxuser !default.perspectivev3 + +# Secrets +Flutter/.env* +config/**/GoogleService-Info.plist diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig index ec97fc6..ce11e43 100644 --- a/ios/Flutter/Debug.xcconfig +++ b/ios/Flutter/Debug.xcconfig @@ -1,2 +1,3 @@ #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" +#include ".env.xcconfig" diff --git a/ios/Flutter/DevelopDebug.xcconfig b/ios/Flutter/DevelopDebug.xcconfig new file mode 100644 index 0000000..6f9e04e --- /dev/null +++ b/ios/Flutter/DevelopDebug.xcconfig @@ -0,0 +1,2 @@ +#include "Debug.xcconfig" +#include ".env.develop.xcconfig" diff --git a/ios/Flutter/DevelopRelease.xcconfig b/ios/Flutter/DevelopRelease.xcconfig new file mode 100644 index 0000000..25050d3 --- /dev/null +++ b/ios/Flutter/DevelopRelease.xcconfig @@ -0,0 +1,2 @@ +#include "Release.xcconfig" +#include ".env.develop.xcconfig" diff --git a/ios/Flutter/ProductionDebug.xcconfig b/ios/Flutter/ProductionDebug.xcconfig new file mode 100644 index 0000000..2dafc20 --- /dev/null +++ b/ios/Flutter/ProductionDebug.xcconfig @@ -0,0 +1,2 @@ +#include "Debug.xcconfig" +#include ".env.production.xcconfig" diff --git a/ios/Flutter/ProductionRelease.xcconfig b/ios/Flutter/ProductionRelease.xcconfig new file mode 100644 index 0000000..0219ac3 --- /dev/null +++ b/ios/Flutter/ProductionRelease.xcconfig @@ -0,0 +1,2 @@ +#include "Release.xcconfig" +#include ".env.production.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig index c4855bf..8f0120b 100644 --- a/ios/Flutter/Release.xcconfig +++ b/ios/Flutter/Release.xcconfig @@ -1,2 +1,3 @@ #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" +#include ".env.xcconfig" diff --git a/ios/Flutter/StagingDebug.xcconfig b/ios/Flutter/StagingDebug.xcconfig new file mode 100644 index 0000000..d1dcd83 --- /dev/null +++ b/ios/Flutter/StagingDebug.xcconfig @@ -0,0 +1,2 @@ +#include "Debug.xcconfig" +#include ".env.staging.xcconfig" diff --git a/ios/Flutter/StagingRelease.xcconfig b/ios/Flutter/StagingRelease.xcconfig new file mode 100644 index 0000000..1447378 --- /dev/null +++ b/ios/Flutter/StagingRelease.xcconfig @@ -0,0 +1,2 @@ +#include "Release.xcconfig" +#include ".env.staging.xcconfig" diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index edd377c..c65ae91 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -53,6 +53,12 @@ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E72BC6D0352C342477993B2F /* Pods-Runner.profile-staging.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-staging.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-staging.xcconfig"; sourceTree = ""; }; + F412D3A52EC22C0700B17DF4 /* DevelopDebug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = DevelopDebug.xcconfig; path = Flutter/DevelopDebug.xcconfig; sourceTree = ""; }; + F412D3A62EC22CA900B17DF4 /* DevelopRelease.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = DevelopRelease.xcconfig; path = Flutter/DevelopRelease.xcconfig; sourceTree = ""; }; + F412D3A82EC22CC800B17DF4 /* ProductionDebug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = ProductionDebug.xcconfig; path = Flutter/ProductionDebug.xcconfig; sourceTree = ""; }; + F412D3A92EC22CCF00B17DF4 /* ProductionRelease.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = ProductionRelease.xcconfig; path = Flutter/ProductionRelease.xcconfig; sourceTree = ""; }; + F412D3AA2EC22CD500B17DF4 /* StagingDebug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = StagingDebug.xcconfig; path = Flutter/StagingDebug.xcconfig; sourceTree = ""; }; + F412D3AB2EC22CDB00B17DF4 /* StagingRelease.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = StagingRelease.xcconfig; path = Flutter/StagingRelease.xcconfig; sourceTree = ""; }; F959D3FA0A9FF0E99245CF9D /* Pods-Runner.profile-develop.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-develop.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-develop.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -91,6 +97,12 @@ 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, + F412D3A52EC22C0700B17DF4 /* DevelopDebug.xcconfig */, + F412D3A62EC22CA900B17DF4 /* DevelopRelease.xcconfig */, + F412D3A82EC22CC800B17DF4 /* ProductionDebug.xcconfig */, + F412D3A92EC22CCF00B17DF4 /* ProductionRelease.xcconfig */, + F412D3AA2EC22CD500B17DF4 /* StagingDebug.xcconfig */, + F412D3AB2EC22CDB00B17DF4 /* StagingRelease.xcconfig */, ); name = Flutter; sourceTree = ""; @@ -429,7 +441,7 @@ }; 249021D4217E4FDB00AE95B9 /* Profile-develop */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + baseConfigurationReference = F412D3A62EC22CA900B17DF4 /* DevelopRelease.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = developDebugAppIcon; CLANG_ENABLE_MODULES = YES; @@ -515,7 +527,7 @@ }; 59C5CF41298AAD0900ECB1CC /* Debug-staging */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + baseConfigurationReference = F412D3AA2EC22CD500B17DF4 /* StagingDebug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = stagingDebugAppIcon; CLANG_ENABLE_MODULES = YES; @@ -599,7 +611,7 @@ }; 59C5CF43298AAD1300ECB1CC /* Release-staging */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + baseConfigurationReference = F412D3AB2EC22CDB00B17DF4 /* StagingRelease.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = stagingReleaseAppIcon; CLANG_ENABLE_MODULES = YES; @@ -680,7 +692,7 @@ }; 59C5CF45298AAD2000ECB1CC /* Profile-staging */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + baseConfigurationReference = F412D3AB2EC22CDB00B17DF4 /* StagingRelease.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = stagingDebugAppIcon; CLANG_ENABLE_MODULES = YES; @@ -766,7 +778,7 @@ }; 59C5CF47298AAD2B00ECB1CC /* Debug-production */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + baseConfigurationReference = F412D3A82EC22CC800B17DF4 /* ProductionDebug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = productionDebugAppIcon; CLANG_ENABLE_MODULES = YES; @@ -850,7 +862,7 @@ }; 59C5CF49298AAD3400ECB1CC /* Release-production */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + baseConfigurationReference = F412D3A92EC22CCF00B17DF4 /* ProductionRelease.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = productionReleaseAppIcon; CLANG_ENABLE_MODULES = YES; @@ -931,7 +943,7 @@ }; 59C5CF4B298AAD3B00ECB1CC /* Profile-production */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + baseConfigurationReference = F412D3A92EC22CCF00B17DF4 /* ProductionRelease.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = productionDebugAppIcon; CLANG_ENABLE_MODULES = YES; @@ -1069,7 +1081,7 @@ }; 97C147061CF9000F007C117D /* Debug-develop */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + baseConfigurationReference = F412D3A52EC22C0700B17DF4 /* DevelopDebug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = developDebugAppIcon; CLANG_ENABLE_MODULES = YES; @@ -1101,7 +1113,7 @@ }; 97C147071CF9000F007C117D /* Release-develop */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + baseConfigurationReference = F412D3A62EC22CA900B17DF4 /* DevelopRelease.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = developReleaseAppIcon; CLANG_ENABLE_MODULES = YES; @@ -1147,7 +1159,7 @@ 59C5CF4A298AAD3B00ECB1CC /* Profile-production */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = "Release-develop"; + defaultConfigurationName = "Debug-production"; }; 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { isa = XCConfigurationList; @@ -1163,7 +1175,7 @@ 59C5CF4B298AAD3B00ECB1CC /* Profile-production */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = "Release-develop"; + defaultConfigurationName = "Debug-production"; }; /* End XCConfigurationList section */ }; diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/develop.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/develop.xcscheme index f1dbece..99989dd 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/develop.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/develop.xcscheme @@ -15,7 +15,7 @@ @@ -47,7 +47,7 @@ @@ -64,7 +64,7 @@ diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/production.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/production.xcscheme index 8b61820..adfa51f 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/production.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/production.xcscheme @@ -15,7 +15,7 @@ @@ -47,7 +47,7 @@ @@ -64,7 +64,7 @@ diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/staging.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/staging.xcscheme index bee9437..635dbd9 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/staging.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/staging.xcscheme @@ -15,7 +15,7 @@ @@ -47,7 +47,7 @@ @@ -64,7 +64,7 @@ diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index c087e13..d92b34d 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -31,9 +31,7 @@ REVERSED_CLIENT_ID CFBundleURLSchemes - com.googleusercontent.apps.907121519909-ac5hdm2av7utn2i85dj1v8v2pcttufb3 - com.googleusercontent.apps.907121519909-5it712s6au9p09d0oo5t2pmp12fs1f8b - com.googleusercontent.apps.907121519909-antq2s70r5iu8hp24i6nckcfbd3f4v0b + $(REVERSED_GOOGLE_CLIENT_ID) @@ -70,5 +68,7 @@ UIViewControllerBasedStatusBarAppearance + GIDClientID + $(GOOGLE_CLIENT_ID) diff --git a/ios/config/develop/GoogleService-Info.plist b/ios/config/develop/GoogleService-Info.plist deleted file mode 100644 index 89e8039..0000000 --- a/ios/config/develop/GoogleService-Info.plist +++ /dev/null @@ -1,34 +0,0 @@ - - - - - CLIENT_ID - XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - REVERSED_CLIENT_ID - XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - API_KEY - XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - GCM_SENDER_ID - XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - PLIST_VERSION - 1 - BUNDLE_ID - com.strv.flutter.template.develop - PROJECT_ID - strv-flutter-template - STORAGE_BUCKET - strv-flutter-template.appspot.com - IS_ADS_ENABLED - - IS_ANALYTICS_ENABLED - - IS_APPINVITE_ENABLED - - IS_GCM_ENABLED - - IS_SIGNIN_ENABLED - - GOOGLE_APP_ID - XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - - \ No newline at end of file diff --git a/ios/config/production/GoogleService-Info.plist b/ios/config/production/GoogleService-Info.plist deleted file mode 100644 index 22d7ba0..0000000 --- a/ios/config/production/GoogleService-Info.plist +++ /dev/null @@ -1,34 +0,0 @@ - - - - - CLIENT_ID - XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - REVERSED_CLIENT_ID - XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - API_KEY - XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - GCM_SENDER_ID - XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - PLIST_VERSION - 1 - BUNDLE_ID - com.strv.flutter.template - PROJECT_ID - strv-flutter-template - STORAGE_BUCKET - strv-flutter-template.appspot.com - IS_ADS_ENABLED - - IS_ANALYTICS_ENABLED - - IS_APPINVITE_ENABLED - - IS_GCM_ENABLED - - IS_SIGNIN_ENABLED - - GOOGLE_APP_ID - XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - - \ No newline at end of file diff --git a/ios/config/staging/GoogleService-Info.plist b/ios/config/staging/GoogleService-Info.plist deleted file mode 100644 index 7b8e3e3..0000000 --- a/ios/config/staging/GoogleService-Info.plist +++ /dev/null @@ -1,34 +0,0 @@ - - - - - CLIENT_ID - XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - REVERSED_CLIENT_ID - XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - API_KEY - XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - GCM_SENDER_ID - XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - PLIST_VERSION - 1 - BUNDLE_ID - com.strv.flutter.template.staging - PROJECT_ID - strv-flutter-template - STORAGE_BUCKET - strv-flutter-template.appspot.com - IS_ADS_ENABLED - - IS_ANALYTICS_ENABLED - - IS_APPINVITE_ENABLED - - IS_GCM_ENABLED - - IS_SIGNIN_ENABLED - - GOOGLE_APP_ID - XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - - \ No newline at end of file diff --git a/lib/app/configuration/configuration.dart b/lib/app/configuration/configuration.dart index be1b042..de9a5bb 100644 --- a/lib/app/configuration/configuration.dart +++ b/lib/app/configuration/configuration.dart @@ -2,11 +2,9 @@ import 'dart:io'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter_app/app/configuration/develop/config_develop.dart'; -import 'package:flutter_app/app/configuration/production/config_production.dart'; -import 'package:flutter_app/app/configuration/staging/config_staging.dart'; import 'package:flutter_app/app/setup/app_platform.dart'; import 'package:flutter_app/app/setup/flavor.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; class Configuration { Configuration({ @@ -30,40 +28,7 @@ class Configuration { final AndroidDeviceInfo? androidDeviceInfo; final IosDeviceInfo? iosDeviceInfo; - String get apiHostUrl => switch (flavor) { - Flavor.develop => ConfigDevelop.apiHostUrl, - Flavor.staging => ConfigStaging.apiHostUrl, - Flavor.production => ConfigProduction.apiHostUrl, - }; - - // Used for Web notifications - String get vapidKey => switch (flavor) { - Flavor.develop => ConfigDevelop.vapidKey, - Flavor.staging => ConfigStaging.vapidKey, - Flavor.production => ConfigProduction.vapidKey, - }; - - // Used for example for Google SignIn init - String? get firebaseClientID => switch (flavor) { - Flavor.develop => switch (currentPlatform) { - AppPlatform.android => ConfigDevelop.androidClientId, - AppPlatform.iOS => ConfigDevelop.iosClientId, - AppPlatform.web => ConfigDevelop.webClientId, - _ => null, - }, - Flavor.staging => switch (currentPlatform) { - AppPlatform.android => ConfigStaging.androidClientId, - AppPlatform.iOS => ConfigStaging.iosClientId, - AppPlatform.web => ConfigStaging.webClientId, - _ => null, - }, - Flavor.production => switch (currentPlatform) { - AppPlatform.android => ConfigProduction.androidClientId, - AppPlatform.iOS => ConfigProduction.iosClientId, - AppPlatform.web => ConfigProduction.webClientId, - _ => null, - }, - }; + String get apiHostUrl => dotenv.get('API_HOST_URL'); // Function which setups base configuration. static Future setup({required Flavor flavor}) async { diff --git a/lib/app/configuration/develop/config_develop.dart b/lib/app/configuration/develop/config_develop.dart deleted file mode 100644 index 7dc7564..0000000 --- a/lib/app/configuration/develop/config_develop.dart +++ /dev/null @@ -1,10 +0,0 @@ -// TODO(strv): Fill correct values -class ConfigDevelop { - static const apiHostUrl = 'https://strv.com'; - - static const vapidKey = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'; - static const androidClientId = ''; - static const iosClientId = ''; - // You can find this value in Firebase Console -> Authentication -> Sign-in method -> Google -> Web SDK configuration. - static const webClientId = 'your_dev_web_client_id'; -} diff --git a/lib/app/configuration/production/config_production.dart b/lib/app/configuration/production/config_production.dart deleted file mode 100644 index 35134d1..0000000 --- a/lib/app/configuration/production/config_production.dart +++ /dev/null @@ -1,10 +0,0 @@ -// TODO(strv): Fill correct values -class ConfigProduction { - static const apiHostUrl = 'https://strv.com'; - - static const vapidKey = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'; - static const androidClientId = ''; - static const iosClientId = ''; - // You can find this value in Firebase Console -> Authentication -> Sign-in method -> Google -> Web SDK configuration. - static const webClientId = 'your_prod_web_client_id'; -} diff --git a/lib/app/configuration/staging/config_staging.dart b/lib/app/configuration/staging/config_staging.dart deleted file mode 100644 index 07bd00d..0000000 --- a/lib/app/configuration/staging/config_staging.dart +++ /dev/null @@ -1,10 +0,0 @@ -// TODO(strv): Fill correct values -class ConfigStaging { - static const apiHostUrl = 'https://strv.com'; - - static const vapidKey = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'; - static const androidClientId = ''; - static const iosClientId = ''; - // You can find this value in Firebase Console -> Authentication -> Sign-in method -> Google -> Web SDK configuration. - static const webClientId = 'your_stg_web_client_id'; -} diff --git a/lib/app/setup/setup_app.dart b/lib/app/setup/setup_app.dart index 01bd9b5..5f3506b 100644 --- a/lib/app/setup/setup_app.dart +++ b/lib/app/setup/setup_app.dart @@ -11,12 +11,10 @@ import 'package:flutter_app/app/setup/app_platform.dart'; import 'package:flutter_app/app/setup/flavor.dart'; import 'package:flutter_app/app/setup/web_setup.dart'; import 'package:flutter_app/app/theme/custom_system_bars_theme.dart'; -import 'package:flutter_app/common/provider/firebase_messaging_service.dart'; -import 'package:flutter_app/common/provider/firebase_remote_config_service.dart'; -import 'package:flutter_app/common/provider/notifications_service.dart'; import 'package:flutter_app/common/provider/theme_mode_provider.dart'; import 'package:flutter_app/core/analytics/crashlytics_manager.dart'; import 'package:flutter_app/core/flogger.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:flutter_web_plugins/url_strategy.dart'; import 'package:freerasp/freerasp.dart'; @@ -41,6 +39,15 @@ Future setupApp({required Flavor flavor}) async { // Configure Flavor await Configuration.setup(flavor: flavor); + // Load secrets from .env file and override with specific flavor file + await dotenv.load( + overrideWithFiles: switch (flavor) { + Flavor.staging => ['.env-staging'], + Flavor.develop => ['.env-development'], + Flavor.production => ['.env-production'], + }, + ); + // Setup Firebase await _setupFirebase(flavor: flavor); await _setupFirebaseCrashlytics(); @@ -78,15 +85,15 @@ Future _setupFirebase({required Flavor flavor}) async { } else if (AppPlatform.isWeb) { await Firebase.initializeApp( // For WebApp we need to specify the FirebaseOptions here! - // TODO(strv): Replace with actual values. Can be found inside Firebase -> Project Settings, under Web App - options: const FirebaseOptions( - apiKey: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', + // TODO(strv): Replace with actual values (use secrets!). Can be found inside Firebase -> Project Settings, under Web App + options: FirebaseOptions( + apiKey: dotenv.get('FIREBASE_WEB_API_KEY'), authDomain: 'strv-flutter-template.firebaseapp.com', projectId: 'strv-flutter-template', storageBucket: 'strv-flutter-template.appspot.com', - messagingSenderId: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', - appId: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', - measurementId: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', + messagingSenderId: dotenv.get('FIREBASE_WEB_MESSAGING_SENDER_ID'), + appId: dotenv.get('FIREBASE_WEB_APP_ID'), + measurementId: dotenv.get('FIREBASE_WEB_MEASUREMENT_ID'), ), ); } @@ -130,21 +137,20 @@ Future _setupFirebaseCrashlytics() async { // TODO(strv): Support it or remove it! Future _setupFirebaseRemoteConfig() async { if (!AppPlatform.isMobile) return; - - await providerContainer.read(firebaseRemoteConfigServiceProvider.future); + // await providerContainer.read(firebaseRemoteConfigServiceProvider.future); } // TODO(strv): Support it or remove it! Future _setupFirebaseMessaging() async { if (!AppPlatform.isLinux && !AppPlatform.isWindows) { - await providerContainer.read(firebaseMessagingServiceProvider.future); + // await providerContainer.read(firebaseMessagingServiceProvider.future); } } // TODO(strv): Support it or remove it! Future _setupLocalNotificationsService() async { if (!AppPlatform.isLinux && !AppPlatform.isWindows) { - await providerContainer.read(notificationsServiceProvider.future); + // await providerContainer.read(notificationsServiceProvider.future); } } @@ -155,24 +161,25 @@ Future _setupRASP({required Flavor flavor}) async { final config = TalsecConfig( isProd: flavor == Flavor.production && !kDebugMode, - watcherMail: 'lukas.hermann@strv.com', + watcherMail: 'flutter_template@strv.com', /// For Android androidConfig: AndroidConfig( - packageName: 'com.strv.flutter.template', + packageName: dotenv.get('APP_ID'), // signingCertHashes: list of hashes of the certificates of the keys which were used to sign the application. // At least one hash value must be provided. Hashes which are passed here must be encoded in Base64 form // https://github.com/talsec/Free-RASP-Community/wiki/Getting-your-signing-certificate-hash-of-app signingCertHashes: [ - '8t1mXm/GqPmhtbVWyeOlXKwa4Oe8l5v3YliMlR81VeM=', // Debug - '0ckwAjw2ivS03BBRWhR1vBD2bqUYUGXl6brlixsmk8g=', // Release + dotenv.get('SIGNING_CERT_DEBUG_BASE64'), // Debug + dotenv.get('SIGNING_CERT_RELEASE_BASE64'), // Release + dotenv.get('SIGNING_CERT_GOOGLE_PLAY_BASE64'), // Google Play ], ), /// For iOS iosConfig: IOSConfig( - bundleIds: ['com.strv.flutter.template'], - teamId: 'M8AK35...', + bundleIds: [dotenv.get('APP_ID')], + teamId: dotenv.get('APPLE_TEAM_ID'), ), ); diff --git a/lib/common/provider/firebase_messaging_service.dart b/lib/common/provider/firebase_messaging_service.dart index 983f3ea..dcc3e0d 100644 --- a/lib/common/provider/firebase_messaging_service.dart +++ b/lib/common/provider/firebase_messaging_service.dart @@ -1,5 +1,4 @@ import 'package:firebase_messaging/firebase_messaging.dart'; -import 'package:flutter_app/app/configuration/configuration.dart'; import 'package:flutter_app/app/setup/app_platform.dart'; import 'package:flutter_app/common/data/entity/notification_payload_entity.dart'; import 'package:flutter_app/common/provider/current_user_state.dart'; @@ -7,6 +6,7 @@ import 'package:flutter_app/common/provider/notifications_service.dart'; import 'package:flutter_app/common/usecase/create_device_token_use_case.dart'; import 'package:flutter_app/core/analytics/crashlytics_manager.dart'; import 'package:flutter_app/core/flogger.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'firebase_messaging_service.g.dart'; @@ -37,7 +37,7 @@ class FirebaseMessagingService extends _$FirebaseMessagingService { Future registerFCMToken({String? fcmToken}) async { try { if (fcmToken == null) { - fcmToken = await FirebaseMessaging.instance.getToken(vapidKey: AppPlatform.isWeb ? Configuration.instance.vapidKey : null); + fcmToken = await FirebaseMessaging.instance.getToken(vapidKey: AppPlatform.isWeb ? dotenv.get('VAPID_KEY') : null); Flogger.d("[Firebase Messaging] Registering User's FCM token: $fcmToken"); } } on Exception catch (e, st) { diff --git a/lib/features/landing/force_update_page_content.dart b/lib/features/landing/force_update_page_content.dart index f7fee6a..dc88c37 100644 --- a/lib/features/landing/force_update_page_content.dart +++ b/lib/features/landing/force_update_page_content.dart @@ -5,6 +5,7 @@ import 'package:flutter_app/common/component/custom_button/custom_button_primary import 'package:flutter_app/common/component/custom_text/custom_text.dart'; import 'package:flutter_app/common/extension/build_context.dart'; import 'package:flutter_app/common/usecase/native_store_open_use_case.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; class ForceUpdatePageContent extends ConsumerWidget { @@ -34,7 +35,9 @@ class ForceUpdatePageContent extends ConsumerWidget { CustomButtonPrimary( onPressed: () { // TODO(strv): Fill correct Android and iOS app IDs - ref.read(nativeStoreOpenUseCaseProvider(androidAppBundleId: 'com.strv.template.app', appStoreId: '123123123')); + ref.read( + nativeStoreOpenUseCaseProvider(androidAppBundleId: dotenv.get('APP_ID'), appStoreId: dotenv.get('APPLE_ID')), + ); }, text: context.locale.forceUpdateButton, ), diff --git a/makefile b/makefile index f99e932..c9b0801 100644 --- a/makefile +++ b/makefile @@ -1,5 +1,5 @@ # https://medium.com/flutter-community/automating-flutter-workflows-with-the-makefile-423b8e023c9a -.PHONY: setup watch clean gen locale install integration_test test generateAndroidProductionAppBundle generateIosStagingIpa generateIosProductionIpa generateWebProduction deployWeb runner_gen +.PHONY: setup watch clean gen locale install integration_test test generateAndroidProductionAppBundle generateIosStagingIpa generateIosProductionIpa generateWebProduction deployWeb runner_gen secretsDecrypt secretsEncrypt secretsClean setup: # Setup the project @fvm dart ./project_setup/lib/main.dart @@ -26,6 +26,7 @@ install: # Install any required packages @fvm dart pub global activate icons_launcher @fvm dart pub global activate patrol_cli @fvm dart pub global activate flutterfire_cli + @make secretsDecrypt integration_test: # Runs Patrol Integration tests @patrol test --target integration_test --flavor develop @@ -64,4 +65,14 @@ deployWeb: runner_gen: # For github actions @flutter pub get @flutter gen-l10n --arb-dir "assets/localization" --template-arb-file "app_en.arb" --output-localization-file "app_localizations.gen.dart" --output-dir "lib/assets" --no-synthetic-package - @dart run build_runner build --delete-conflicting-outputs \ No newline at end of file + @dart run build_runner build --delete-conflicting-outputs + +secretsDecrypt: # Decrypt secrets + @sh ./extras/secrets/tools/load-secrets.sh + +secretsEncrypt: # Encrypt secrets + @sh ./extras/secrets/tools/encrypt-secrets.sh + +secretsClean: # Clean secrets + @sh ./extras/secrets/tools/clean-secrets.sh + \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 4f239bc..876e2ac 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -590,6 +590,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.5.2" + flutter_dotenv: + dependency: "direct main" + description: + name: flutter_dotenv + sha256: d4130c4a43e0b13fefc593bc3961f2cb46e30cb79e253d4a526b1b5d24ae1ce4 + url: "https://pub.dev" + source: hosted + version: "6.0.0" flutter_gen_core: dependency: transitive description: @@ -985,10 +993,10 @@ packages: dependency: transitive description: name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.16.0" mime: dependency: transitive description: @@ -1606,26 +1614,26 @@ packages: dependency: transitive description: name: test - sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7" + sha256: "65e29d831719be0591f7b3b1a32a3cda258ec98c58c7b25f7b84241bc31215bb" url: "https://pub.dev" source: hosted - version: "1.26.3" + version: "1.26.2" test_api: dependency: transitive description: name: test_api - sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" url: "https://pub.dev" source: hosted - version: "0.7.7" + version: "0.7.6" test_core: dependency: transitive description: name: test_core - sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0" + sha256: "80bf5a02b60af04b09e14f6fe68b921aad119493e26e490deaca5993fef1b05a" url: "https://pub.dev" source: hosted - version: "0.6.12" + version: "0.6.11" time: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ac97c6f..89b7d73 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -53,6 +53,7 @@ dependencies: window_manager: 0.5.1 # Security + flutter_dotenv: 6.0.0 freerasp: 7.2.1 # Logging @@ -131,6 +132,10 @@ flutter: generate: true assets: + - .env + - .env-staging + - .env-production + - .env-development - assets/png/ - assets/svg/ From 23d35c8bce65a445383fa3612680273dd1cd6998 Mon Sep 17 00:00:00 2001 From: Michal Urbanek Date: Wed, 14 Jan 2026 16:21:05 +0100 Subject: [PATCH 2/2] fix: PR review comments --- README.md | 2 +- android/extras/keystores/README.md | 2 +- extras/secrets/tools/decrypt-secrets.sh | 4 ++-- extras/secrets/tools/load-secrets.sh | 2 +- lib/app/setup/setup_app.dart | 5 ++++- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 037b756..78b51ab 100644 --- a/README.md +++ b/README.md @@ -212,7 +212,7 @@ They are also overrides the same as project ones - e.g. `.env.xcconfig` is overr - Update `.env`, `google-service.json` files and `keystore` secrets - Run `make secretsEncrypt` to regenerate secrets with your private key -If you need to +### If you need to: - add another API key, just place it into the proper `.env` file and use `make secretsEncrypt` to regenerate encrypted files `.enc`. - encrypt another file, modify `extras/secrets/tools/encrypt-secrets.sh` and `extras/secrets/tools/decrypt-secrets.sh` files to include it diff --git a/android/extras/keystores/README.md b/android/extras/keystores/README.md index 69781d2..c6315b3 100644 --- a/android/extras/keystores/README.md +++ b/android/extras/keystores/README.md @@ -8,7 +8,7 @@ Note: The password for store and key must be the same for `PKCS12` storetype. Here is an example for organizational information that you need to fill during generating of a new key: ``` -CN=Joe Doe, OU=STRV, O=STRV, L=Prague, ST=Czechia, C=CZ +CN=John Doe, OU=STRV, O=STRV, L=Prague, ST=Czechia, C=CZ ``` To get the SHA1 Certificate of the keystore (for example for Firebase purpose) you can use following commands: diff --git a/extras/secrets/tools/decrypt-secrets.sh b/extras/secrets/tools/decrypt-secrets.sh index c7d95d4..17de50d 100755 --- a/extras/secrets/tools/decrypt-secrets.sh +++ b/extras/secrets/tools/decrypt-secrets.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# decrypts ASK_ANGEL properties and signing keys if available +# decrypts secret properties and signing keys if available decrypt() { local INPUT="$1" @@ -38,7 +38,7 @@ if [[ -f extras/secrets/release.properties.enc ]]; then decrypt extras/secrets/release.properties.enc android/extras/keystores/release.properties fi -# Android GoogleService-Info.plist files +# Android google-services.json file if [[ -f extras/secrets/google-services.json.enc ]]; then decrypt extras/secrets/google-services.json.enc android/app/google-services.json "--input-type json --output-type json" fi diff --git a/extras/secrets/tools/load-secrets.sh b/extras/secrets/tools/load-secrets.sh index 17aa366..3ebeea5 100755 --- a/extras/secrets/tools/load-secrets.sh +++ b/extras/secrets/tools/load-secrets.sh @@ -1,4 +1,4 @@ -!/usr/bin/env bash +#!/usr/bin/env bash if [[ ! -z "$SECRETS_ENCRYPT_KEY" ]]; then echo "copying secret encrypt key" diff --git a/lib/app/setup/setup_app.dart b/lib/app/setup/setup_app.dart index 5f3506b..22a355e 100644 --- a/lib/app/setup/setup_app.dart +++ b/lib/app/setup/setup_app.dart @@ -137,12 +137,14 @@ Future _setupFirebaseCrashlytics() async { // TODO(strv): Support it or remove it! Future _setupFirebaseRemoteConfig() async { if (!AppPlatform.isMobile) return; + // TODO(strv): Uncomment once secrets are filled with proper values // await providerContainer.read(firebaseRemoteConfigServiceProvider.future); } // TODO(strv): Support it or remove it! Future _setupFirebaseMessaging() async { if (!AppPlatform.isLinux && !AppPlatform.isWindows) { + // TODO(strv): Uncomment once secrets are filled with proper values // await providerContainer.read(firebaseMessagingServiceProvider.future); } } @@ -150,6 +152,7 @@ Future _setupFirebaseMessaging() async { // TODO(strv): Support it or remove it! Future _setupLocalNotificationsService() async { if (!AppPlatform.isLinux && !AppPlatform.isWindows) { + // TODO(strv): Uncomment once secrets are filled with proper values // await providerContainer.read(notificationsServiceProvider.future); } } @@ -161,7 +164,7 @@ Future _setupRASP({required Flavor flavor}) async { final config = TalsecConfig( isProd: flavor == Flavor.production && !kDebugMode, - watcherMail: 'flutter_template@strv.com', + watcherMail: 'michal.urbanek@strv.com', /// For Android androidConfig: AndroidConfig(