I'm developing an Firebase function which should simply generate a PDF file and send it via Email to an email address. I think I have done a lot by this code:
`exports.getpdf = functions.https.onRequest((request, response) => {
const Printer = require('pdfmake');
const fonts = require('pdfmake/build/vfs_fonts.js');
const p = new Printer(fontDescriptors);
const chunks: any[] = [];
const pdfDoc = p.createPdfKitDocument(docDefinition);
pdfDoc.on('data', (chunk: any) => {
chunks.push(chunk);
});
pdfDoc.on('end', () => {
const result = Buffer.concat(chunks);
const nodemailer = require('nodemailer');
const mailTransport = nodemailer.createTransport({
host: "***",
port: 587,
secureConnection: true,
auth: {
user: "***",
pass: "***"
},
tls: {
ciphers: "SSLv3",
}
});
let x = JSON.stringify(result);
const mailOptions = {
from: "***",
to: '***',
subject: 'monthly billing',
html: '<h1>New Billing</h1><textarea>' + x + '</textarea>',
};
mailTransport.sendMail(mailOptions).then((res: any) => {
response.status(200).send(JSON.stringify(res));
}).catch((error: any) => {
response.status(403).send(JSON.stringify(error));
});
});
});`
When I'm calling this function, I get an Email with the following result:
enter image description here
How can I attach the generated PDF (the result variable) into the Email as normal PDF file?
Can anyone please help me there?
I've tried with encoding for base64 and buffer, but nothing changed
Related
I have made a all-in-one moderation bot by compiling certain codes. Recently, I was adding Ticket-System to my bot and it showed me this error. Although, I tried all the fixes I could find on the internet but still couldn't solve it.
Here is my code -->
ticket-config.js
const { Util, MessageEmbed } = require('discord.js');
const configOptions = require('../../configOptions');
module.exports = {
name: "ticketconfig",
description: "Configuration ticket system.",
options: configOptions,
permission: "ADMINISTRATOR",
run: async(interaction, client) => {
const replyMessage = {
content: "Config has been set!"
}
if (interaction.options.getSubcommand() === 'message') {
const message = interaction.options.getString('message');
const content = interaction.options.getString('content') || null;
let data = await client.db.get('config', interaction.guild.id);
if (!data) data = {};
data.message = message;
data.content = content;
await client.db.set('config', interaction.guild.id, data);
return interaction.reply(replyMessage)
}
configOptions.js
module.exports = [
{
name: "message",
description: "Configuration your ticket message",
type: 1,
options: [
{
name: "message",
description: "The message to sent in ticket",
type: 3,
required: true
},
{
name: "content",
description: "content will be appeared above embed message, use /variables command to see all available variables.",
type: 3
}
]
},
./handler/index.js
module.exports = async (client) => {
const eventFiles = await globPromise(`${process.cwd()}/events/*.js`);
eventFiles.map((value) => require(value));
const slashCommands = await globPromise(
`${process.cwd()}/SlashCommands/*/*.js`
);
const arrayOfSlashCommands = [];
slashCommands.map((value) => {
const file = require(value);
if (!file?.name) return;
client.slashCommands.set(file.name, file);
if (["MESSAGE", "USER"].includes(file.type)) delete file.description;
arrayOfSlashCommands.push(file);
});
client.on("ready", async () => {
await client.application.commands.set(arrayOfSlashCommands);
});
Below is how i create the client.
import { create as ipfsHttpClient } from 'ipfs-http-client';
const projectId = 'xx';
const projectSecret = 'xx';
const auth = `Basic ${Buffer.from(`${projectId}:${projectSecret}`).toString('base64')}`;
const options = {
host: 'ipfs.infura.io',
protocol: 'https',
port: 5001,
apiPath: '/ipfs/api/v0',
headers: {
authorization: auth,
},
};
const dedicatedEndPoint = 'https://xx.infura-ipfs.io';
const client = ipfsHttpClient(options);
Here is the function that will be called from front-end that takes in a file, uploads to IPFS and returns URL. Please note that the "ipfsHTTPClient()" is just the create function.
const uploadToIPFS = async (file) => {
try {
const added = await client.add({ content: file });
const url = `${dedicatedEndPoint}${added.path}`;
return url;
} catch (error) {
console.log('Error uploading file to IPFS: ', error);
}
};
The error I am getting is
POST https://ipfs.infura.io:5001/ipfs/api/v0/add?stream-channels=true&progress=false 403 (Forbidden)
When i console log the error it says the IPFS method is not supported.
On the IPFS forum, i have seen someone say that add function does not work anymore but i have also seen people using it and it working. Im not sure whats wrong here.
Here is how i call the function on front-end
const { uploadToIPFS } = useContext(NFTContext);
// function called from useDropzone
const onDrop = useCallback(async (acceptedFile) => {
const url = await uploadToIPFS(acceptedFile[0]);
setFileUrl(url);
}, []);
All the above code is correct and the error was from Next.js
Needed to add
images: {
domains: ['xx.infura-ipfs.io'],
},
to the next.config.js file.
I have resolved this problem
so make sure first you have installed buffer
npm install --save buffer
then import it in your file
import {Buffer} from 'buffer';
then it works successfully
import { create } from "ipfs-http-client";
import { Buffer } from "buffer";
const projectId = "YOUR_INFURA_PROJECT_ID";
const projectSecret = "YOUR_INFURA_PROJECT_SECRET";
const auth = `Basic ${Buffer.from(`${projectId}:${projectSecret}`).toString(
"base64"
)}`;
const client = create({
host: "ipfs.infura.io",
port: 5001,
protocol: "https",
apiPath: "/api/v0",
headers: {
authorization: auth,
},
});
const uploadFiles = async (e) => {
e.preventDefault();
setUploading(true);
if (text !== "") {
try {
const added = await client.add(text);
setDescriptionUrl(added.path);
} catch (error) {
toast.warn("error to uploading text");
}
}
I have a button that triggers this function from my app that calls the, "testThisShitOut()" function.
Here is the code on my app (.jsx file)
firebaseConfig = {
'blah blah'
}
const app = initializeApp(firebaseConfig);
const functions = getFunctions(app);
const stripeCheckout = httpsCallable(functions, 'createStripeCheckout');
function testThisOut() {
stripeCheckout('this is the data', 'more shit')
.then((result) => {
// Read result of the Cloud Function.
/** #type {any} */
const data = result.data;
const sanitizedMessage = data.text;
})
.catch((error) => {
console.log(error, "we got an error")
// Getting the Error details.
const code = error.code;
const message = error.message;
const details = error.details;
// ...
});
}
and here is what I have in my index.js file under my functions folder that was created when I install firebase tools and stuff.
const functions = require("firebase-functions");
exports.createStripeCheckout = functions.https.onCall(async (data, context) => {
const YOUR_DOMAIN = 'https://mysite';
console.log("this is the data", data)
console.log("this is the context", context)
const stripe = require('stripe')('blah');
const session = await stripe.checkout.sessions.create({
line_items: [
{
price: 'blah',
quantity: 1,
},
],
mode: 'payment',
success_url: `${YOUR_DOMAIN}?success=true`,
cancel_url: `${YOUR_DOMAIN}?canceled=true`,
});
res.redirect(303, session.url);
});
Any ideas on how to fix this? I haven't tried much yet. Not sure what to do. Thanks for the help :)
I'm not very familiar with creating custom hooks, but what I would like to acheive is to create a function that takes in a file, which is then uploaded to firebase.
It would look like:
const UseUpload = (file) => {
const [fileInfo, setFileInfo] = useState({
fileUrl: '',
progress: 0,
error: '',
fileName: '',
})
const handleUpload = () => {
let file = e.target.files[0]
if(file) {
const storageRef = firebase
.storage()
.ref(`songs`)
.child(file.name)
const task = storageRef.put(file)
task.on(
"state_changes",
function progress(snap) {
setLoading(true);
const percentage = (snap.bytesTransferred / snap.totalBytes) * 100;
setFileInfo({
...fileInfo,
progress: percentage
})
},
function error(err) {
setFileInfo({
...fileInfo,
error: err
})
},
function complete() {
storageRef.getDownloadURL().then((url) => {
setFileInfo({
...fileInfo,
fileUrl: url,
fileName: file.name
})
});
}
)
}
}
return fileInfo
}
SO basically what this does, is it takes in a file from an input type=file and then returns the fileInfo state.
Is there a way of achieving this?
I've been stuck on this for a while now. My code is based on https://webrtc.org/getting-started/firebase-rtc-codelab. I basically just changed it to React and firebase realtime database instead of firestore.
const VideoRoom = () => {
const config = {
apiKey: xxx,
authDomain: xxx,
databaseURL: xxx,
projectId: xxx,
storageBucket: xxx,
messagingSenderId: xxx,
appId: xxx,
};
const rtcconfig = {
iceServers: [
{
urls: [
"stun:stun1.l.google.com:19302",
"stun:stun2.l.google.com:19302",
],
},
],
iceCandidatePoolSize: 10,
};
const { roomId } = useParams();
if (!firebase.apps.length) {
firebase.initializeApp(config);
}
const db = firebase.database();
const rooms = () => db.ref("rooms");
const room = (roomId) => db.ref(`rooms/${roomId}`);
const callerCandidates = (roomId) =>
db.ref(`rooms/${roomId}/callerCandidates`);
const calleeCandidates = (roomId) =>
db.ref(`rooms/${roomId}/calleeCandidates`);
const peerConnection = new RTCPeerConnection(rtcconfig);
var localStream = new MediaStream();
var remoteStream = null;
var localVideo = useRef(null);
var remoteVideo = useRef(null);
room(roomId)
.once("value")
.then((snapshot) => {
if (!snapshot.val()) {
createRoom();
return;
}
joinRoomById(roomId, snapshot.val());
});
useEffect(() => {
peerConnection.addEventListener("icegatheringstatechange", () => {
console.log(
`ICE gathering state changed: ${peerConnection.iceGatheringState}`
);
});
peerConnection.addEventListener("connectionstatechange", () => {
console.log(`Connection state change: ${peerConnection.connectionState}`);
});
peerConnection.addEventListener("signalingstatechange", () => {
console.log(`Signaling state change: ${peerConnection.signalingState}`);
});
peerConnection.addEventListener("iceconnectionstatechange ", () => {
console.log(
`ICE connection state change: ${peerConnection.iceConnectionState}`
);
});
});
const createRoom = async () => {
console.log("create room");
localStream.getTracks().forEach((track) => {
console.log("adding localStream to peerConnection");
peerConnection.addTrack(track, localStream);
});
peerConnection.addEventListener("icecandidate", (event) => {
console.log("listening for icecandidate on peerConnection");
if (!event.candidate) {
console.log("final icecandidate");
return;
}
console.log("callerCandidate written to database");
callerCandidates(roomId).set(event.candidate.toJSON());
});
const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);
console.log("Offer created and added to peerConnection local description");
const roomWithOffer = {
offer: {
type: offer.type,
sdp: offer.sdp,
},
};
await room(roomId).update(roomWithOffer);
console.log("Offer written to room database document");
peerConnection.addEventListener("track", (event) => {
event.streams[0].getTracks().forEach((track) => {
console.log(
"listening for track on peerConnection and adding them to remoteStream"
);
remoteStream.addTrack(track);
});
});
room(roomId).on("value", async (snapshot) => {
console.log("listening for remote session in room database document");
const data = snapshot.val();
if (!peerConnection.currentRemoteDescription && data && data.answer) {
console.log("Got remote description: ", data.answer);
const rtcSessionDescription = new RTCSessionDescription(data.answer);
await peerConnection.setRemoteDescription(rtcSessionDescription);
}
});
calleeCandidates(roomId).on("value", (snapshot) => {
console.log(
"listening for remote ICE candidates in room/calleCandidates database document"
);
if (snapshot.val()) {
snapshot.val().forEach(async (change) => {
if (change.type === "added") {
let data = change.doc.data();
await peerConnection.addIceCandidate(new RTCIceCandidate(data));
}
});
}
});
};
};
I'm getting the roomId through react router, I left out the rest of the component for the sake of readability.
The console returns following:
create room
Signaling state change: have-local-offer
Offer created and added to peerConnection local description
ICE gathering state changed: complete
listening for icecandidate on peerConnection
final icecandidate
adding localStream to peerConnection
Offer written to room database document
listening for remote ICE candidates in room/calleCandidates database document
listening for remote session in room database document
In the about:webrtc firefox tab I can see that the offer was created succesfully but there arent any ice candidates
Thanks in advance!
So, turns out ICE Candidates gathering only start when some track is added to the stream, and that MUST be done BEFORE the offer creation.
So if you gather the media and add it to the RTCPeerConnection instance BEFORE creating the offer, then you'll see ICE candidates being gathered properly.
EDIT: Aside from this, If you provide the options upon offer creation like below, it will start ICE gathering as expected:
await this.connection.createOffer({
offerToReceiveAudio: true,
offerToReceiveVideo: true,
});
Hope it helps!