Compare commits

...
Sign in to create a new pull request.

145 commits

Author SHA1 Message Date
Viktor Havaka
882b7a7cec
Enable donates for AppStore (250302).
Signed-off-by: Viktor Havaka <viktor.govako@gmail.com>
2025-03-05 17:42:47 -03:00
18c36b18d2 Add 250227
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-03-02 16:15:54 +00:00
Viktor Havaka
7c48e43fc4
Set iOS donates on 250301.
Signed-off-by: Viktor Havaka <viktor.govako@gmail.com>
2025-02-28 17:51:56 -03:00
Viktor Havaka
7c92fc3ce6
Enable donates.
Signed-off-by: Viktor Havaka <viktor.govako@gmail.com>
2025-02-26 23:05:57 -03:00
71c8217bb8 Add 250213
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-17 17:08:42 +00:00
Viktor Havaka
fc751e37aa
Enable donates for Google Play.
Signed-off-by: Viktor Havaka <viktor.govako@gmail.com>
2025-01-31 12:48:41 -03:00
Viktor Havaka
9a794b9652
Enable donates for AppStore.
Signed-off-by: Viktor Havaka <viktor.govako@gmail.com>
2025-01-29 10:43:31 -03:00
a2be921690 Add 250121
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-01-26 22:53:10 +03:00
878dfa9c06 Disable Xmas tree
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-01-26 11:32:08 +03:00
eab3488455 Specify GitHub Environment to allow access to CF secrets
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2024-12-23 17:25:08 +00:00
e65565a1e8 Enable Xmas tree 2024-12-23 17:19:01 +00:00
22617fa964 Add actual CONTRIBUTORS file 2024-12-13 22:54:17 +01:00
Alexander Borsuk
e41c085fd7 npm run upgrade
Signed-off-by: Alexander Borsuk <me@alex.bio>
2024-12-13 14:57:57 +01:00
Alexander Borsuk
cc43bceb03 Added vietnam server
Signed-off-by: Alexander Borsuk <me@alex.bio>
2024-12-13 14:52:26 +01:00
3ec9d9e5b9 Distinct links for About/Donate and PlacePage/Other 2024-12-13 14:52:26 +01:00
Viktor Havaka
4b8c381c33 Enable donates for Google Play (241127)
Signed-off-by: Viktor Havaka <viktor.govako@gmail.com>
2024-12-13 14:52:26 +01:00
Viktor Havaka
a269f44aa4 Enable donates for AppStore (241127).
Signed-off-by: Viktor Havaka <viktor.govako@gmail.com>
2024-12-13 14:52:26 +01:00
Viktor Havaka
bb534682fd Added 241122 data version.
Signed-off-by: Viktor Havaka <viktor.govako@gmail.com>
2024-12-13 14:52:26 +01:00
Viktor Havaka
7fb90b0444 Enable iOS donates for TestFlight (241126).
Signed-off-by: Viktor Havaka <viktor.govako@gmail.com>
2024-12-13 14:52:26 +01:00
14b002618e Add PlacePage donations
https://docs.google.com/document/d/1f2zjYBr24Wlh1vBcTtCmisx-HidaYwivkXZGCHjlTTY/edit
2024-12-13 14:52:26 +01:00
Viktor Havaka
05100af769
Added 241017 data version.
Signed-off-by: Viktor Havaka <viktor.govako@gmail.com>
2024-11-14 16:20:57 -03:00
Alexander Borsuk
9eb4405f03 241107 data version
Signed-off-by: Alexander Borsuk <me@alex.bio>
2024-11-14 19:59:28 +01:00
Viktor Havaka
5d26908ec2
Enable donates for AppStore and GP.
Signed-off-by: Viktor Havaka <viktor.govako@gmail.com>
2024-11-13 08:04:31 -03:00
Alexander Borsuk
d7ac8bc5fa Added new version of MapXPlorer 1.8.7-1-ios into abusers list
Signed-off-by: Alexander Borsuk <me@alex.bio>
2024-11-12 12:38:53 +01:00
Alexander Borsuk
fde585a122 Added new version of MapXPlorer into abusers list
Signed-off-by: Alexander Borsuk <me@alex.bio>
2024-11-12 07:52:36 +01:00
Viktor Havaka
13ad21530d
Enable donates for Google Play (241023)
Signed-off-by: Viktor Havaka <viktor.govako@gmail.com>
2024-10-26 09:31:03 -03:00
Alexander Borsuk
50c140ebcb
Trap for cdn abusers with a specific map/app version (#89) 2024-10-25 14:18:57 +02:00
Viktor Havaka
cbe27ccda4
Enable donates for AppStore (241022).
Signed-off-by: Viktor Havaka <viktor.govako@gmail.com>
2024-10-23 17:59:13 -03:00
Viktor Havaka
087cbaf316
Enable donates for Google Play (241008)
Signed-off-by: Viktor Havaka <viktor.govako@gmail.com>
2024-10-11 19:12:54 -03:00
Viktor Havaka
30cbe4463c
Update donates for Apple (08) and Google (04).
Signed-off-by: Viktor Havaka <viktor.govako@gmail.com>
2024-10-09 20:26:22 -03:00
0cb3ff3dc1 Don't use cdn-us1 (Cloudflare + Backbaze) for the actual versions
Cloudflare almost always have lowest ping, leaving no chance to
other servers to server data for iOS users. Viktor reported some
issues with Cloudflare from Argentina. "Let's see if us-west1 +
us-east1 + uk1 + nl1 servers can handle America and Oceania.
2024-10-05 11:19:58 -07:00
b8200aadc8
Enable donates for Apple 241004
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2024-10-05 07:07:00 -07:00
375533572c Add 241001 2024-10-04 07:50:54 +01:00
ecc91e7737 Refactor the list of servers + add us-east1 2024-09-25 22:46:45 +03:00
Viktor Govako
225cc73df7 Run prettier. 2024-09-21 08:03:54 -03:00
Viktor Govako
2783718dd0 Enable donates for GP 240919. 2024-09-21 08:03:54 -03:00
37b706fa51
Enable donates for iOS 240908
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2024-09-15 18:36:45 +01:00
9c8fac62f2
Disable donates in Google Play while waiting for an appeal
https://github.com/organicmaps/organicmaps/issues/9240#issuecomment-2342973474

Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2024-09-11 09:38:20 +01:00
983913c1af Add 240904 2024-09-08 21:33:59 +01:00
Alexander Borsuk
0fdfb8a2ea Replaced us2 with us3
Signed-off-by: Alexander Borsuk <me@alex.bio>
2024-08-22 15:51:26 +02:00
Alexander Borsuk
fe6e1d889c Enabled donations for GP 240817
Signed-off-by: Alexander Borsuk <me@alex.bio>
2024-08-19 15:56:31 +02:00
Alexander Borsuk
3a7b3fe8cd Enabled donations for iOS 240815
Signed-off-by: Alexander Borsuk <me@alex.bio>
2024-08-17 15:02:58 +02:00
Alexander Borsuk
a177326249 New map data 240810
Signed-off-by: Alexander Borsuk <me@alex.bio>
2024-08-16 17:46:11 +02:00
671d4934db
Remove DONATE_URL_RU
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2024-08-13 11:30:36 +01:00
Alexander Borsuk
9a9af6fdcc Add cdn-de3 node 2024-07-31 09:04:25 +02:00
Alexander Borsuk
84689e9ec6 Update github actions 2024-07-31 09:04:25 +02:00
Alexander Borsuk
db02d73f9e Update readme 2024-07-31 09:04:25 +02:00
Alexander Borsuk
9587929d85 Removed eslint
It fails with dependency error for several months:

Running custom build: npm ci --prefer-offline --no-audit && npm run build
npm error code ERESOLVE
npm error ERESOLVE could not resolve
npm error
npm error While resolving: @typescript-eslint/eslint-plugin@7.18.0
npm error Found: eslint@9.8.0
npm error node_modules/eslint
npm error   dev eslint@"^9.8.0" from the root project
npm error   peer eslint@"^6.0.0 || ^7.0.0 || >=8.0.0" from @eslint-community/eslint-utils@4.4.0
npm error   node_modules/@eslint-community/eslint-utils
npm error     @eslint-community/eslint-utils@"^4.4.0" from @typescript-eslint/utils@7.18.0
npm error     node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils
npm error       @typescript-eslint/utils@"7.18.0" from @typescript-eslint/eslint-plugin@7.18.0
npm error       node_modules/@typescript-eslint/eslint-plugin
npm error         dev @typescript-eslint/eslint-plugin@"^7.18.0" from the root project
npm error       1 more (@typescript-eslint/type-utils)
npm error     @eslint-community/eslint-utils@"^4.2.0" from eslint@9.8.0
npm error
npm error Could not resolve dependency:
npm error peer eslint@"^8.56.0" from @typescript-eslint/eslint-plugin@7.18.0
npm error node_modules/@typescript-eslint/eslint-plugin
npm error   dev @typescript-eslint/eslint-plugin@"^7.18.0" from the root project
npm error
npm error Conflicting peer dependency: eslint@8.57.0
npm error node_modules/eslint
npm error   peer eslint@"^8.56.0" from @typescript-eslint/eslint-plugin@7.18.0
npm error   node_modules/@typescript-eslint/eslint-plugin
npm error     dev @typescript-eslint/eslint-plugin@"^7.18.0" from the root project
npm error
npm error Fix the upstream dependency conflict, or retry
npm error this command with --force or --legacy-peer-deps
npm error to accept an incorrect (and potentially broken) dependency resolution.
npm error
npm error
npm error For a full report see:
npm error /Users/alex/.npm/_logs/2024-07-31T06_48_17_698Z-eresolve-report.txt
npm error A complete log of this run can be found in: /Users/alex/.npm/_logs/2024-07-31T06_48_17_698Z-debug-0.log

✘ [ERROR] Running custom build `npm ci --prefer-offline --no-audit && npm run build` failed. There are likely more logs from your build command above.
2024-07-31 09:03:41 +02:00
Alexander Borsuk
424bfab1f1 npm run upgrade 2024-07-31 07:58:20 +02:00
Alexander Borsuk
9fc90d1dd5 Enable donates for GP android 240729
Signed-off-by: Alexander Borsuk <me@alex.bio>
2024-07-29 20:42:12 +02:00
f7840e6a5a Add 240723 2024-07-28 11:00:18 +03:00
Alexander Borsuk
3e1d1e7aab Enable donates for iOS 240727
Signed-off-by: Alexander Borsuk <me@alex.bio>
2024-07-27 19:00:27 +02:00
Alexander Borsuk
265f65c8e3 Enable donations for Android hotfix 240723
Signed-off-by: Alexander Borsuk <me@alex.bio>
2024-07-25 09:37:20 +02:00
Alexander Borsuk
6bafd059da Enabled GP donates for 240708
Signed-off-by: Alexander Borsuk <me@alex.bio>
2024-07-15 14:23:20 +03:00
Alexander Borsuk
da32ce8662 npm run upgrade
Signed-off-by: Alexander Borsuk <me@alex.bio>
2024-07-10 09:13:09 +03:00
Alexander Borsuk
9afb5094d2 Enabled donations for iOS July release 2024-07-10 09:10:43 +03:00
d4e1a65e23 Add 240702 2024-07-08 22:00:01 +01:00
8b3b48d689
Enable donations for 240619 (Apple)
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2024-06-21 14:37:52 +03:00
02e58cb29b
Enable donations for 240619 (Google)
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2024-06-20 10:06:58 +03:00
441e08f964 Add 240613 2024-06-19 10:24:46 +03:00
73e5aed304
Donates 240602
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2024-06-05 11:27:01 +03:00
36e12a6497 Add 240528 2024-06-02 21:03:42 +03:00
c2ae1236eb
Enable donations for 240511 (Google)
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2024-05-13 19:43:56 +03:00
Alexander Borsuk
6bfc5c45c4 Enable donates iOS 240510
Signed-off-by: Alexander Borsuk <me@alex.bio>
2024-05-11 23:05:08 +02:00
2a92abd741 Add 240429
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2024-05-04 21:23:48 +03:00
ea3cef7ca5 Revert "Add G-Core CDN"
This reverts commit af1ba4f50d.
2024-05-04 21:23:48 +03:00
7410279e36 Add G-Core CDN
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2024-04-26 15:57:06 +03:00
a7e13bea9a
Enable donations for 240331 (Apple)
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2024-04-03 17:44:34 +03:00
988c8d4c7d
Enable donations for 240331 (Google)
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2024-04-03 07:15:43 +03:00
6c95780f09 Add 240326
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2024-03-31 10:17:15 +03:00
accf6a5607
Enable donates for 240307 (AppStore) 2024-03-08 22:42:27 +03:00
944fe69dcb
Enable donates for 240303 (AppStore) 2024-03-07 18:06:32 +03:00
342050ba56
Enable donates for 240303 2024-03-05 09:24:42 +03:00
fc36848ecb Add 240228
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2024-03-04 08:23:15 +02:00
Alexander Borsuk
2341c15787 Enabled donations for Android 240301
Signed-off-by: Alexander Borsuk <me@alex.bio>
2024-03-02 08:40:57 +02:00
Alexander Borsuk
9bb0348fca Enable iOS donates 240227
Signed-off-by: Alexander Borsuk <me@alex.bio>
2024-03-01 20:34:17 +02:00
28e9b826d1
Enable donates for 240220 2024-02-20 23:23:26 +03:00
Alexander Borsuk
a2225f5d04 Enable donates for 240206
Signed-off-by: Alexander Borsuk <me@alex.bio>
2024-02-08 00:56:42 +02:00
2f43b868ab Add 240202 2024-02-06 11:43:01 +02:00
Alexander Borsuk
83919b9a79 Disabled Christmas Tree
Signed-off-by: Alexander Borsuk <me@alex.bio>
2024-01-21 03:45:15 +02:00
Alexander Borsuk
d0524e78d1 Enable donates for ios and android 240109
Signed-off-by: Alexander Borsuk <me@alex.bio>
2024-01-10 23:33:54 +02:00
c57a691110 Add 240105 2024-01-09 23:13:05 +02:00
57daba6e09 Temporary disable donates Google Play for case [0-8882000035986]
https://github.com/organicmaps/organicmaps/issues/7000
2023-12-28 14:21:44 +01:00
Alexander Borsuk
8fa182c15b Donates for iOS 12 hotfix release 231222
Signed-off-by: Alexander Borsuk <me@alex.bio>
2023-12-24 20:03:32 +01:00
245f3f61ad
Enable donates for Google 231221 2023-12-23 10:02:58 +03:00
b08cf5bff0
Redirect users from Russia directly to CloudPayments (test)
Skip https://organicmaps.app/ru/donate web-site and redirect users to https://pay.cloudtips.ru/p/774e5188 via https://donate.organicmaps.ru
2023-12-22 12:02:07 +03:00
Alexander Borsuk
fccf37e9e7 Enable donates for iOS 231220
Signed-off-by: Alexander Borsuk <me@alex.bio>
2023-12-21 19:18:51 +01:00
f02e1ec6a4
Enable donates and NY for iOS 231218 2023-12-19 21:12:28 +03:00
923b8f7274
Enable donates and NY for Google 231218 2023-12-19 09:11:01 +03:00
4468cdd776 Add 231213
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2023-12-17 22:37:10 +02:00
Alexander Borsuk
dc91b633ba npm run upgrade, fixed toml prettier error
Signed-off-by: Alexander Borsuk <me@alex.bio>
2023-12-14 22:56:17 +02:00
Alexander Borsuk
9b2e72600d Enable donates for 2023.12.13 iOS hotfix
Signed-off-by: Alexander Borsuk <me@alex.bio>
2023-12-14 21:51:24 +01:00
38e9ce98bb Enable donations for Apple Store 2023-11-21 20:48:07 +02:00
428d8a7cf1
Enable donation for Google Play 2023-11-21 12:26:38 +03:00
8261dad9e7 Add 231113 2023-11-17 11:58:21 +02:00
Alexander Borsuk
749a23eaaf Enabled donations on iOS
Signed-off-by: Alexander Borsuk <me@alex.bio>
2023-09-25 19:58:45 +02:00
Alexander Borsuk
a51a9a1a66 Enable GP donates
Signed-off-by: Alexander Borsuk <me@alex.bio>
2023-09-23 20:15:21 +03:00
8811f9eb9f Add 230920 2023-09-22 21:00:45 +02:00
Alexander Borsuk
b72a43642c Enabled donates on iOS 2023-08-21 19:33:41 +03:00
3cb3a0b021 Add 230814 2023-08-18 18:27:23 +03:00
Alexander Borsuk
646d1d742a New data version on servers
Signed-off-by: Alexander Borsuk <me@alex.bio>
2023-07-15 03:13:38 +02:00
Alexander Borsuk
9f8037a8b8 npm run upgrade
Signed-off-by: Alexander Borsuk <me@alex.bio>
2023-07-15 03:13:38 +02:00
Alexander Borsuk
daefc2b9f8 Enable donates
Signed-off-by: Alexander Borsuk <me@alex.bio>
2023-07-15 03:13:38 +02:00
Viktor Govako
b9923075d7 Set iOS version 230604 2023-06-07 23:08:29 +02:00
8e58be8700 Add 230602 2023-06-05 17:28:37 +02:00
Alexander Borsuk
f9cda06dce npm run upgrade to use wrangler 3 for dev
Signed-off-by: Alexander Borsuk <me@alex.bio>
2023-06-01 06:59:32 +02:00
Alexander Borsuk
7956637a53 Respond with CF-cached beta server for all non-official data versions
Signed-off-by: Alexander Borsuk <me@alex.bio>
2023-05-31 08:55:17 +02:00
8eff170a1c Enable donations 2023-05-10 10:11:42 +02:00
4e0f234f0d Add 230503 2023-05-08 18:44:39 +02:00
Alexander Borsuk
1f2b52bc34 Enabled donations
Signed-off-by: Alexander Borsuk <me@alex.bio>
2023-04-05 10:17:58 +03:00
6ce9fc1bdf Add 230329 2023-04-02 21:25:18 +02:00
Alexander Borsuk
64a0fc7363 Support Linux and Mac versions parsing 2023-03-31 22:04:48 +02:00
Alexander Borsuk
535a7e1f56 Removed unused donation logs 2023-03-31 18:43:17 +03:00
Alexander Borsuk
e1e4cbbd0d Fixed type error 2023-03-31 18:43:17 +03:00
Alexander Borsuk
d41bc4228d Moved servers-endpoint reply into servers.ts
Signed-off-by: Alexander Borsuk <me@alex.bio>
2023-03-31 18:43:14 +03:00
Alexander Borsuk
c5f24e9912 Updated dependencies
Signed-off-by: Alexander Borsuk <me@alex.bio>
2023-03-31 18:42:47 +03:00
0b2824e067 Fix missing Android donation button
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2023-03-31 18:42:27 +03:00
ce61738a7e Enable donates for 230305 2023-03-08 16:05:50 +01:00
Alexander Borsuk
351fcfbfca npm run upgrade
Signed-off-by: Alexander Borsuk <me@alex.bio>
2023-03-08 16:01:28 +01:00
Alexander Borsuk
524651fe87 Returned back 3 data versions for nl1 and uk1, after applying btrfs with compression
Signed-off-by: Alexander Borsuk <me@alex.bio>
2023-03-08 16:01:28 +01:00
156e7d1cc3 Bump versions 2023-03-03 22:22:28 +01:00
Alexander Borsuk
51ba0a3752 Updated dependencies
Signed-off-by: Alexander Borsuk <me@alex.bio>
2023-02-15 12:14:08 +01:00
Alexander Borsuk
c523ab02a6 Enabled donates
Signed-off-by: Alexander Borsuk <me@alex.bio>
2023-02-15 12:14:08 +01:00
b41355f6c4 Bump versions
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2023-02-13 14:38:59 +02:00
90693bac24 Bump versions
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2023-01-27 13:12:43 +02:00
Alexander Borsuk
6552994012 Updated dependencies
Signed-off-by: Alexander Borsuk <me@alex.bio>
2023-01-15 21:14:09 +01:00
Alexander Borsuk
1912710579 Disable Christmas
Signed-off-by: Alexander Borsuk <me@alex.bio>
2023-01-14 16:15:36 +01:00
3b60daee40 Re-enable Huawei donates
Closes #51
2022-12-21 17:44:13 +02:00
f919baf1e5 Temporary disable donates for Huawei during review 2022-12-21 16:48:05 +02:00
ffe727de33 Enable Christmas / New Year Magic 2022-12-21 16:48:05 +02:00
fdf37d67dd Enable donates 2022-12-21 16:48:05 +02:00
adaed55bc3 Add 221216 2022-12-20 17:15:24 +02:00
Alexander Borsuk
9bcd62f236 Enabled back de1 aka eu2
Signed-off-by: Alexander Borsuk <me@alex.bio>
2022-12-02 08:59:46 +01:00
Alexander Borsuk
f21651f4cf Replaced de1 with de2
Re-enable back de1 on December 1

Signed-off-by: Alexander Borsuk <me@alex.bio>
2022-11-29 23:27:34 +01:00
Alexander Borsuk
e969d97dee Add npm run logs
Signed-off-by: Alexander Borsuk <me@alex.bio>
2022-11-29 23:21:25 +01:00
Alexander Borsuk
e7ba263f54 Updated dependencies
Signed-off-by: Alexander Borsuk <me@alex.bio>
2022-11-29 23:19:21 +01:00
Alexander Borsuk
489df04590 Enabled donates for iOS
Signed-off-by: Alexander Borsuk <me@alex.bio>
2022-11-29 00:44:03 +01:00
2dacf66a2c Enable donates for Google Play version 221124 2022-11-28 14:18:36 +01:00
6bd219559a Add 221119 and cdn-de2 2022-11-26 14:09:51 +02:00
Alexander Borsuk
cda02e3a23 Enabled donates url for iOS
Old iOS versions (without donate menu item support) do not send build number and flavor.
Newer iOS versions send it in the form: 2022.11.21-3-ios.

Signed-off-by: Alexander Borsuk <me@alex.bio>
2022-11-23 07:48:08 +02:00
Alexander Borsuk
8a4330d582 Temporarily workaround missing CF type definition check
Will revert it when CF fixes their @cloudflare/workers-types
2022-11-23 07:48:08 +02:00
Alexander Borsuk
7c3947d978 Upgrade to Wrangler 2
Signed-off-by: Alexander Borsuk <me@alex.bio>
2022-11-23 07:48:08 +02:00
Alexander Borsuk
feb0f468ef .gitignore .DS_Store files
Signed-off-by: Alexander Borsuk <me@alex.bio>
2022-11-23 07:48:08 +02:00
Alexander Borsuk
5a7540fc88 [2] Refactored tests. No any changes in the logic.
Now there is no copy-paste and it is easier to add new test cases

Signed-off-by: Alexander Borsuk <me@alex.bio>
2022-11-21 11:53:58 +02:00
Alexander Borsuk
8779625701 [1] Renamed generic utils to specialized versions
It is required for follow up commits

Signed-off-by: Alexander Borsuk <me@alex.bio>
2022-11-21 11:53:58 +02:00
25 changed files with 3055 additions and 8112 deletions

View file

@ -1,18 +0,0 @@
env:
es2020: true
worker: true
extends: ['plugin:@typescript-eslint/recommended']
parser: '@typescript-eslint/parser'
parserOptions:
ecmaVersion: 2020
ecmaFeatures:
impliedStrict: true
sourceType: module
root: true
rules:
indent: [error, 2, { SwitchCase: 1 }]
semi: [error, always]
quotes: [error, single, avoid-escape]
no-trailing-spaces: [error]
no-unused-vars: [error, { argsIgnorePattern: ^_ }]
prefer-const: [error, { destructuring: all }]

View file

@ -24,8 +24,9 @@ jobs:
- name: Dependencies
run: npm ci
- name: Lint
run: npm run lint:ci
# TODO(AB): ESLint dependencies are broken. Replace with a better and less complex linter.
# - name: Lint
# run: npm run lint:ci
- name: Format
run: npm run format:ci

View file

@ -7,10 +7,11 @@ on:
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: wrangler publish
uses: cloudflare/wrangler-action@1.3.0
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
environment: prod

3
.gitignore vendored
View file

@ -1,4 +1,7 @@
dist/
.wrangler/
# Autogenerated by esbuild.
workers-site/index.js
node_modules/
# Created by Finder on Mac
.DS_Store

View file

@ -1,4 +1,5 @@
{
"plugins": ["prettier-plugin-toml"],
"printWidth": 120,
"tabWidth": 2,
"singleQuote": true,

4
CONTRIBUTORS Normal file
View file

@ -0,0 +1,4 @@
Alexander Borsuk <me@alex.bio>
Roman Tsisyk <roman@organicmaps.app>
Viktor Havaka <viktor.govako@gmail.com>

View file

@ -1,4 +1,5 @@
Copyright 2021 Alexander Borsuk <me@alex.bio>
Copyright 2024 Organic Maps Contributors (see the CONTRIBUTORS file).
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View file

@ -12,22 +12,42 @@ npm i
## Development
Use `npx wrangler dev` for localhost development.
Use `npx wrangler dev` for localhost development and for testing using Cloudflare dev tools.
```
curl -H "X-OM-DataVersion: 241001" -H "X-OM-AppVersion: 2024.10.22-10-Google" -H 'Accept-Language: fr-FR' http://localhost:8787/maps
```
## Update node dependencies to their major versions
```bash
npm run upgrade
```
## Preview on workers.dev
Use `npx wrangler preview` to open and test deployed worker in browser.
Use `npx wrangler publish` to open and test deployed worker in browser at https://meta-dev.omaps.workers.dev
## Monitor Worker logs
For dev: `npx wrangler tail [--format json]`
For production: `npx wrangler tail --env prod [--format json]`
or
`npm run logs`
## Deployment
All pushes to master automatically deploy dev version to https://meta.omaps.workers.dev/
All pushes to master automatically deploy prod version to https://meta.omaps.workers.dev/ and https://meta.omaps.app/
Deploy to prod manually using `npx wrangler publish --env prod` or this
[action](https://github.com/organicmaps/meta/actions/workflows/deploy-master-to-prod.yml).
Deploy to test dev version live at https://meta-dev.omaps.workers.dev/ manually using `npx wrangler publish`.
## Known issues
- Cloudflare's free Flexible SSL certificates does not support 4-th level
- Cloudflare's free Flexible SSL certificates do not support 4-th level
subdomains like a.b.example.com, so you can see strange SSL errors.
- HTTPS `fetch` requests from Workers are converted to HTTP ones if the target
host is in the same Cloudflare zone, see [here](https://community.cloudflare.com/t/does-cloudflare-worker-allow-secure-https-connection-to-fetch-even-on-flexible-ssl/68051/12)

10003
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -7,29 +7,25 @@
"scripts": {
"build": "esbuild src/index.ts --bundle --outfile=dist/index.js",
"test": "jest",
"lint": "eslint --fix --ext .tsx,.ts src/",
"lint:ci": "eslint --ext .tsx,.ts src/ --max-warnings 0",
"format": "prettier --write '{src,test}/**/*.{ts,tsx}' '*.json' '*.yml' '*.toml' '.github/**/*.yml'",
"format:ci": "prettier --check '{src,test}/**/*.{ts,tsx}' '*.json' '*.yml' '*.toml' '.github/**/*.yml'",
"upgrade": "npx npm-check-updates -u"
"format": "prettier --write '{src,test}/**/*.{ts,tsx,json}' '*.json' '*.toml' '.github/**/*.yml'",
"format:ci": "prettier --check '{src,test}/**/*.{ts,tsx,json}' '*.json' '*.toml' '.github/**/*.yml'",
"upgrade": "npx npm-check-updates -u && npm install",
"logs": "npx wrangler tail --env prod --format json"
},
"author": "Alexander Borsuk <me@alex.bio>",
"license": "MIT",
"devDependencies": {
"@cloudflare/workers-types": "^3.16.0",
"@cloudflare/wrangler": "^1.19.13",
"@types/jest": "^29.0.3",
"@typescript-eslint/eslint-plugin": "^5.38.0",
"@typescript-eslint/parser": "^5.38.0",
"esbuild": "^0.15.9",
"eslint": "^8.24.0",
"jest": "^29.0.3",
"jest-environment-miniflare": "^2.9.0",
"miniflare": "^2.9.0",
"prettier": "^2.7.1",
"prettier-plugin-toml": "^0.3.1",
"ts-jest": "^29.0.2",
"typescript": "^4.8.3",
"wrangler": "^2.1.6"
"@cloudflare/workers-types": "^4.20241205.0",
"@types/jest": "^29.5.14",
"esbuild": "^0.24.0",
"jest": "^29.7.0",
"jest-environment-miniflare": "^2.14.4",
"miniflare": "^3.20241205.0",
"npm-check-updates": "^17.1.11",
"prettier": "^3.4.2",
"prettier-plugin-toml": "^2.0.1",
"ts-jest": "^29.2.5",
"typescript": "^5.7.2",
"wrangler": "^3.95.0"
}
}

View file

@ -1,54 +1,6 @@
export {};
import { parseDataVersion, parseAppVersion } from './utils';
// TODO: Implement automated version checks from this metaserver script.
// It should check by cron if actual files are really available on all servers.
export const SERVER = {
backblaze: {
// BackBlaze + CloudFlare (US-West) unmetered.
url: 'https://cdn-us1.organicmaps.app/',
dataVersions: [
210529, 210703, 210729, 210825, 211002, 211022, 211122, 220103, 220204, 220314, 220415, 220515, 220613, 220718,
220816, 220912, 221029,
],
},
uk1: {
// Mythic Beasts VPS (London, UK) 200TB/mo.
url: 'https://cdn-uk1.organicmaps.app/',
dataVersions: [220816, 220912, 221029],
},
nl1: {
// // Mythic Beasts VPS (Amsterdam, NL) 200TB/mo.
url: 'https://cdn-nl1.organicmaps.app/',
dataVersions: [220816, 220912, 221029],
},
planet: {
// Hetzner BareMetal (Falkenstein, Germany) unmetered
url: 'https://cdn.organicmaps.app/',
dataVersions: [
220103, 220204, 220314, 220415, 220515, 220613, 220718, 220816, 220912, 221019 /* beta only */, 221029,
],
},
fi1: {
// Hetzner Cloud (Helsinki, Finland), 20TB/mo
url: 'https://cdn-fi1.organicmaps.app/',
dataVersions: [220912, 221029],
},
de1: {
// Hetzner Cloud (Falkenstein, Germany), 20TB/mo
url: 'https://cdn-eu2.organicmaps.app/',
dataVersions: [220912, 221029],
},
us2: {
// Hetzner Cloud (Asburn, US East), 20TB/mo
url: 'https://cdn-us2.organicmaps.app/',
dataVersions: [220912, 221029],
},
};
const DONATE_URL = 'https://organicmaps.app/donate/';
const DONATE_URL_RU = 'https://organicmaps.app/ru/donate/';
import { getServersList } from './servers';
// Main entry point.
addEventListener('fetch', (event) => {
@ -61,91 +13,8 @@ export async function handleRequest(request: Request) {
switch (pathname) {
case '/maps': // Public for map files.
case '/resources': // Public for resources.
case '/servers': {
// Private for map files.
let servers;
// Starting from 2021-09, our clients have 'X-OM-DataVersion' header with the value
// of their current maps data version, for example, "211022" (October 22, 2021).
// It is lowercased by Cloudflare.
const dataVersion = parseDataVersion(request.headers.get('x-om-dataversion'));
if (dataVersion === null) {
// Older clients download from the archive.
servers = [SERVER.backblaze];
} else {
switch (request.cf?.continent) {
// See https://developers.cloudflare.com/firewall/cf-firewall-language/fields for a list of all continents.
case 'NA': // North America
case 'SA': // South America
case 'OC': // Oceania
servers = [SERVER.backblaze, SERVER.us2, SERVER.uk1, SERVER.nl1, SERVER.planet].filter((server) =>
server.dataVersions.includes(dataVersion),
);
break;
default:
// Every other continent + Tor networks.
servers = [SERVER.planet, SERVER.uk1, SERVER.nl1, SERVER.fi1, SERVER.de1].filter((server) =>
server.dataVersions.includes(dataVersion),
);
// Only fallback to the archive in the US if nothing was found closer.
if (servers.length == 0 && SERVER.backblaze.dataVersions.includes(dataVersion)) {
servers = [SERVER.backblaze];
}
}
}
// Fallback to the planet with freshly generated/beta data.
if (servers.length == 0) {
servers = [SERVER.planet];
}
servers = servers.map((server) => server.url);
// Header "X-OM-AppVersion: 2022.09.22-3-Google" (lowercased by CF) is supported from August 23, 2022.
const appVersion = parseAppVersion(request.headers.get('x-om-appversion'));
if (!appVersion) {
// Old format for <220823
return new Response(JSON.stringify(servers), {
headers: { 'Content-Type': 'application/json' },
});
}
// New format for >=220823
const response: {
servers: string[];
settings?: {
DonateUrl?: string;
};
} = {
servers: servers,
};
// Disable donates for Google reviewers for all google app versions AFTER this one.
const lastApprovedAndReleasedGoogleAppVersionCode = 221102;
let donatesEnabled = true;
if (
appVersion.flavor === 'google' &&
((request.cf?.asOrganization || '').toLowerCase().includes('google') ||
appVersion.code > lastApprovedAndReleasedGoogleAppVersionCode)
) {
donatesEnabled = false;
}
if (donatesEnabled) {
// To count enabled donations.
console.log('Donates enabled');
if (request.cf?.country == 'RU') {
response.settings = {
DonateUrl: DONATE_URL_RU,
};
} else {
response.settings = {
DonateUrl: DONATE_URL,
};
}
}
return new Response(JSON.stringify(response), {
headers: { 'Content-Type': 'application/json' },
});
}
case '/servers':
return getServersList(request);
}
return new Response('', { status: 404 });
}

44
src/locales.json Normal file
View file

@ -0,0 +1,44 @@
{
"en": {
"placePagePrompt": "Organic Maps app is free for everyone, thanks to your donations. No ads. No trackers. Open source.",
"perMonth": "/month",
"perYear": "/year",
"otherAmount": "Other"
},
"de": {
"placePagePrompt": "Organic Maps ist dank deiner Spenden für alle kostenlos. Keine Werbung. Keine Tracker. Open Source.",
"perMonth": "/Monat",
"perYear": "/Jahr",
"otherAmount": "Andere"
},
"fr": {
"placePagePrompt": "L'application Organic Maps est gratuite pour tout le monde grâce à vos dons. Pas de publicité. Pas de trackers. Open-source.",
"perMonth": "/mois",
"perYear": "/an",
"otherAmount": "Autre"
},
"nl": {
"placePagePrompt": "De Organic Maps app is gratis voor iedereen dankzij jullie donaties. Geen advertenties. Geen trackers. Open-source.",
"perMonth": "/maand",
"perYear": "/jaar",
"otherAmount": "Ander"
},
"it": {
"placePagePrompt": "L'app Organic Maps è gratuita per tutti grazie alle vostre donazioni. Nessuna pubblicità. Nessun tracker. Open-source.",
"perMonth": "/mese",
"perYear": "/anno",
"otherAmount": "Altro"
},
"es": {
"placePagePrompt": "Organic Maps es gratis para todos gracias a sus donaciones. Sin anuncios. Sin rastreadores. Código abierto.",
"perMonth": "/mes",
"perYear": "/año",
"otherAmount": "Otro"
},
"pt": {
"placePagePrompt": "O app Organic Maps é gratuito para todos graças às suas doações. Sem anúncios. Sem rastreadores. Código aberto.",
"perMonth": "/mês",
"perYear": "/ano",
"otherAmount": "Outro"
}
}

15
src/locales.ts Normal file
View file

@ -0,0 +1,15 @@
import LOCALES_JSON from './locales.json';
export interface Locale {
placePagePrompt: string;
perMonth: string;
perYear: string;
otherAmount: string;
}
export interface Locales {
[key: string]: Locale;
}
const LOCALES = LOCALES_JSON as Locales;
export default LOCALES;

198
src/products.json Normal file
View file

@ -0,0 +1,198 @@
{
"FR": [
{
"title": "4,99€$per_month",
"link": "https://donate.stripe.com/6oEg2912f2c81r200j"
},
{
"title": "34,99€$per_year",
"link": "https://donate.stripe.com/eVa2bjdP19EA0mY6oI"
},
{
"title": "$other",
"link": "https://donate.stripe.com/3csg29aCP0401r2dRj"
}
],
"DE": [
{
"title": "4,99€$per_month",
"link": "https://donate.stripe.com/6oEg2912f2c81r200j"
},
{
"title": "34,99€$per_year",
"link": "https://donate.stripe.com/eVa2bjdP19EA0mY6oI"
},
{
"title": "$other",
"link": "https://donate.stripe.com/3csg29aCP0401r2dRj"
}
],
"NL": [
{
"title": "4,99€$per_month",
"link": "https://donate.stripe.com/6oEg2912f2c81r200j"
},
{
"title": "34,99€$per_year",
"link": "https://donate.stripe.com/eVa2bjdP19EA0mY6oI"
},
{
"title": "$other",
"link": "https://donate.stripe.com/3csg29aCP0401r2dRj"
}
],
"IT": [
{
"title": "4,99€$per_month",
"link": "https://donate.stripe.com/6oEg2912f2c81r200j"
},
{
"title": "34,99€$per_year",
"link": "https://donate.stripe.com/eVa2bjdP19EA0mY6oI"
},
{
"title": "$other",
"link": "https://donate.stripe.com/3csg29aCP0401r2dRj"
}
],
"ES": [
{
"title": "4,99€$per_month",
"link": "https://donate.stripe.com/6oEg2912f2c81r200j"
},
{
"title": "34,99€$per_year",
"link": "https://donate.stripe.com/eVa2bjdP19EA0mY6oI"
},
{
"title": "$other",
"link": "https://donate.stripe.com/3csg29aCP0401r2dRj"
}
],
"PT": [
{
"title": "4,99€$per_month",
"link": "https://donate.stripe.com/6oEg2912f2c81r200j"
},
{
"title": "34,99€$per_year",
"link": "https://donate.stripe.com/eVa2bjdP19EA0mY6oI"
},
{
"title": "$other",
"link": "https://donate.stripe.com/3csg29aCP0401r2dRj"
}
],
"BE": [
{
"title": "4,99€$per_month",
"link": "https://donate.stripe.com/6oEg2912f2c81r200j"
},
{
"title": "34,99€$per_year",
"link": "https://donate.stripe.com/eVa2bjdP19EA0mY6oI"
},
{
"title": "$other",
"link": "https://donate.stripe.com/3csg29aCP0401r2dRj"
}
],
"AT": [
{
"title": "4,99€$per_month",
"link": "https://donate.stripe.com/6oEg2912f2c81r200j"
},
{
"title": "34,99€$per_year",
"link": "https://donate.stripe.com/eVa2bjdP19EA0mY6oI"
},
{
"title": "$other",
"link": "https://donate.stripe.com/3csg29aCP0401r2dRj"
}
],
"LU": [
{
"title": "4,99€$per_month",
"link": "https://donate.stripe.com/6oEg2912f2c81r200j"
},
{
"title": "34,99€$per_year",
"link": "https://donate.stripe.com/eVa2bjdP19EA0mY6oI"
},
{
"title": "$other",
"link": "https://donate.stripe.com/3csg29aCP0401r2dRj"
}
],
"MC": [
{
"title": "4,99€$per_month",
"link": "https://donate.stripe.com/6oEg2912f2c81r200j"
},
{
"title": "34,99€$per_year",
"link": "https://donate.stripe.com/eVa2bjdP19EA0mY6oI"
},
{
"title": "$other",
"link": "https://donate.stripe.com/3csg29aCP0401r2dRj"
}
],
"AD": [
{
"title": "4,99€$per_month",
"link": "https://donate.stripe.com/6oEg2912f2c81r200j"
},
{
"title": "34,99€$per_year",
"link": "https://donate.stripe.com/eVa2bjdP19EA0mY6oI"
},
{
"title": "$other",
"link": "https://donate.stripe.com/3csg29aCP0401r2dRj"
}
],
"SM": [
{
"title": "4,99€$per_month",
"link": "https://donate.stripe.com/6oEg2912f2c81r200j"
},
{
"title": "34,99€$per_year",
"link": "https://donate.stripe.com/eVa2bjdP19EA0mY6oI"
},
{
"title": "$other",
"link": "https://donate.stripe.com/3csg29aCP0401r2dRj"
}
],
"GB": [
{
"title": "£4.99$per_month",
"link": "https://donate.stripe.com/8wMg29fX98Awd9K28u"
},
{
"title": "£34.99$per_year",
"link": "https://donate.stripe.com/eVabLT9yL2c89Xy7sP"
},
{
"title": "$other",
"link": "https://donate.stripe.com/9AQg29h1dcQMedO3cH"
}
],
"US": [
{
"title": "$5.49$per_month",
"link": "https://donate.stripe.com/00g3fncKXcQMedO5kL"
},
{
"title": "$36.99$per_year",
"link": "https://donate.stripe.com/bIY6rz5ivcQM9XyaF6"
},
{
"title": "$other",
"link": "https://donate.stripe.com/eVa8zH5ivbMIfhS6oS"
}
]
}

40
src/products.ts Normal file
View file

@ -0,0 +1,40 @@
import LOCALES from './locales';
import PRODUCTS_JSON from './products.json';
export interface Product {
title: string;
link: string;
}
const PRODUCTS = PRODUCTS_JSON as Record<string, Product[]>;
export interface ProductsConfig {
placePagePrompt: string;
products: Product[];
}
export function getProducts(locale: string | null): ProductsConfig | undefined {
if (!locale) {
return undefined;
}
const parts = locale.split(/[-_]/);
const language = parts[0].toLowerCase();
const country = parts[1] ? parts[1].toUpperCase() : '';
const products = PRODUCTS[country];
const trans = LOCALES[language];
if (products === undefined || trans === undefined) {
return undefined;
}
return {
placePagePrompt: trans.placePagePrompt,
products: products.map((product) => ({
...product,
title: product.title
.replace('$other', trans.otherAmount)
.replace('$per_month', trans.perMonth)
.replace('$per_year', trans.perYear),
})),
};
}

239
src/servers.ts Normal file
View file

@ -0,0 +1,239 @@
import { parseDataVersion, parseAppVersion } from './versions';
import { getProducts, ProductsConfig } from './products';
export const DATA_VERSIONS = [
210529, //
210703,
210729,
210825,
211002,
211022,
211122,
220103,
220204,
220314,
220415,
220515,
220613,
220718,
220816,
220912,
221029,
221119,
221216,
230121,
230210,
230227,
230329,
230503,
230602,
230710,
230814,
230920,
231113,
231213,
240105,
240202,
240228,
240326,
240429,
240528,
240613,
240702,
240723,
240810,
240904,
241001,
241017,
241107,
241122,
250121,
250213,
250227,
];
const kUnlimited = 99999;
// TODO: Implement automated version checks from this metaserver script.
// It should check by cron if actual files are really available on all servers.
export const SERVER = {
backblaze: {
// BackBlaze + CloudFlare (US-West) unmetered.
url: 'https://cdn-us1.organicmaps.app/',
dataVersions: kUnlimited,
},
uk1: {
// Mythic Beasts VPS (London, UK) 200TB/mo.
url: 'https://cdn-uk1.organicmaps.app/',
dataVersions: 3,
},
nl1: {
// // Mythic Beasts VPS (Amsterdam, NL) 200TB/mo.
url: 'https://cdn-nl1.organicmaps.app/',
dataVersions: 3,
},
planet: {
// Hetzner BareMetal (Helsinki, Finland) unmetered
url: 'https://cdn.organicmaps.app/',
dataVersions: kUnlimited,
},
beta: {
// Alias of the planet above that is proxied via CF and with enabled /maps/ *.mwm caching,
// to speed-up downloads for beta testers.
url: 'https://cdn-beta.organicmaps.app/',
// Can have any non-publicly available maps data version.
dataVersions: [],
},
fi1: {
// Hetzner Cloud (Helsinki, Finland), 20TB/mo
url: 'https://cdn-fi1.organicmaps.app/',
dataVersions: 2,
},
de1: {
// Hetzner Cloud (Falkenstein, Germany), 20TB/mo
url: 'https://cdn-eu2.organicmaps.app/',
dataVersions: 2,
},
de2: {
// Hetzner Cloud (Falkenstein, Germany), 20TB/mo
url: 'https://cdn-de2.organicmaps.app/',
dataVersions: 3,
},
de3: {
// Hetzner Cloud (Nuremberg, Germany), 20TB/mo
url: 'https://cdn-de3.organicmaps.app/',
dataVersions: 4,
},
us_east1: {
// Hetzner Cloud (Ashburn, US East), 20TB/mo
url: 'https://cdn-us-east1.organicmaps.app/',
dataVersions: 3,
},
us_west1: {
// Hetzner Cloud (Hillsdate, US West), 20TB/mo
url: 'https://cdn-us-west1.organicmaps.app/',
dataVersions: 2,
},
vi1: {
// Vietnam, unlimited 1Gbps bandwidth
url: 'https://cdn-vi1.organicmaps.app/',
dataVersions: 5,
},
};
// Exported for tests.
export const DONATE_URL = 'https://organicmaps.app/donate/';
export async function getServersList(request: Request) {
// Private for map files.
let servers;
// Starting from 2021-09, our clients have 'X-OM-DataVersion' header with the value
// of their current maps data version, for example, "211022" (October 22, 2021).
// It is lowercased by Cloudflare.
const dataVersion = parseDataVersion(request.headers.get('x-om-dataversion'));
const abusedVersions = ['1.8.6-4-ios', '1.8.7-1-ios', '1.8.8-1-ios'];
if (dataVersion === null) {
// Older clients download from the archive.
servers = [SERVER.backblaze];
} else if (dataVersion == 240702 && abusedVersions.includes(request.headers.get('x-om-appversion') || 'unknown')) {
// Redirect https://apps.apple.com/us/app/mapxplorer-navigation-radar/id6463052823
// who abuses our servers to a slow download "trap" node.
return new Response('["https://cdn-fi2.organicmaps.app/"]', {
headers: { 'Content-Type': 'application/json' },
});
} else {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore workarounds error TS2339: Property 'continent' does not exist on type 'IncomingRequestCfProperties<unknown>'.
switch (request.cf?.continent) {
// See https://developers.cloudflare.com/firewall/cf-firewall-language/fields for a list of all continents.
case 'NA': // North America
case 'SA': // South America
servers = [SERVER.us_east1, SERVER.us_west1, SERVER.uk1, SERVER.nl1, SERVER.planet].filter((server) =>
DATA_VERSIONS.slice(-server.dataVersions).includes(dataVersion),
);
break;
case 'OC': // Oceania
servers = [SERVER.vi1, SERVER.us_east1, SERVER.us_west1, SERVER.planet].filter((server) =>
DATA_VERSIONS.slice(-server.dataVersions).includes(dataVersion),
);
break;
case 'AS': // Asia
servers = [SERVER.vi1, SERVER.uk1, SERVER.nl1, SERVER.planet].filter((server) =>
DATA_VERSIONS.slice(-server.dataVersions).includes(dataVersion),
);
break;
default:
// Every other continent + Tor networks.
servers = [SERVER.planet, SERVER.uk1, SERVER.nl1, SERVER.fi1, SERVER.de1, SERVER.de2, SERVER.de3].filter(
(server) => DATA_VERSIONS.slice(-server.dataVersions).includes(dataVersion),
);
// Only fallback to the archive in the US if nothing was found closer.
if (servers.length == 0 && DATA_VERSIONS.slice(-SERVER.backblaze.dataVersions).includes(dataVersion)) {
servers = [SERVER.backblaze];
}
}
}
// Fallback to the planet with freshly generated/beta data.
if (servers.length == 0) {
servers = [SERVER.beta, SERVER.planet];
}
servers = servers.map((server) => server.url);
// Header "X-OM-AppVersion: 2022.09.22-3-Google" (lowercased by CF) is supported from August 23, 2022.
const appVersion = parseAppVersion(request.headers.get('x-om-appversion'));
if (!appVersion) {
// Old format for <220823
return new Response(JSON.stringify(servers), {
headers: { 'Content-Type': 'application/json' },
});
}
// New format for >=220823
const response: {
servers: string[];
settings?: {
DonateUrl?: string;
NY?: string;
};
productsConfig?: ProductsConfig;
} = {
servers: servers,
};
// Disable donates for reviewers for all app versions AFTER this one.
const lastApprovedAndReleasedGoogleAppVersionCode = 250217;
const lastApprovedAndReleasediOSAppVersionCode = 250303;
let donatesEnabled = true;
if (
appVersion.flavor === 'google' &&
((typeof request.cf?.asOrganization === 'string' && request.cf?.asOrganization.toLowerCase().includes('google')) ||
appVersion.code > lastApprovedAndReleasedGoogleAppVersionCode)
) {
donatesEnabled = false;
} else if (appVersion.build === undefined) {
// Disable donates for older iOS versions without donates menu support.
donatesEnabled = false;
} else if (
appVersion.flavor === 'ios' &&
((typeof request.cf?.asOrganization === 'string' && request.cf?.asOrganization.toLowerCase().includes('apple')) ||
appVersion.code > lastApprovedAndReleasediOSAppVersionCode)
) {
donatesEnabled = false;
}
if (donatesEnabled) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
response.settings = {
DonateUrl: DONATE_URL,
NY: 'false', // Must be `string` instead of `bool`, otherwise clients will crash
};
if (appVersion.code >= 241022) {
const locale = request.headers.get('accept-language');
response.productsConfig = getProducts(locale);
}
}
return new Response(JSON.stringify(response), {
headers: { 'Content-Type': 'application/json' },
});
}

View file

@ -1,52 +0,0 @@
export {};
export function parseDataVersion(strDataVersion: string | null): number | null {
if (!strDataVersion) {
return null;
}
const dataVersion = parseInt(strDataVersion);
if (Number.isNaN(dataVersion) || dataVersion < 210000 || dataVersion > 500000) {
return null;
}
return dataVersion;
}
const VERSION_RE = new RegExp('(\\d{4}).(\\d{1,2}).(\\d{1,2})-(\\d{1,9})(?:-([^-]+))?');
export function parseAppVersion(
versionName: string | null,
): { code: number; build: number | undefined; flavor: string | undefined } | null {
if (!versionName) {
return null;
}
const m = versionName.match(VERSION_RE);
if (m === null || m.length < 6) {
return null;
}
const yyyy = parseInt(m[1]);
const mm = parseInt(m[2]);
const dd = parseInt(m[3]);
const build = Number.isNaN(parseInt(m[4])) ? undefined : parseInt(m[4]);
const flavor = (m[5] !== undefined && m[5].toLowerCase()) || undefined;
if (
Number.isNaN(yyyy) ||
yyyy > 2099 ||
yyyy < 2022 ||
Number.isNaN(mm) ||
mm > 12 ||
mm < 1 ||
Number.isNaN(dd) ||
dd > 31 ||
dd < 1
) {
return null;
}
const code = parseInt(String(yyyy % 100) + String(mm).padStart(2, '0') + String(dd).padStart(2, '0'));
return {
code: code,
flavor: flavor,
build: build,
};
}

64
src/versions.ts Normal file
View file

@ -0,0 +1,64 @@
export {};
export function parseDataVersion(strDataVersion: string | null): number | null {
if (!strDataVersion) {
return null;
}
const dataVersion = parseInt(strDataVersion);
if (Number.isNaN(dataVersion) || dataVersion < 210000 || dataVersion > 500000) {
return null;
}
return dataVersion;
}
// 2022.11.20 for iOS versions released before November 21 (without donate menu)
// 2022.11.24-4-ios for newer iOS versions (with donate menu)
// 2022.12.24-10-Google for Android
// 2022.12.24-3-3f4ca43-Linux or 2022.12.24-3-3f4ca43-dirty-Linux for Linux
// 2022.12.24-3-3f4ca43-Darwin for Mac
const VERSION_RE =
/(?<year>\d{4})\.(?<month>\d{1,2})\.(?<day>\d{1,2})(?:$|-(?<build>[0-9]+)(?:-[0-9a-f]+)?(?:-dirty)?-(?<flavor>[A-Za-z3264]+))/;
// Returns code like 221224 for both platforms, build and flavor for Android and newer iOS versions.
export function parseAppVersion(
versionName: string | null,
): { code: number; build?: number; flavor?: string | undefined } | null {
if (!versionName) {
return null;
}
const m = versionName.match(VERSION_RE);
if (m === null || m.length < 4 || !m.groups) {
return null;
}
const yyyy = parseInt(m.groups.year);
if (Number.isNaN(yyyy) || yyyy > 2099 || yyyy < 2022) {
return null;
}
const mm = parseInt(m.groups.month);
if (Number.isNaN(mm) || mm > 12 || mm < 1) {
return null;
}
const dd = parseInt(m.groups.day);
if (Number.isNaN(dd) || dd > 31 || dd < 1) {
return null;
}
const code = parseInt(String(yyyy % 100) + String(mm).padStart(2, '0') + String(dd).padStart(2, '0'));
// Older iOS versions without donate button.
if (!m.groups.build) {
return { code: code };
}
const buildNumber = parseInt(m.groups.build);
const build = Number.isNaN(buildNumber) ? 0 : buildNumber;
// 'ios' for iOS devices.
const flavor = (m.groups.flavor !== undefined && m.groups.flavor.toLowerCase()) || undefined;
return {
code: code,
flavor: flavor,
build: build,
};
}

View file

@ -1,35 +0,0 @@
import { describe, expect, test } from '@jest/globals';
import { handleRequest, SERVER } from '../src/index';
const URL = 'https://worker/servers';
const LAST_DATA_VERSION = SERVER.planet.dataVersions[SERVER.planet.dataVersions.length - 1];
describe('old versions', () => {
test('no dataVersion', async () => {
const req = new Request(URL);
const result = await handleRequest(req);
expect(result.status).toBe(200);
expect(JSON.parse(await result.text())).toEqual([SERVER.backblaze.url]);
});
test('has dataVersion', async () => {
const server = SERVER.fi1;
let req = new Request(URL, {
headers: {
'X-OM-DataVersion': String(server.dataVersions[0]),
},
});
const result = await handleRequest(req);
expect(result.status).toBe(200);
expect(JSON.parse(await result.text())).toContain(server.url);
});
test('default routing to planet', async () => {
let req = new Request(URL, {
headers: {
'X-OM-DataVersion': '210000', // this version doesn't exist on servers
},
});
const result = await handleRequest(req);
expect(result.status).toBe(200);
expect(JSON.parse(await result.text())).toEqual([SERVER.planet.url]);
});
});

14
test/products.test.ts Normal file
View file

@ -0,0 +1,14 @@
import { describe, expect, test } from '@jest/globals';
import { getProducts } from '../src/products';
describe('getProducts', () => {
test('fr-FR', () => {
const fr_FR = getProducts('fr-FR');
expect(fr_FR).toBeDefined();
if (!fr_FR) return;
expect(fr_FR.placePagePrompt).toBe(
"L'application Organic Maps est gratuite pour tout le monde grâce à vos dons. Pas de publicité. Pas de trackers. Open-source.",
);
expect(fr_FR.products[fr_FR.products.length - 1].title).toEqual('Autre');
});
});

109
test/servers.test.ts Normal file
View file

@ -0,0 +1,109 @@
import { describe, expect, test } from '@jest/globals';
import { getServersList, SERVER, DATA_VERSIONS, DONATE_URL } from '../src/servers';
const URL = 'https://worker/servers';
const LAST_DATA_VERSION = DATA_VERSIONS[DATA_VERSIONS.length - 1];
// Note: CF lowercases all headers.
describe('X-OM-DataVersion', () => {
test('no X-OM-DataVersion', async () => {
const req = new Request(URL);
const result = await getServersList(req);
expect(result.status).toBe(200);
expect(JSON.parse(await result.text())).toEqual([SERVER.backblaze.url]);
});
test('has X-OM-DataVersion', async () => {
const server = SERVER.fi1;
let req = new Request(URL, {
headers: {
'X-OM-DataVersion': String(LAST_DATA_VERSION),
},
});
const result = await getServersList(req);
expect(result.status).toBe(200);
expect(JSON.parse(await result.text())).toContain(server.url);
});
test('Default routing to planet', async () => {
let req = new Request(URL, {
headers: {
'X-OM-DataVersion': '210000', // this version doesn't exist on servers
},
});
const result = await getServersList(req);
expect(result.status).toBe(200);
expect(JSON.parse(await result.text())).toEqual([SERVER.beta.url, SERVER.planet.url]);
});
});
describe('X-OM-AppVersion DonateUrl', () => {
test('Old versions without X-OM-AppVersion and old metaserver JSON format', async () => {
const req = new Request(URL);
const response = await getServersList(req);
expect(response.status).toBe(200);
expect(JSON.parse(await response.text())).toEqual([SERVER.backblaze.url]);
});
const server = SERVER.fi1;
test('Newer metaserver JSON format with donates support', async () => {
let req = new Request(URL, {
headers: {
'X-OM-AppVersion': '2022.08.23-1-Google',
'X-OM-DataVersion': String(LAST_DATA_VERSION),
},
});
const response = await getServersList(req);
expect(response.status).toBe(200);
const result = JSON.parse(await response.text());
expect(result.servers).toBeDefined();
expect(result.servers.length).toBeGreaterThan(0);
expect(result.servers).toContain(server.url);
expect(result.settings).toBeDefined();
expect(result.settings.DonateUrl).toBeDefined();
expect(result.settings.DonateUrl).toEqual(DONATE_URL);
});
test('Newer metaserver JSON format with donates support', async () => {
let req = new Request(URL, {
headers: {
'X-OM-AppVersion': '2022.08.23-1-Google',
'X-OM-DataVersion': String(LAST_DATA_VERSION),
},
//@ts-ignore
cf: { country: 'RU' },
});
const response = await getServersList(req);
expect(response.status).toBe(200);
const result = JSON.parse(await response.text());
expect(result.settings.DonateUrl).toBeDefined();
});
test('Older iOS versions with X-OM-AppVersion but without donates', async () => {
let req = new Request(URL, {
headers: {
'X-OM-AppVersion': '2022.11.20',
'X-OM-DataVersion': String(LAST_DATA_VERSION),
},
});
const response = await getServersList(req);
expect(response.status).toBe(200);
const result = JSON.parse(await response.text());
expect(result.settings).not.toBeDefined();
});
test('Newer iOS versions with donate menu support', async () => {
let req = new Request(URL, {
headers: {
'X-OM-AppVersion': '2022.11.20-4-ios',
'X-OM-DataVersion': String(LAST_DATA_VERSION),
},
});
const response = await getServersList(req);
expect(response.status).toBe(200);
const result = JSON.parse(await response.text());
expect(result.settings.DonateUrl).toBeDefined();
expect(result.settings.DonateUrl).toEqual(DONATE_URL);
});
});

View file

@ -1,36 +0,0 @@
import { describe, expect, test } from '@jest/globals';
import { parseDataVersion, parseAppVersion } from '../src/utils';
describe('parseDataVersion', () => {
test('220801', () => expect(parseDataVersion('220801')).toEqual(220801));
test('210000', () => expect(parseDataVersion('210000')).toEqual(210000));
test('500000', () => expect(parseDataVersion('500000')).toEqual(500000));
test('200000', () => expect(parseDataVersion('200000')).toEqual(null));
test('500001', () => expect(parseDataVersion('500001')).toEqual(null));
test('garbage', () => expect(parseDataVersion('garbage')).toEqual(null));
test('', () => expect(parseDataVersion('')).toEqual(null));
test('', () => expect(parseDataVersion(null)).toEqual(null));
});
describe('parseAppVersion', () => {
test('2022.08.01-1', () => expect(parseAppVersion('2022.08.01-1')).toEqual({ code: 220801, build: 1 }));
test('2022.08.01-1-Google', () =>
expect(parseAppVersion('2022.08.01-1-Google')).toEqual({ code: 220801, build: 1, flavor: 'google' }));
// -debug is ignored
test('2022.08.01-1-Google-debug', () =>
expect(parseAppVersion('2022.08.01-1-Google-debug')).toEqual({ code: 220801, build: 1, flavor: 'google' }));
test('2022.1.1-0', () => expect(parseAppVersion('2022.1.2-0')).toEqual({ code: 220102, build: 0 }));
test('2099.12.31-999999999', () =>
expect(parseAppVersion('2099.12.31-999999999')).toEqual({ code: 991231, build: 999999999 }));
test('2021.01.31-1', () => expect(parseAppVersion('2021.01.31-1')).toEqual(null));
test('2100.01.31-1', () => expect(parseAppVersion('2100.01.31-1')).toEqual(null));
test('2022.00.31-1', () => expect(parseAppVersion('2022.00.31-1')).toEqual(null));
test('2022.13.31-1', () => expect(parseAppVersion('2022.13.31-1')).toEqual(null));
test('2022.01.00-1', () => expect(parseAppVersion('2022.01.00-1')).toEqual(null));
test('2022.01.32-1', () => expect(parseAppVersion('2022.01.32-1')).toEqual(null));
test('202.01.31-1', () => expect(parseAppVersion('2100.01.31-1')).toEqual(null));
test('2022..31-11', () => expect(parseAppVersion('2100.01.31-1')).toEqual(null));
test('garbage', () => expect(parseAppVersion('garbage')).toEqual(null));
test('', () => expect(parseAppVersion('')).toEqual(null));
test('null', () => expect(parseAppVersion('null')).toEqual(null));
});

68
test/versions.test.ts Normal file
View file

@ -0,0 +1,68 @@
import { describe, expect, test } from '@jest/globals';
import { parseDataVersion, parseAppVersion } from '../src/versions';
describe('parseDataVersion', () => {
const tests: { [key: string]: number | null } = {
'220801': 220801,
'210000': 210000,
'500000': 500000,
'200000': null,
'500001': null,
garbage: null,
null: null,
'': null,
};
for (const input in tests) {
test(input, () => expect(parseDataVersion(input)).toEqual(tests[input]));
}
test('', () => expect(parseDataVersion(null)).toEqual(null));
});
describe('parseAppVersion', () => {
const tests: { [key: string]: object | null } = {
// Older iOS releases without donate menu
'2022.08.01': { code: 220801 },
// Newer iOS releases with donate menu
'2022.11.25-5-ios': { code: 221125, build: 5, flavor: 'ios' },
// There were no such versions in production.
'2022.08.01-1': null,
'2022.08.01-1-Google': { code: 220801, build: 1, flavor: 'google' },
// -debug is ignored
'2022.08.01-1-Google-debug': { code: 220801, build: 1, flavor: 'google' },
// TODO: Fix regexp. Not it should not happen in production.
//'2022.08.01-1-fd-debug': { code: 220801, build: 1, flavor: 'fd' },
'2022.1.1-0': null,
'2099.12.31-999999999': null,
'2023.03.22-1-4fac32de-Linux': { code: 230322, build: 1, flavor: 'linux' },
'2023.03.22-1-4fac32de-dirty-Linux': { code: 230322, build: 1, flavor: 'linux' },
'2023.03.22-1-4fac32de-Darwin': { code: 230322, build: 1, flavor: 'darwin' },
'2023.03.22-1-4fac32de-dirty-Darwin': { code: 230322, build: 1, flavor: 'darwin' },
'2021.01.31-1': null,
'2100.01.31-1': null,
'2022.00.31-1': null,
'2022.13.31-1': null,
'2022.01.00-1': null,
'2022.01.32-1': null,
'22.01.31-1': null,
'22.01.31': null,
'22.01.31-3-flavor': null,
'202.01.31-1': null,
'202.01.312-1': null,
'202.01.312': null,
'202.01.31-aa-flavor': null,
'202.01.31-5a-flavor': null,
'202.01.31-a5-flavor': null,
'2022..31-11': null,
'2022..31-11-flavor': null,
'.11.31-11': null,
'.11.31-11-flavor': null,
'.11..31-11-flavor': null,
garbage: null,
'': null,
null: null,
};
for (const input in tests) {
test(input, () => expect(parseAppVersion(input)).toEqual(tests[input]));
}
test('', () => expect(parseAppVersion(null)).toEqual(null));
});

View file

@ -5,12 +5,13 @@
"module": "ES6",
"moduleResolution": "node",
"target": "ESNext",
"lib": ["ESNext", "webworker"],
"lib": ["ESNext"],
"alwaysStrict": true,
"strict": true,
"preserveConstEnums": true,
"sourceMap": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"types": ["@cloudflare/workers-types"]
},
"include": ["src/"],

View file

@ -1,14 +1,15 @@
# Default worker is for dev only.
# See omaps and organicmaps environments below for production.
name = 'meta-dev'
type = 'javascript'
compatibility_date = '2021-11-11'
main = 'src/index.ts'
compatibility_date = '2022-11-21'
# Organic Maps CF Account ID.
account_id = '462f578f0939f041e2c24ec99adce458'
workers_dev = true
# Whether Wrangler should send usage metrics to Cloudflare for this project.
send_metrics = false
[build]
upload.format = 'service-worker'
command = 'npm ci --prefer-offline --no-audit && npm run build'
[vars]
@ -17,8 +18,6 @@ DEBUG = true
[env.prod]
name = 'meta'
workers_dev = false
# omaps.app CF zone ID.
zone_id = '3fce06554abc3899504e11d928be0ee7'
# See the full list of handled paths in the code.
route = 'meta.omaps.app/*'