I am trying to load a css file and an image into my pug file and neither are loading. It feels like I have tried everything I could find with no solution. I have my css and image in the public folder which is set as static with express. I appreciate any help I can get.
The folder structure is as follows:
server.js
views
checklist.pug
routes
checklist.js
public
images
logo.png
stylesheets
checklist.css
pdfs
server.js
require('dotenv').config();
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const app = express();
const checklists = require('./routes/checklists');
const port = 3000;
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// View engine
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use(express.static(path.join(__dirname, 'public')));
// Routes
app.use('/checklists', checklists);
app.listen(port, () => {
console.log(`Server started on port ${port}...`);
});
checklist.pug
html(lang='en')
head
link(href='stylesheets/checklist.css' rel='stylesheet')
link(rel='preconnect' href='https://fonts.googleapis.com')
link(rel='preconnect' href='https://fonts.gstatic.com' crossorigin)
link(href='https://fonts.googleapis.com/css2?family=Roboto&display=swap' rel='stylesheet')
body
div(class='checklist-title')
img(src='images/logo.png' alt='Logo...')
checklist.js
const express = require('express');
const aws = require('aws-sdk');
const { v4 } = require('uuid');
const puppeteer = require('puppeteer');
const pug = require('pug');
const dynamodb_config = require('../config/dynamodb/dynamo_config');
aws.config.update(dynamodb_config);
const { validateChecklist } = require('../middleware/validation');
const authenticateToken = require('../middleware/authenticateToken');
const router = express.Router();
const docClient = new aws.DynamoDB.DocumentClient();
const TABLE_NAME = 'Checklists';
// Generate html from checklist object, convert to pdf and send in response
router.get('/:id/pdf', authenticateToken, (req, res) => {
// Get the checklist and make sure the user owns it
const params = {
TableName: TABLE_NAME,
Key: {
id: req.params.id
}
}
docClient.get(params, async (err, data) => {
if (err) {
console.error(err);
res.status(500);
res.json({
error: err
});
}
else {
// Check that an item was found
if (typeof data.Item != 'undefined') {
// An item was found
// Check that the user who sent the request is the owner of the item
if (req.username == data.Item.createdBy) {
// CODE TO GENERATE CHECKLIST HERE
try {
const checklist = {
checklist: data.Item
}
// render html
const compiledFunction = pug.compileFile('views/checklist.pug');
const htmlContent = compiledFunction(checklist);
// convert html to pdf and save file
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setContent(htmlContent, { waitUntil: 'networkidle2' });
await page.pdf({ path: `pdfs/${req.username}.pdf`, printBackground: true });
await browser.close();
res.status(200);
res.json({
message: 'success'
});
}
catch (err) {
console.error(err);
}
}
else {
// User does not own the item
console.error('You are not authorized to access that item');
res.status(403);
res.json({
error: 'You are not authorized to access that item'
});
}
}
else {
// No item was found
console.error(`A checklist with id ${req.params.id} could not be found`);
res.status(404);
res.json({
error: `A checklist with id ${req.params.id} could not be found`
});
}
}
});
});
module.exports = router;
CodePudding user response:
So the problem ended up lying with puppeteer in the end like @OldGeezer said. Since I was using the page.setContent() function from puppeteer to load html content directly into the browser instead of giving it a path to an html file, neither of the relative paths to my image or css worked. How I ended up solving it was by using the page.goto(path) function in puppeteer to load an empty html file I created in the public folder and then using the page.setContent() function to load the html I wanted. This allowed the browser to be in the right directory to load the external files via their relative paths.
server.js
views
checklist.pug
routes
checklist.js
public
empty.html
images
logo.png
stylesheets
checklist.css
pdfs
checklist.js
const express = require('express');
const aws = require('aws-sdk');
const { v4 } = require('uuid');
const puppeteer = require('puppeteer');
const pug = require('pug');
const dynamodb_config = require('../config/dynamodb/dynamo_config');
aws.config.update(dynamodb_config);
const { validateChecklist } = require('../middleware/validation');
const authenticateToken = require('../middleware/authenticateToken');
const router = express.Router();
const docClient = new aws.DynamoDB.DocumentClient();
const TABLE_NAME = 'Checklists';
// Generate html from checklist object, convert to pdf and send in response
router.get('/:id/pdf', authenticateToken, (req, res) => {
// Get the checklist and make sure the user owns it
const params = {
TableName: TABLE_NAME,
Key: {
id: req.params.id
}
}
docClient.get(params, async (err, data) => {
if (err) {
console.error(err);
res.status(500);
res.json({
error: err
});
}
else {
// Check that an item was found
if (typeof data.Item != 'undefined') {
// An item was found
// Check that the user who sent the request is the owner of the item
if (req.username == data.Item.createdBy) {
// CODE TO GENERATE CHECKLIST HERE
try {
const checklist = {
checklist: data.Item
}
// render html
const compiledFunction = pug.compileFile('views/checklist.pug');
const htmlContent = compiledFunction(checklist);
// convert html to pdf and save file
const browser = await puppeteer.launch();
const page = await browser.newPage();
// go to empty html file
await page.goto('file://' __dirname '/../public/empty.html');
await page.setContent(htmlContent, { waitUntil: 'networkidle2' });
await page.pdf({ path: `pdfs/${req.username}.pdf`, printBackground: true });
await browser.close();
res.status(200);
res.json({
message: 'success'
});
}
catch (err) {
console.error(err);
}
}
else {
// User does not own the item
console.error('You are not authorized to access that item');
res.status(403);
res.json({
error: 'You are not authorized to access that item'
});
}
}
else {
// No item was found
console.error(`A checklist with id ${req.params.id} could not be found`);
res.status(404);
res.json({
error: `A checklist with id ${req.params.id} could not be found`
});
}
}
});
});
module.exports = router;
CodePudding user response:
According to here, it seems like the page rendered this way has no current folder. So one way to include images is to use a data url. I tested the below and it worked.
checklist.pug:
div(class="checklist-title")
img(src="data:image/png;base64," logo alt="Logo...")
Server Javascript:
// read image file into a base64 string. You should not use sync read.
const logo = fs.readFileSync("./public/images/logo.png", { encoding: "base64"});
// add property `logo` to your compile function data
const htmlContent = pug.compileFile("views/checklist.pug")({logo: logo});
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setContent(htmlContent, { waitUntil: ["domcontentloaded", "networkidle0"]});
await page.pdf( { path: `pdfs/${req.params.id}.pdf`, printBackground: true});
await browser.close();