Moved servers-endpoint reply into servers.ts
Signed-off-by: Alexander Borsuk <me@alex.bio>
This commit is contained in:
parent
c5f24e9912
commit
d41bc4228d
3 changed files with 170 additions and 169 deletions
163
src/index.ts
163
src/index.ts
|
@ -1,61 +1,6 @@
|
|||
export {};
|
||||
|
||||
import { parseDataVersion, parseAppVersion } from './versions';
|
||||
|
||||
// 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, 221119, 221216, 230121, 230210, 230227,
|
||||
],
|
||||
},
|
||||
uk1: {
|
||||
// Mythic Beasts VPS (London, UK) 200TB/mo.
|
||||
url: 'https://cdn-uk1.organicmaps.app/',
|
||||
dataVersions: [230121, 230210, 230227],
|
||||
},
|
||||
nl1: {
|
||||
// // Mythic Beasts VPS (Amsterdam, NL) 200TB/mo.
|
||||
url: 'https://cdn-nl1.organicmaps.app/',
|
||||
dataVersions: [230121, 230210, 230227],
|
||||
},
|
||||
planet: {
|
||||
// Hetzner BareMetal (Falkenstein, Germany) unmetered
|
||||
url: 'https://cdn.organicmaps.app/',
|
||||
dataVersions: [
|
||||
220103, 220204, 220314, 220415, 220515, 220613, 220718, 220816, 220912, 221029, 221119, 221216, 230121, 230210,
|
||||
230227,
|
||||
],
|
||||
},
|
||||
fi1: {
|
||||
// Hetzner Cloud (Helsinki, Finland), 20TB/mo
|
||||
url: 'https://cdn-fi1.organicmaps.app/',
|
||||
dataVersions: [230210, 230227],
|
||||
},
|
||||
de1: {
|
||||
// Hetzner Cloud (Falkenstein, Germany), 20TB/mo
|
||||
url: 'https://cdn-eu2.organicmaps.app/',
|
||||
dataVersions: [230210, 230227],
|
||||
},
|
||||
de2: {
|
||||
// Hetzner Cloud (Falkenstein, Germany), 20TB/mo
|
||||
url: 'https://cdn-de2.organicmaps.app/',
|
||||
dataVersions: [230121, 230210, 230227],
|
||||
},
|
||||
us2: {
|
||||
// Hetzner Cloud (Asburn, US East), 20TB/mo
|
||||
url: 'https://cdn-us2.organicmaps.app/',
|
||||
dataVersions: [230210, 230227],
|
||||
},
|
||||
};
|
||||
|
||||
// Exported for tests.
|
||||
export const DONATE_URL = 'https://organicmaps.app/donate/';
|
||||
export const DONATE_URL_RU = 'https://organicmaps.app/ru/donate/';
|
||||
import { getServersList } from './servers';
|
||||
|
||||
// Main entry point.
|
||||
addEventListener('fetch', (event) => {
|
||||
|
@ -68,110 +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 {
|
||||
// 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
|
||||
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, SERVER.de2].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;
|
||||
NY?: string;
|
||||
};
|
||||
} = {
|
||||
servers: servers,
|
||||
};
|
||||
|
||||
// Disable donates for reviewers for all app versions AFTER this one.
|
||||
const lastApprovedAndReleasedGoogleAppVersionCode = 230305;
|
||||
const lastApprovedAndReleasediOSAppVersionCode = 230302;
|
||||
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) {
|
||||
// To count enabled donations.
|
||||
console.log('Donates enabled');
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore workarounds error TS2339: Property 'country' does not exist on type 'IncomingRequestCfProperties<unknown>'.
|
||||
if (request.cf?.country == 'RU') {
|
||||
response.settings = {
|
||||
DonateUrl: DONATE_URL_RU,
|
||||
NY: 'false', // Must be `string` instead of `bool`, otherwise clients will crash
|
||||
};
|
||||
} else {
|
||||
response.settings = {
|
||||
DonateUrl: DONATE_URL,
|
||||
NY: 'false', // Must be `string` instead of `bool`, otherwise clients will crash
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return new Response(JSON.stringify(response), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
case '/servers':
|
||||
return getServersList(request);
|
||||
}
|
||||
return new Response('', { status: 404 });
|
||||
}
|
||||
|
|
158
src/servers.ts
Normal file
158
src/servers.ts
Normal file
|
@ -0,0 +1,158 @@
|
|||
import { parseDataVersion, parseAppVersion } from './versions';
|
||||
|
||||
// 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, 221119, 221216, 230121, 230210, 230227,
|
||||
],
|
||||
},
|
||||
uk1: {
|
||||
// Mythic Beasts VPS (London, UK) 200TB/mo.
|
||||
url: 'https://cdn-uk1.organicmaps.app/',
|
||||
dataVersions: [230121, 230210, 230227],
|
||||
},
|
||||
nl1: {
|
||||
// // Mythic Beasts VPS (Amsterdam, NL) 200TB/mo.
|
||||
url: 'https://cdn-nl1.organicmaps.app/',
|
||||
dataVersions: [230121, 230210, 230227],
|
||||
},
|
||||
planet: {
|
||||
// Hetzner BareMetal (Falkenstein, Germany) unmetered
|
||||
url: 'https://cdn.organicmaps.app/',
|
||||
dataVersions: [
|
||||
220103, 220204, 220314, 220415, 220515, 220613, 220718, 220816, 220912, 221029, 221119, 221216, 230121, 230210,
|
||||
230227,
|
||||
],
|
||||
},
|
||||
fi1: {
|
||||
// Hetzner Cloud (Helsinki, Finland), 20TB/mo
|
||||
url: 'https://cdn-fi1.organicmaps.app/',
|
||||
dataVersions: [230210, 230227],
|
||||
},
|
||||
de1: {
|
||||
// Hetzner Cloud (Falkenstein, Germany), 20TB/mo
|
||||
url: 'https://cdn-eu2.organicmaps.app/',
|
||||
dataVersions: [230210, 230227],
|
||||
},
|
||||
de2: {
|
||||
// Hetzner Cloud (Falkenstein, Germany), 20TB/mo
|
||||
url: 'https://cdn-de2.organicmaps.app/',
|
||||
dataVersions: [230121, 230210, 230227],
|
||||
},
|
||||
us2: {
|
||||
// Hetzner Cloud (Asburn, US East), 20TB/mo
|
||||
url: 'https://cdn-us2.organicmaps.app/',
|
||||
dataVersions: [230210, 230227],
|
||||
},
|
||||
};
|
||||
|
||||
// Exported for tests.
|
||||
export const DONATE_URL = 'https://organicmaps.app/donate/';
|
||||
export const DONATE_URL_RU = 'https://organicmaps.app/ru/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'));
|
||||
if (dataVersion === null) {
|
||||
// Older clients download from the archive.
|
||||
servers = [SERVER.backblaze];
|
||||
} 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
|
||||
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, SERVER.de2].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;
|
||||
NY?: string;
|
||||
};
|
||||
} = {
|
||||
servers: servers,
|
||||
};
|
||||
|
||||
// Disable donates for reviewers for all app versions AFTER this one.
|
||||
const lastApprovedAndReleasedGoogleAppVersionCode = 230305;
|
||||
const lastApprovedAndReleasediOSAppVersionCode = 230302;
|
||||
let donatesEnabled = true;
|
||||
if (
|
||||
appVersion.flavor === 'google' &&
|
||||
((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' && (request.cf?.asOrganization || '').toLowerCase().includes('apple')) ||
|
||||
appVersion.code > lastApprovedAndReleasediOSAppVersionCode
|
||||
) {
|
||||
donatesEnabled = false;
|
||||
}
|
||||
|
||||
if (donatesEnabled) {
|
||||
// To count enabled donations.
|
||||
console.log('Donates enabled');
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore workarounds error TS2339: Property 'country' does not exist on type 'IncomingRequestCfProperties<unknown>'.
|
||||
if (request.cf?.country == 'RU') {
|
||||
response.settings = {
|
||||
DonateUrl: DONATE_URL_RU,
|
||||
NY: 'false', // Must be `string` instead of `bool`, otherwise clients will crash
|
||||
};
|
||||
} else {
|
||||
response.settings = {
|
||||
DonateUrl: DONATE_URL,
|
||||
NY: 'false', // Must be `string` instead of `bool`, otherwise clients will crash
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return new Response(JSON.stringify(response), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import { describe, expect, test } from '@jest/globals';
|
||||
import { handleRequest, SERVER, DONATE_URL, DONATE_URL_RU } from '../src/index';
|
||||
import { getServersList, SERVER, DONATE_URL, DONATE_URL_RU } from '../src/servers';
|
||||
|
||||
const URL = 'https://worker/servers';
|
||||
const LAST_DATA_VERSION = SERVER.planet.dataVersions[SERVER.planet.dataVersions.length - 1];
|
||||
|
@ -8,7 +8,7 @@ const LAST_DATA_VERSION = SERVER.planet.dataVersions[SERVER.planet.dataVersions.
|
|||
describe('X-OM-DataVersion', () => {
|
||||
test('no X-OM-DataVersion', async () => {
|
||||
const req = new Request(URL);
|
||||
const result = await handleRequest(req);
|
||||
const result = await getServersList(req);
|
||||
expect(result.status).toBe(200);
|
||||
expect(JSON.parse(await result.text())).toEqual([SERVER.backblaze.url]);
|
||||
});
|
||||
|
@ -20,7 +20,7 @@ describe('X-OM-DataVersion', () => {
|
|||
'X-OM-DataVersion': String(server.dataVersions[0]),
|
||||
},
|
||||
});
|
||||
const result = await handleRequest(req);
|
||||
const result = await getServersList(req);
|
||||
expect(result.status).toBe(200);
|
||||
expect(JSON.parse(await result.text())).toContain(server.url);
|
||||
});
|
||||
|
@ -31,7 +31,7 @@ describe('X-OM-DataVersion', () => {
|
|||
'X-OM-DataVersion': '210000', // this version doesn't exist on servers
|
||||
},
|
||||
});
|
||||
const result = await handleRequest(req);
|
||||
const result = await getServersList(req);
|
||||
expect(result.status).toBe(200);
|
||||
expect(JSON.parse(await result.text())).toEqual([SERVER.planet.url]);
|
||||
});
|
||||
|
@ -40,7 +40,7 @@ describe('X-OM-DataVersion', () => {
|
|||
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 handleRequest(req);
|
||||
const response = await getServersList(req);
|
||||
expect(response.status).toBe(200);
|
||||
expect(JSON.parse(await response.text())).toEqual([SERVER.backblaze.url]);
|
||||
});
|
||||
|
@ -54,7 +54,7 @@ describe('X-OM-AppVersion DonateUrl', () => {
|
|||
'X-OM-DataVersion': String(server.dataVersions[0]),
|
||||
},
|
||||
});
|
||||
const response = await handleRequest(req);
|
||||
const response = await getServersList(req);
|
||||
expect(response.status).toBe(200);
|
||||
const result = JSON.parse(await response.text());
|
||||
expect(result.servers).toBeDefined();
|
||||
|
@ -74,7 +74,7 @@ describe('X-OM-AppVersion DonateUrl', () => {
|
|||
//@ts-ignore
|
||||
cf: { country: 'RU' },
|
||||
});
|
||||
const response = await handleRequest(req);
|
||||
const response = await getServersList(req);
|
||||
expect(response.status).toBe(200);
|
||||
const result = JSON.parse(await response.text());
|
||||
expect(result.settings.DonateUrl).toBeDefined();
|
||||
|
@ -88,7 +88,7 @@ describe('X-OM-AppVersion DonateUrl', () => {
|
|||
'X-OM-DataVersion': String(server.dataVersions[0]),
|
||||
},
|
||||
});
|
||||
const response = await handleRequest(req);
|
||||
const response = await getServersList(req);
|
||||
expect(response.status).toBe(200);
|
||||
const result = JSON.parse(await response.text());
|
||||
expect(result.settings).not.toBeDefined();
|
||||
|
@ -101,7 +101,7 @@ describe('X-OM-AppVersion DonateUrl', () => {
|
|||
'X-OM-DataVersion': String(server.dataVersions[0]),
|
||||
},
|
||||
});
|
||||
const response = await handleRequest(req);
|
||||
const response = await getServersList(req);
|
||||
expect(response.status).toBe(200);
|
||||
const result = JSON.parse(await response.text());
|
||||
expect(result.settings.DonateUrl).toBeDefined();
|
Loading…
Add table
Reference in a new issue