diff --git a/.github/workflows/frontend-admin-build-local.yml b/.github/workflows/frontend-admin-build-local.yml index b8b50544..7f34c9c1 100644 --- a/.github/workflows/frontend-admin-build-local.yml +++ b/.github/workflows/frontend-admin-build-local.yml @@ -73,13 +73,13 @@ jobs: run: | if [ "${{ github.ref_name }}" = "main" ] || [ "${{ github.base_ref }}" = "main" ]; then echo "profile=production" >> $GITHUB_OUTPUT - echo "channel=production" >> $GITHUB_OUTPUT + echo "channel=production-admin" >> $GITHUB_OUTPUT echo "api_url=https://mln-uber.club" >> $GITHUB_OUTPUT echo "update_url=https://ota.uber-stup.club" >> $GITHUB_OUTPUT echo "apk_name=admin-panel-production-$(date +%Y%m%d-%H%M).apk" >> $GITHUB_OUTPUT else echo "profile=pre-prod" >> $GITHUB_OUTPUT - echo "channel=pre-prod" >> $GITHUB_OUTPUT + echo "channel=pre-prod-admin" >> $GITHUB_OUTPUT echo "api_url=https://5.181.0.112.nip.io" >> $GITHUB_OUTPUT echo "update_url=https://ota.uber-stup.club" >> $GITHUB_OUTPUT echo "apk_name=admin-panel-pre-prod-$(date +%Y%m%d-%H%M).apk" >> $GITHUB_OUTPUT @@ -172,8 +172,9 @@ jobs: env: EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} EXPO_PUBLIC_API_URL: ${{ steps.config.outputs.api_url }} + EXPO_PUBLIC_UPDATE_URL: https://ota.uber-stup.club run: | - RUNTIME_VERSION=$(jq -r '.expo.runtimeVersion // .expo.version' app.json) + RUNTIME_VERSION=$(jq -r '.expo.version' app.json) npx expo export --platform android --output-dir dist cd dist && zip -r ../bundle.zip . && cd .. curl -X POST "https://ota.uber-stup.club/api/upload" \ diff --git a/.github/workflows/frontend-client-build-local.yml b/.github/workflows/frontend-client-build-local.yml index 72a0b909..09431151 100644 --- a/.github/workflows/frontend-client-build-local.yml +++ b/.github/workflows/frontend-client-build-local.yml @@ -172,8 +172,9 @@ jobs: env: EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} EXPO_PUBLIC_API_URL: ${{ steps.config.outputs.api_url }} + EXPO_PUBLIC_UPDATE_URL: https://ota.uber-stup.club run: | - RUNTIME_VERSION=$(jq -r '.expo.runtimeVersion // .expo.version' app.json) + RUNTIME_VERSION=$(jq -r '.expo.version' app.json) npx expo export --platform android --output-dir dist cd dist && zip -r ../bundle.zip . && cd .. curl -X POST "https://ota.uber-stup.club/api/upload" \ diff --git a/frontend-admin/App.tsx b/frontend-admin/App.tsx index 23549c56..d9120788 100644 --- a/frontend-admin/App.tsx +++ b/frontend-admin/App.tsx @@ -1,6 +1,7 @@ -import React, { useMemo } from "react"; +import React, { useMemo, useEffect } from "react"; import { StatusBar } from "expo-status-bar"; import { ActivityIndicator, View, StyleSheet } from "react-native"; +import * as Updates from "expo-updates"; import { NavigationContainer } from "@react-navigation/native"; import { createNativeStackNavigator } from "@react-navigation/native-stack"; @@ -91,6 +92,22 @@ function RootNavigator() { } export default function App() { + useEffect(() => { + if (__DEV__) return; + const checkForUpdate = async () => { + try { + const update = await Updates.checkForUpdateAsync(); + if (update.isAvailable) { + await Updates.fetchUpdateAsync(); + await Updates.reloadAsync(); + } + } catch { + // Silently ignore update errors + } + }; + checkForUpdate(); + }, []); + return ( diff --git a/frontend-admin/app.config.js b/frontend-admin/app.config.js new file mode 100644 index 00000000..54e99fb5 --- /dev/null +++ b/frontend-admin/app.config.js @@ -0,0 +1,11 @@ +module.exports = ({ config }) => { + const updateUrl = process.env.EXPO_PUBLIC_UPDATE_URL; + + return { + ...config, + updates: { + ...config.updates, + ...(updateUrl ? { url: updateUrl } : {}), + }, + }; +}; diff --git a/frontend-admin/app.json b/frontend-admin/app.json index 800bf641..2c0174cb 100644 --- a/frontend-admin/app.json +++ b/frontend-admin/app.json @@ -36,6 +36,7 @@ "plugins": [ "expo-font", "expo-location", + "expo-updates", [ "expo-build-properties", { @@ -50,6 +51,12 @@ "projectId": "fcb7a0bc-5b2f-453d-ba16-9f97d9f0e440" } }, - "owner": "xor290" + "owner": "xor290", + "runtimeVersion": { + "policy": "appVersion" + }, + "updates": { + "url": "https://u.expo.dev/fcb7a0bc-5b2f-453d-ba16-9f97d9f0e440" + } } } diff --git a/frontend-admin/eas.json b/frontend-admin/eas.json index bf99a48f..1105f96e 100644 --- a/frontend-admin/eas.json +++ b/frontend-admin/eas.json @@ -25,7 +25,7 @@ "EXPO_PUBLIC_API_URL": "https://5.181.0.112.nip.io", "EXPO_PUBLIC_UPDATE_URL": "https://ota.uber-stup.club" }, - "channel": "pre-prod" + "channel": "pre-prod-admin" }, "production": { "distribution": "internal", @@ -37,7 +37,7 @@ "EXPO_PUBLIC_API_URL": "https://mln-uber.club", "EXPO_PUBLIC_UPDATE_URL": "https://ota.uber-stup.club" }, - "channel": "production" + "channel": "production-admin" } } } diff --git a/frontend-admin/package-lock.json b/frontend-admin/package-lock.json index 8c8a9900..1886645d 100644 --- a/frontend-admin/package-lock.json +++ b/frontend-admin/package-lock.json @@ -21,6 +21,7 @@ "expo-image-picker": "~17.0.11", "expo-location": "~19.0.8", "expo-status-bar": "~3.0.9", + "expo-updates": "~29.0.17", "jwt-decode": "^4.0.0", "react": "19.1.0", "react-dom": "19.1.0", @@ -79,6 +80,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", + "peer": true, "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", @@ -1366,6 +1368,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", + "peer": true, "engines": { "node": ">=6.9.0" } @@ -2558,6 +2561,7 @@ "version": "7.1.28", "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.1.28.tgz", "integrity": "sha512-d1QDn+KNHfHGt3UIwOZvupvdsDdiHYZBEj7+wL2yDVo3tMezamYy60H9s3EnNVE1Ae1ty0trc7F2OKqo/RmsdQ==", + "peer": true, "dependencies": { "@react-navigation/core": "^7.14.0", "escape-string-regexp": "^4.0.0", @@ -2757,6 +2761,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.17.tgz", "integrity": "sha512-Qec1E3mhALmaspIrhWt9jkQMNdw6bReVu64mjvhbhq2NFPftLPVr+l1SZgmw/66WwBNpDh7ao5AT6gF5v41PFA==", "devOptional": true, + "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -3325,6 +3330,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -3970,7 +3976,6 @@ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "optional": true, - "peer": true, "dependencies": { "iconv-lite": "^0.6.2" } @@ -3980,7 +3985,6 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "optional": true, - "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -4112,6 +4116,7 @@ "resolved": "https://registry.npmjs.org/expo/-/expo-54.0.34.tgz", "integrity": "sha512-XkVHguZZDC8BcTQxHAd14/TQFbDp1Wt0Z/KApO9t68Ll5A127hLCPzU+a9gytfCIiyL/V1IpF1vIcOLKEVAoNQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.20.0", "@expo/cli": "54.0.24", @@ -4210,6 +4215,12 @@ "react-native": "*" } }, + "node_modules/expo-eas-client": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/expo-eas-client/-/expo-eas-client-1.0.8.tgz", + "integrity": "sha512-5or11NJhSeDoHHI6zyvQDW2cz/yFyE+1Cz8NTs5NK8JzC7J0JrkUgptWtxyfB6Xs/21YRNifd3qgbBN3hfKVgA==", + "license": "MIT" + }, "node_modules/expo-file-system": { "version": "19.0.22", "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-19.0.22.tgz", @@ -4224,6 +4235,7 @@ "version": "14.0.11", "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-14.0.11.tgz", "integrity": "sha512-ga0q61ny4s/kr4k8JX9hVH69exVSIfcIc19+qZ7gt71Mqtm7xy2c6kwsPTCyhBW2Ro5yXTT8EaZOpuRi35rHbg==", + "peer": true, "dependencies": { "fontfaceobserver": "^2.1.0" }, @@ -4253,6 +4265,12 @@ "expo": "*" } }, + "node_modules/expo-json-utils": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/expo-json-utils/-/expo-json-utils-0.15.0.tgz", + "integrity": "sha512-duRT6oGl80IDzH2LD2yEFWNwGIC2WkozsB6HF3cDYNoNNdUvFk6uN3YiwsTsqVM/D0z6LEAQ01/SlYvN+Fw0JQ==", + "license": "MIT" + }, "node_modules/expo-keep-awake": { "version": "15.0.8", "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-15.0.8.tgz", @@ -4270,6 +4288,19 @@ "expo": "*" } }, + "node_modules/expo-manifests": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/expo-manifests/-/expo-manifests-1.0.11.tgz", + "integrity": "sha512-6zItytTewN37Cjhp3glUg0ozrgW2GwB8x9wtfzUNoJIMmxO38nnGdTLMaotYhRqdf5PP2Dzdmej1HDHXVNUpRw==", + "license": "MIT", + "dependencies": { + "@expo/config": "~12.0.13", + "expo-json-utils": "~0.15.0" + }, + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-modules-autolinking": { "version": "3.0.25", "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-3.0.25.tgz", @@ -4320,6 +4351,57 @@ "react-native": "*" } }, + "node_modules/expo-structured-headers": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/expo-structured-headers/-/expo-structured-headers-5.0.0.tgz", + "integrity": "sha512-RmrBtnSphk5REmZGV+lcdgdpxyzio5rJw8CXviHE6qH5pKQQ83fhMEcigvrkBdsn2Efw2EODp4Yxl1/fqMvOZw==", + "license": "MIT" + }, + "node_modules/expo-updates": { + "version": "29.0.17", + "resolved": "https://registry.npmjs.org/expo-updates/-/expo-updates-29.0.17.tgz", + "integrity": "sha512-9h78cs6Q2rs/dEY7zAgyEm/m6J5rHy8RNpRyhilEAvrzrGLHChVZJT+bSR2RwNJg1DtwUNEjCgZrxDlM7LnNkg==", + "license": "MIT", + "dependencies": { + "@expo/code-signing-certificates": "^0.0.6", + "@expo/plist": "^0.4.8", + "@expo/spawn-async": "^1.7.2", + "arg": "4.1.0", + "chalk": "^4.1.2", + "debug": "^4.3.4", + "expo-eas-client": "~1.0.8", + "expo-manifests": "~1.0.11", + "expo-structured-headers": "~5.0.0", + "expo-updates-interface": "~2.0.0", + "getenv": "^2.0.0", + "glob": "^13.0.0", + "ignore": "^5.3.1", + "resolve-from": "^5.0.0" + }, + "bin": { + "expo-updates": "bin/cli.js" + }, + "peerDependencies": { + "expo": "*", + "react": "*", + "react-native": "*" + } + }, + "node_modules/expo-updates-interface": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/expo-updates-interface/-/expo-updates-interface-2.0.0.tgz", + "integrity": "sha512-pTzAIufEZdVPKql6iMi5ylVSPqV1qbEopz9G6TSECQmnNde2nwq42PxdFBaUEd8IZJ/fdJLQnOT3m6+XJ5s7jg==", + "license": "MIT", + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-updates/node_modules/arg": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.0.tgz", + "integrity": "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg==", + "license": "MIT" + }, "node_modules/expo/node_modules/@expo/cli": { "version": "54.0.24", "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-54.0.24.tgz", @@ -7010,6 +7092,7 @@ "version": "19.1.0", "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -7027,6 +7110,7 @@ "version": "19.1.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "peer": true, "dependencies": { "scheduler": "^0.26.0" }, @@ -7054,6 +7138,7 @@ "version": "0.81.5", "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.81.5.tgz", "integrity": "sha512-1w+/oSjEXZjMqsIvmkCRsOc8UBYv163bTWKTI8+1mxztvQPhCRYGTvZ/PL1w16xXHneIj/SLGfxWg2GWN2uexw==", + "peer": true, "dependencies": { "@jest/create-cache-key-function": "^29.7.0", "@react-native/assets-registry": "0.81.5", @@ -7180,6 +7265,7 @@ "version": "5.6.2", "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.6.2.tgz", "integrity": "sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg==", + "peer": true, "peerDependencies": { "react": "*", "react-native": "*" @@ -7189,6 +7275,7 @@ "version": "4.16.0", "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.16.0.tgz", "integrity": "sha512-yIAyh7F/9uWkOzCi1/2FqvNvK6Wb9Y1+Kzn16SuGfN9YFJDTbwlzGRvePCNTOX0recpLQF3kc2FmvMUhyTCH1Q==", + "peer": true, "dependencies": { "react-freeze": "^1.0.0", "react-native-is-edge-to-edge": "^1.2.1", @@ -7203,6 +7290,7 @@ "version": "0.21.2", "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.21.2.tgz", "integrity": "sha512-SO2t9/17zM4iEnFvlu2DA9jqNbzNhoUP+AItkoCOyFmDMOhUnBBznBDCYN92fGdfAkfQlWzPoez6+zLxFNsZEg==", + "peer": true, "dependencies": { "@babel/runtime": "^7.18.6", "@react-native/normalize-colors": "^0.74.1", @@ -7232,6 +7320,7 @@ "version": "13.15.0", "resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.15.0.tgz", "integrity": "sha512-Vzjgy8mmxa/JO6l5KZrsTC7YemSdq+qB01diA0FqjUTaWGAGwuykpJ73MDj3+mzBSlaDxAEugHzTtkUQkQEQeQ==", + "peer": true, "dependencies": { "escape-string-regexp": "^4.0.0", "invariant": "2.2.4" @@ -7245,6 +7334,7 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.5.1.tgz", "integrity": "sha512-lJG6Uk9YuojjEX/tQrCbcbmpdLCSFxDK1rJlkDhgqkVi1KZzG7cdcBFQRqyNOOzR9Y0CXNuldmtWTGOyM0k0+w==", + "peer": true, "dependencies": { "@babel/plugin-transform-arrow-functions": "^7.0.0-0", "@babel/plugin-transform-class-properties": "^7.0.0-0", @@ -7368,6 +7458,7 @@ "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -7624,8 +7715,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "optional": true, - "peer": true + "optional": true }, "node_modules/sax": { "version": "1.4.4", @@ -8231,6 +8321,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "peer": true, "engines": { "node": ">=12" }, diff --git a/frontend-admin/package.json b/frontend-admin/package.json index f93c97b5..93911244 100644 --- a/frontend-admin/package.json +++ b/frontend-admin/package.json @@ -22,6 +22,7 @@ "expo-image-picker": "~17.0.11", "expo-location": "~19.0.8", "expo-status-bar": "~3.0.9", + "expo-updates": "~29.0.17", "jwt-decode": "^4.0.0", "react": "19.1.0", "react-dom": "19.1.0", diff --git a/mobile/app.config.js b/mobile/app.config.js new file mode 100644 index 00000000..54e99fb5 --- /dev/null +++ b/mobile/app.config.js @@ -0,0 +1,11 @@ +module.exports = ({ config }) => { + const updateUrl = process.env.EXPO_PUBLIC_UPDATE_URL; + + return { + ...config, + updates: { + ...config.updates, + ...(updateUrl ? { url: updateUrl } : {}), + }, + }; +};