Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#371 update modified time #372

Merged
merged 12 commits into from
Jul 11, 2024
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ The list of trackers and companies is generated from the database [whotracks.me]
**VPN Services**:

- [source/vpn_services.json](https://raw.githubusercontent.com/AdguardTeam/companiesdb/main/dist/vpn_services.json) - contains a list of "Services" that can be added to exclusions in AdGuard VPN apps. This file is composed manually and not
built from other sources.
- [dist/vpn_services.json](https://raw.githubusercontent.com/AdguardTeam/companiesdb/main/dist/vpn_services.json) - just a copy of [source/vpn_services.json](https://raw.githubusercontent.com/AdguardTeam/companiesdb/main/dist/vpn_services.json).
built from other sources. New services should be added in alphabetical order.
- [dist/vpn_services.json](https://raw.githubusercontent.com/AdguardTeam/companiesdb/main/dist/vpn_services.json) - just a copy of [source/vpn_services.json](https://raw.githubusercontent.com/AdguardTeam/companiesdb/main/dist/vpn_services.json) with automatically added update time if the service has been added or modified.

## How to add new or rewrite whotracks.me data

Expand Down
130 changes: 120 additions & 10 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,20 +63,30 @@ const trackersJSONSchema = zod.object({
type TrackersJSON = zod.infer<typeof trackersJSONSchema>;

/**
* Schema parser for the VPN services JSON file.
* Schema parser for the VPN service.
*/
const vpnServicesJSONSchema = zod.object({
const vpnServiceSchema = zod.object({
service_id: zod.string(),
service_name: zod.string(),
categories: zod.array(zod.string()),
domains: zod.array(zod.string()),
icon_domain: zod.string(),
modified_time: zod.string(),
}).strict().array();
modified_time: zod.string().optional(),
}).strict();

/**
* Schema type of the VPN service.
*/
type VpnService = zod.infer<typeof vpnServiceSchema>;

/**
* Schema parser for the VPN services JSON file.
*/
const vpnServicesJSONSchema = vpnServiceSchema.array();

/**
* Schema type of the VPN services JSON file.
*/
* Schema type of the VPN services JSON file.
*/
type VpnServicesJSON = zod.infer<typeof vpnServicesJSONSchema>;

/**
Expand Down Expand Up @@ -127,6 +137,18 @@ function readTrackersJSON(source: string): TrackersJSON {
return trackersJSONSchema.parse(data);
}

/**
* Reads and parse the vpn services JSON.
*
* @param source Source JSON path.
* @returns parsed services JSON data.
*/
function readVpnServicesJSON(source: string): VpnServicesJSON {
const data = readJSON(source);

return vpnServicesJSONSchema.parse(data);
}

/**
* Sorts the records alphabetically by key or value.
*
Expand Down Expand Up @@ -270,6 +292,87 @@ function buildTrackersDomains(

return sortRecordsAlphabetically(merged, false);
}
/**
* Formats the current date and time in the format 'YYYY-MM-DD HH:MM'.
* @returns The current date and time in the format 'YYYY-MM-DD HH:MM'.
*/
function getCurrentDateTime(): string {
const now = new Date();

const day = String(now.getDate()).padStart(2, '0');
const month = String(now.getMonth() + 1).padStart(2, '0');
const year = now.getFullYear();
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');

return `${year}-${month}-${day} ${hours}:${minutes}`;
}

/**
* Updates the modified_time field in the vpn services JSON file.
* If a service is not present in the upToDateMap, it will be added with the current date.
* If service was changed, the modified_time field will be updated to the current date.
* @param vpnInput The updated vpn services JSON file.
* @param vpnOutput The previously recorded vpn services JSON file.
* @returns The updated vpn services JSON file with the modified_time field updated, if necessary.
*/

function updateVpnServicesJSONDate(
vpnInput: VpnServicesJSON,
vpnOutput: VpnServicesJSON,
): VpnServicesJSON {
const timeUpdated = getCurrentDateTime();

// Create maps of services based on their id
const upToDateMap: { [key: string]: VpnService } = vpnOutput.reduce((map, obj) => ({
...map,
[obj.service_id]: obj,
}), {});

const updatedMap: { [key: string]: VpnService } = vpnInput.reduce((map, obj) => ({
...map,
[obj.service_id]: obj,
}), {});

// Iterate over keys in updatedMap
Object.keys(updatedMap).forEach((key) => {
// Check if the key exists in upToDateMap
if (!upToDateMap[key]) {
upToDateMap[key] = {
...updatedMap[key],
modified_time: timeUpdated,
};
} else {
let shouldUpdateDate = false;
// Check values
Object.keys(updatedMap[key]).forEach((k) => {
// if the value is a string, compare the strings
if (typeof updatedMap[key][k as keyof VpnService] === 'string') {
if (
updatedMap[key][k as keyof VpnService]
!== upToDateMap[key][k as keyof VpnService]) {
shouldUpdateDate = true;
}
} else if (Array.isArray(updatedMap[key][k as keyof VpnService])) {
// if the value is an array, compare the arrays
const updatedArray = updatedMap[key][k as keyof VpnService] as any[];
const upToDateArray = upToDateMap[key][k as keyof VpnService] as any[];
if (!updatedArray.every((element, index) => element === upToDateArray[index])) {
shouldUpdateDate = true;
}
}
});
if (shouldUpdateDate) {
upToDateMap[key] = {
...updatedMap[key],
modified_time: timeUpdated,
};
}
}
});

return Object.values(sortRecordsAlphabetically(upToDateMap));
}

/**
* Builds the trackers CSV file in the following form:
Expand Down Expand Up @@ -369,11 +472,18 @@ try {

fs.writeFileSync(TRACKERS_CSV_OUTPUT_PATH, csv);

const vpnServicesJSON = readJSON(VPN_SERVICES_INPUT_PATH);

const parsed = vpnServicesJSONSchema.parse(vpnServicesJSON);
// previously recorded VPN services JSON
const upToDateVpnServicesJSON = readVpnServicesJSON(VPN_SERVICES_OUTPUT_PATH);
// VPN services JSON with new records/updates
const vpnServicesJSON = readVpnServicesJSON(VPN_SERVICES_INPUT_PATH);
// modified VPN services JSON with updated modified_time field
// if service was updated or added
const updatedVpnServicesJSON = updateVpnServicesJSONDate(
vpnServicesJSON,
upToDateVpnServicesJSON,
);

writeJSON<VpnServicesJSON>(VPN_SERVICES_OUTPUT_PATH, parsed);
writeJSON<VpnServicesJSON>(VPN_SERVICES_OUTPUT_PATH, updatedVpnServicesJSON);

consola.info('Finished building the companies DB');
} catch (ex) {
Expand Down
Loading