|
|
|
import * as functions from "firebase-functions"; |
|
import { ShadowFile, ShdwDrive } from "@shadow-drive/sdk"; |
|
import { Wallet } from "@project-serum/anchor"; |
|
|
|
import { Metaplex } from "@metaplex-foundation/js"; |
|
import jimp from "jimp"; |
|
import { Connection, Keypair, PublicKey } from "@solana/web3.js"; |
|
|
|
const secretKey = Uint8Array.from(process.env.SHADOW_KEY as Iterable<number>); |
|
const keypair = Keypair.fromSecretKey(secretKey); |
|
const connection = new Connection( |
|
`https://rpc.helius.xyz/?api-key=${process.env.HELIUS_RPC_KEY}`, |
|
"confirmed" |
|
); |
|
|
|
const metaplex = Metaplex.make(connection); |
|
const wallet = new Wallet(keypair); |
|
|
|
type TTransform = { |
|
match: string; |
|
type: string; |
|
value: string | number; |
|
}; |
|
const supportedTrasnformations = [ |
|
{ match: "w", type: "width" }, |
|
{ match: "h", type: "height" }, |
|
]; |
|
|
|
export const transform = functions.https.onRequest( |
|
async (request, response) => { |
|
response.set("Access-Control-Allow-Origin", "*"); |
|
response.set("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); |
|
response.set("Access-Control-Allow-Headers", "Content-Type"); |
|
|
|
// Get the API key from headers or query parameters |
|
const apiKey = request.headers["api-key"] || request.query["api-key"]; |
|
|
|
if (!apiKey) { |
|
response.status(401).send("API key is required"); |
|
return; |
|
} |
|
|
|
// Validate the API key |
|
const expectedApiKey = "alloi-secret-club"; |
|
if (apiKey !== expectedApiKey) { |
|
response.status(403).send("Invalid API key"); |
|
return; |
|
} |
|
|
|
const drive = await new ShdwDrive(connection, wallet) |
|
.init() |
|
.catch((err) => { |
|
console.log(err); |
|
return err; |
|
}); |
|
|
|
const accts = await drive.getStorageAccounts("v2"); |
|
const accountKey = "alloi"; |
|
let accountPublicKey; |
|
// handle printing pubKey of first storage acct |
|
const existingAccount = accts.find( |
|
(r: any) => r.account.identifier === accountKey |
|
); |
|
|
|
if (!existingAccount) { |
|
const account = await drive |
|
.createStorageAccount(accountKey, "10MB", "v2") |
|
.then((r: any) => { |
|
console.log(r); |
|
return r; |
|
}) |
|
.catch((err: any) => { |
|
console.log(err); |
|
return err; |
|
}); |
|
accountPublicKey = new PublicKey(account.shdw_bucket); |
|
} else { |
|
accountPublicKey = existingAccount.publicKey; |
|
} |
|
|
|
// Url alloi/transform/mintid/w-300_h-300 |
|
const parts = request.url.split("/"); |
|
const mintAddress = new PublicKey(parts[2]); |
|
|
|
// Check shadowdrive |
|
const fileName = `${parts[2]}__${parts[3]}`; |
|
const imagePath = `https://shdw-drive.genesysgo.net/${accountPublicKey}/${fileName}`; |
|
|
|
const existingImage = await jimp.read(imagePath).catch(() => { |
|
return; |
|
}); |
|
|
|
console.log(existingImage); |
|
if (existingImage) { |
|
response.set("Content-Type", existingImage.getMIME()); |
|
const buffer = await existingImage.getBufferAsync(jimp.MIME_PNG); |
|
response.send(buffer); |
|
return; |
|
} |
|
|
|
// Get transformations based on supported list |
|
const transformations = parts[3] |
|
.split("_") |
|
.map((r) => { |
|
const match = r.split("-")[0]; |
|
console.log( |
|
"match", |
|
match, |
|
supportedTrasnformations.find((x) => x.match === match) |
|
); |
|
const transform = { |
|
...supportedTrasnformations.find((x) => x.match === match), |
|
value: r.split("-")[1], |
|
} as TTransform; |
|
console.log(transform); |
|
if (!transform?.type) { |
|
return null; |
|
} |
|
return transform; |
|
}) |
|
.filter((r) => r !== null); |
|
|
|
// Get mint address |
|
// See if the path matches an exisiting image based on the transform props |
|
// If so return cached image from shadow drive |
|
// if not process image, |
|
// save to shadowdrive |
|
// return it |
|
|
|
const nft = await metaplex.nfts().findByMint({ mintAddress }); |
|
if (!nft.json || !nft.json.image) { |
|
console.error("Image not found"); |
|
response.json({ error: "Image not found" }); |
|
return; |
|
} |
|
const nftImage = nft.json.image; |
|
// Read the image. |
|
const image = await jimp.read(nftImage); |
|
|
|
// Handle resize |
|
const width = transformations.find((r) => r?.type === "width"); |
|
const height = transformations.find((r) => r?.type === "height"); |
|
if (width && height) { |
|
await image.resize(Number(width.value), Number(height.value)); |
|
} else if (width) { |
|
await image.resize(Number(width.value), jimp.AUTO); |
|
} else if (height) { |
|
await image.resize(jimp.AUTO, Number(height.value)); |
|
} |
|
|
|
const buffer = await image.getBufferAsync(jimp.MIME_PNG); |
|
|
|
const fileToUpload: ShadowFile = { |
|
name: `${fileName}`, |
|
file: buffer, |
|
}; |
|
console.log("uploading file"); |
|
const uploadFile = await drive.uploadFile(accountPublicKey, fileToUpload); |
|
console.log(uploadFile); |
|
|
|
response.set("Content-Type", image.getMIME()); |
|
response.send(buffer); |
|
return; |
|
} |
|
); |