alexandercerutti / passkit-generator

The easiest way to generate custom Apple Wallet passes in Node.js
MIT License
883 stars 109 forks source link

pass.getAsBuffer() fails on Lambda #114

Closed PCC94 closed 2 years ago

PCC94 commented 2 years ago

Running OS

AWS Lambda

Running Node Version

Node.js 16.x

Description

Hi!

First at all, thank you for all your work!

To get into the point, i'm using your library on an AWS Lambda to generating .pkpass files and saving them on a S3 bucket.

Before I put it on lambda, i tried to do all the work on locale and it works well. As you can guess, there the troubles begans when i put the same code on aws.

Duration: 3050.50 ms
Billed Duration: 3000 ms
Memory Size: 128 MB
Max Memory Used: 115 MB
Init Duration: 858.67 ms.   
Task timed out after 3.05 seconds

It seems like the the method pass.getAsBuffer() requests more memory and/or time than the lambda (as set) can offer, so i tried to extended these limits but it still don't work. I also tried to make the pass lighter removing all unecessary images, but nothing.

This is the function where the error occurs.


  async createAndDownloadPass(username) {
        const s3 = new AWS.S3()

        const [signerCert, signerKey, wwdr] = await this.getCertificates() // no troubles here

        const pass = await PKPass.from({
            model: `${this.appleTemplatePath}/modelTemplate.pass`,
            certificates: {
                wwdr: wwdr,
                signerCert: signerCert,
                signerKey: signerKey,
                signerKeyPassphrase: process.env.SIGNER_KEY_PASSPHRASE
            },
        });
        pass.secondaryFields.push({
            "key": "username",
            "value": username
        })

        // no problems until here
        console.log('converting pass in buffer') // <--------------- Last log before it breaks

        const buffer = pass.getAsBuffer() // <---------------  the guilty?

        // never reached 
        const passName = `GeneratedPass-${Math.random()}.pkpass`;
        console.log(`uploading ${passName} on S3 bucket '${process.env.BUCKET_NAME}'`)

       // (i don't know even if it works)
        const {Location} = await s3 
            .upload({
                Bucket: ${process.env.BUCKET_NAME},
                Key: passName,
                ContentType: pass.mimeType,
                Body: buffer,
            })
            .promise();

        console.log(`${passName} Location: `, Location)
        return Location
    }

I think it's odd this operation costs so many resources anyway, so is there something wrong in the way i wrote the code?

Thank You

Paolo

Expected behavior

pass.getAsBuffer() returns a buffer

Steps to reproduce

I think the only way is to set up a Lambda function and put the code on it.

alexandercerutti commented 2 years ago

Hey there @PCC94, thank you for using passkit-generator!

I actually never set up a real lambda with passkit-generator on it. I only tried to run one on a local serverless instance (as per the examples), so I might not be as useful as I wish. But I'll try to do my best.

I don't see anything wrong with your code.

It is actually weird that it takes so long, but I know it might take a while due to signature generation that happens through node-forge. I do remember when I set up the Cloudflare Workers through community help, a performance report was kind of "alerting" that the heaviest part was the signature generation.

So, it does not print anything else after the log you said? No errors, right?

I think the only way is to set up a Lambda function and put the code on it.

Yep, I agree. Are you using one of the models in the examples or a custom one? If custom, would you mind proving it to me just to have a real-world example? I'm available to receive it on PM too if you want to keep it secret (please note that teamIdentifier and passTypeIdentifier and other metadata in pass.json cannot be considered secrets).

I have to understand how to setup and debug a lambda on AWS. There must be something weird. Maybe I can use this to debug one. 🤔

saving them on a S3 bucket.

Just a little final note: Apple does not suggest saving generated passes on a server as they are pretty useless. You can generate one and serve it directly whenever you want by just feeding the data to your generation flow.

Thank you!

PCC94 commented 2 years ago

Hi @alexandercerutti, thank you for your kind response!

We resolved this issue just incrementing both the max level of memory and time, so it was just a performance issue and now it works fine!

I don't know if I simply understimated the amount of resources needed for this task or if there is some margin for performance improvements, in any case i thank you again for your work and kindness.

Paolo