Skip to content

Instantly share code, notes, and snippets.

@maxnowack
Created September 27, 2022 14:32
Show Gist options
  • Save maxnowack/754ee97d54b7ba9bf4db38335e18657c to your computer and use it in GitHub Desktop.
Save maxnowack/754ee97d54b7ba9bf4db38335e18657c to your computer and use it in GitHub Desktop.

Revisions

  1. maxnowack created this gist Sep 27, 2022.
    271 changes: 271 additions & 0 deletions getiCloudInfo.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,271 @@
    import fetch from 'node-fetch'

    interface ShortGUID {
    value: string;
    shouldFetchRootRecord: boolean;
    }

    interface ZoneID {
    zoneName: string;
    ownerRecordName: string;
    zoneType: string;
    }

    interface CloudkitType {
    value: string;
    type: string;
    }

    interface CloudkitTitle {
    value: string;
    type: string;
    }

    interface Fields {
    'cloudkit.type': CloudkitType;
    'cloudkit.title': CloudkitTitle;
    }

    interface PluginFields {
    [key: string]: any,
    }

    interface Created {
    timestamp: number;
    userRecordName: string;
    deviceID: string;
    }

    interface Modified {
    timestamp: number;
    userRecordName: string;
    deviceID: string;
    }

    interface ZoneID2 {
    zoneName: string;
    ownerRecordName: string;
    zoneType: string;
    }

    interface Share2 {
    recordName: string;
    zoneID: ZoneID2;
    }

    interface NameComponents {
    givenName: string;
    familyName: string;
    }

    interface LookupInfo {
    emailAddress: string;
    }

    interface UserIdentity {
    userRecordName: string;
    nameComponents: NameComponents;
    lookupInfo: LookupInfo;
    }

    interface Participant {
    participantId: string;
    userIdentity: UserIdentity;
    type: string;
    acceptanceStatus: string;
    permission: string;
    orgUser: boolean;
    }

    interface NameComponents2 {
    givenName: string;
    familyName: string;
    }

    interface LookupInfo2 {
    emailAddress: string;
    }

    interface UserIdentity2 {
    userRecordName: string;
    nameComponents: NameComponents2;
    lookupInfo: LookupInfo2;
    }

    interface Owner {
    participantId: string;
    userIdentity: UserIdentity2;
    type: string;
    acceptanceStatus: string;
    permission: string;
    orgUser: boolean;
    }

    interface Share {
    recordName: string;
    recordType: string;
    fields: Fields;
    pluginFields: PluginFields;
    recordChangeTag: string;
    created: Created;
    modified: Modified;
    share: Share2;
    publicPermission: string;
    participants: Participant[];
    owner: Owner;
    displayedHostname: string;
    shortGUID: string;
    }

    interface LastEditorName {
    value: string;
    type: string;
    }

    interface Extension {
    value: string;
    type: string;
    }

    interface Basehash {
    value: string;
    type: string;
    }

    interface Size {
    value: number;
    type: string;
    }

    interface EncryptedBasename {
    value: string;
    type: string;
    }

    interface Quarantine {
    value: string;
    type: string;
    }

    interface Mtime {
    value: number;
    type: string;
    }

    interface Value {
    fileChecksum: string;
    size: number;
    wrappingKey: string;
    referenceChecksum: string;
    downloadURL: string;
    }

    interface FileContent {
    value: Value;
    type: string;
    }

    interface PublicSharingPermission {
    value: number;
    type: string;
    }

    interface Fields2 {
    lastEditorName: LastEditorName;
    extension: Extension;
    basehash: Basehash;
    size: Size;
    encryptedBasename: EncryptedBasename;
    quarantine: Quarantine;
    mtime: Mtime;
    fileContent: FileContent;
    publicSharingPermission: PublicSharingPermission;
    }

    interface PluginFields2 {
    }

    interface Created2 {
    timestamp: number;
    userRecordName: string;
    deviceID: string;
    }

    interface Modified2 {
    timestamp: number;
    userRecordName: string;
    deviceID: string;
    }

    interface ZoneID3 {
    zoneName: string;
    ownerRecordName: string;
    zoneType: string;
    }

    interface Share3 {
    recordName: string;
    zoneID: ZoneID3;
    }

    interface RootRecord {
    recordName: string;
    recordType: string;
    fields: Fields2;
    pluginFields: PluginFields2;
    recordChangeTag: string;
    created: Created2;
    modified: Modified2;
    deleted: boolean;
    share: Share3;
    displayedHostname: string;
    shortGUID: string;
    }

    interface NameComponents3 {
    givenName: string;
    familyName: string;
    }

    interface LookupInfo3 {
    emailAddress: string;
    }

    interface OwnerIdentity {
    userRecordName: string;
    nameComponents: NameComponents3;
    lookupInfo: LookupInfo3;
    }

    export interface iCloudInfo {
    shortGUID: ShortGUID;
    containerIdentifier: string;
    environment: string;
    databaseScope: string;
    zoneID: ZoneID;
    share: Share;
    rootRecordName: string;
    rootRecord: RootRecord;
    ancestorRecords: any[];
    ownerIdentity: OwnerIdentity;
    potentialMatchList: any[];
    }

    const getGuidFromUrl = (url: string) => {
    const regex = /icloud\.com\/iclouddrive\/(.{25})/gi
    const result = regex.exec(url)
    if (!result || !result[1]) throw new Error(`Cannot get iCloud guid from url '${url}'`)
    return result[1]
    }

    const iCloudDataUrl = 'https://ckdatabasews.icloud.com/database/1/com.apple.cloudkit/production/public/records/resolve'
    const getiCloudInfo = async (url: string): Promise<iCloudInfo> => fetch(iCloudDataUrl, {
    method: 'post',
    body: JSON.stringify({
    shortGUIDs: [{ value: getGuidFromUrl(url) }],
    })
    })
    .then(r => r.json())
    .then(r => (r.results && r.results[0]) || undefined)

    export default getiCloudInfo