Skip to content

Instantly share code, notes, and snippets.

@ochui
Created November 13, 2023 12:37
Show Gist options
  • Save ochui/ecad5fc3499a7c5b176fe9b0809672fb to your computer and use it in GitHub Desktop.
Save ochui/ecad5fc3499a7c5b176fe9b0809672fb to your computer and use it in GitHub Desktop.
AWS S3 RemoteAuth strategy for whatsapp-web.js
const fs = require('fs');
class S3Store {
constructor({ s3, bucketName } = {}) {
if (!s3 || !bucketName) {
throw new Error('A valid S3 instance and bucket name are required for S3Store.');
}
this.s3 = s3;
this.bucketName = bucketName;
}
async sessionExists(options) {
try {
await this.s3.headObject({ Bucket: this.bucketName, Key: `${options.session}.zip` }).promise();
return true;
} catch (error) {
if (error.code === 'NotFound') {
return false;
}
throw error;
}
}
async save(options) {
const params = {
Bucket: this.bucketName,
Key: `${options.session}.zip`,
Body: fs.createReadStream(`${options.session}.zip`),
};
await this.s3.upload(params).promise();
await this.#deletePrevious(options);
}
async extract(options) {
const params = {
Bucket: this.bucketName,
Key: `${options.session}.zip`,
};
const stream = this.s3.getObject(params).createReadStream();
return new Promise((resolve, reject) => {
stream
.pipe(fs.createWriteStream(options.path))
.on('error', err => reject(err))
.on('close', () => resolve());
});
}
async delete(options) {
await this.s3.deleteObject({ Bucket: this.bucketName, Key: `${options.session}.zip` }).promise();
}
async #deletePrevious(options) {
const listParams = { Bucket: this.bucketName, Prefix: `${options.session}.zip` };
const response = await this.s3.listObjectsV2(listParams).promise();
if (response.Contents.length > 1) {
const oldSession = response.Contents.reduce((a, b) => (a.LastModified < b.LastModified ? a : b));
await this.s3.deleteObject({ Bucket: this.bucketName, Key: oldSession.Key }).promise();
}
}
}
// Example usage:
// const s3 = new AWS.S3({/* S3 configuration */});
// const s3Store = new S3Store({ s3, bucketName: 'your-s3-bucket-name' });
// s3Store.save({ session: 'your-session-id' });
// s3Store.extract({ session: 'your-session-id', path: 'local-path' });
// s3Store.delete({ session: 'your-session-id' });
module.exports = S3Store;
@ochui
Copy link
Author

ochui commented Nov 14, 2023

AWS V3 SDK

const fs = require('fs');
const { Readable } = require('stream');
const { HeadObjectCommand, UploadCommand, GetObjectCommand, DeleteObjectCommand, ListObjectsV2Command } = require('@aws-sdk/client-s3');

class S3Store {
    constructor({ s3, bucketName } = {}) {
        if (!s3 || !bucketName) {
            throw new Error('A valid S3 instance and bucket name are required for S3Store.');
        }
        this.s3 = s3;
        this.bucketName = bucketName;
    }

    async sessionExists(options) {
        try {
            await this.s3.send(new HeadObjectCommand({ Bucket: this.bucketName, Key: `${options.session}.zip` }));
            return true;
        } catch (error) {
            if (error.name === 'NotFound') {
                return false;
            }
            throw error;
        }
    }

    async save(options) {
        const params = {
            Bucket: this.bucketName,
            Key: `${options.session}.zip`,
            Body: fs.createReadStream(`${options.session}.zip`),
        };
        await this.s3.send(new UploadCommand(params));
        await this.#deletePrevious(options);
    }

    async extract(options) {
        const params = {
            Bucket: this.bucketName,
            Key: `${options.session}.zip`,
        };
        const response = await this.s3.send(new GetObjectCommand(params));

        const stream = Readable.from(response.Body);

        return new Promise((resolve, reject) => {
            stream
                .pipe(fs.createWriteStream(options.path))
                .on('error', err => reject(err))
                .on('close', () => resolve());
        });
    }

    async delete(options) {
        await this.s3.send(new DeleteObjectCommand({ Bucket: this.bucketName, Key: `${options.session}.zip` }));
    }

    async #deletePrevious(options) {
        const listParams = { Bucket: this.bucketName, Prefix: `${options.session}.zip` };
        const response = await this.s3.send(new ListObjectsV2Command(listParams));

        if (response.Contents.length > 1) {
            const oldSession = response.Contents.reduce((a, b) => (a.LastModified < b.LastModified ? a : b));
            await this.s3.send(new DeleteObjectCommand({ Bucket: this.bucketName, Key: oldSession.Key }));
        }
    }
}

module.exports = S3Store;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment