I'm having issues properly exporting a PDF when using Apple MapKit tiles. It works fine with Google Maps or Bing tiles.
Expected output:
Actual output:
Notice the tiles zoomed incorrectly.
How to reproduce the issue:
Use the following curl command with a local Puppeteer server:
curl -X GET -G "http://localhost:8080" --data-urlencode "url=https://en.mycoursewalk.com/coursewalks/18624/print?print_pdf=true&unlisted_id=e61d9b86d7" --data-urlencode "page_orientation=Landscape" --data-urlencode "paper_size=Letter" --output "test.pdf"
You can access the page in your browser at: https://en.mycoursewalk.com/coursewalks/18624/print?unlisted_id=e61d9b86d7
package.json
{
"name": "chrome-puppeteer-pdf-export",
"version": "1.0.1",
"description": "",
"engines": {
"node": "12.22.6"
},
"main": "app.js",
"directories": {
"doc": "doc",
"lib": "lib",
"test": "test"
},
"scripts": {
"start": "node app.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"express": "^4.17.1",
"valid-url": "^1.0.9",
"puppeteer": "10.4.0"
}
}
app.js
const express = require('express');
const app = express();
const puppeteer = require('puppeteer');
const port = process.env.PORT || 8080;
const validUrl = require('valid-url');
const parseUrl = function(url) {
url = decodeURIComponent(url);
if (!/^(?:f|ht)tps?\:\/\//.test(url)) {
url = 'http://' + url;
}
return url;
};
app.get('/', function(req, res) {
const urlToScreenshot = parseUrl(req.query.url);
if (validUrl.isWebUri(urlToScreenshot)) {
console.log('Screenshotting: ' + urlToScreenshot);
const page_orientation = req.query.page_orientation;
const paper_size = req.query.paper_size;
(async() => {
let browser;
try {
browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
const page = await browser.newPage();
await page.emulateMediaType('print');
await page.setCacheEnabled(false);
await page.setViewport({ width: 1024, height: 768, deviceScaleFactor: 2 });
await page.goto(urlToScreenshot, { timeout: 30000, waitUntil: 'networkidle0' });
await page.waitFor(250);
await page.pdf({
format: paper_size,
landscape: (page_orientation === 'Landscape'),
margin: { top: 36, right: 36, bottom: 20, left: 36 },
printBackground: true
}).then(function(buffer) {
res.setHeader('Content-Disposition', 'attachment;filename="export.pdf"');
res.setHeader('Content-Type', 'application/pdf');
res.send(buffer)
});
} catch (err) {
console.log(err.message);
} finally {
if (browser) {
browser.close();
}
}
})();
} else {
res.send('Invalid url: ' + urlToScreenshot);
}
});
app.listen(port, function() {
console.log('App listening on port ' + port)
});
UPDATE
The problem is only happening for maps that end up on a fractional zoom level. I allow a zoom step of 0.25.
The problem only happens with the Apple MapKit tiles. It works fine with the Google Maps or Bing.
The expected output screenshot above is taken by printing to PDF from the Google Chrome browser. The map also displays correctly in the browser. The problem only happens when generating a PDF using Puppeteer
Related
I am using a simple API to send Emails via axios and nodemailer in Next JS.
Locally everythings works normally, after deployment to Vercel i am getting Error 500 when trying to send an Email.
This is from the Form Component:
async function onSubmitForm(values) {
setLoading(true);
let config = {
method: "post",
url: `${env.process.NEXT_LINK}/api/contact`,
headers: {
"Content-Type": "application/json",
},
data: values,
};
try {
const response = await axios(config);
setLoading(false);
setSuccess(true);
} catch (err) {
console.log("frontend error", err);
}
}
Getting this Error in Dev Tools:
frontend error
o {message: 'Request failed with status code 500', name: 'AxiosError', code: 'ERR_BAD_RESPONSE', config: {…}, request: XMLHttpRequest, …}
code
:
"ERR_BAD_RESPONSE"
config
:
{transitional: {…}, transformRequest: Array(1), transformResponse: Array(1), timeout: 0, adapter: ƒ, …}
message
:
"Request failed with status code 500"
name
:
"AxiosError"
request
:
XMLHttpRequest {onreadystatechange: null, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}
response
:
{data: '', status: 500, statusText: '', headers: {…}, config: {…}, …}
[[Prototype]]
:
Error
The contact API File:
import handlebars from "handlebars";
const nodemailer = require("nodemailer");
const path = require("path");
const fs = require("fs");
export default async (req, res) => {
const { firstName, name, email, message, phone } = req.body;
const transporter = nodemailer.createTransport({
host: "smtp.gmail.com",
port: 465,
secure: true,
auth: {
user: process.env.NEXT_USER,
pass: process.env.NEXT_GMAIL,
},
});
await new Promise((resolve, reject) => {
// verify connection configuration
transporter.verify(function (error, success) {
if (error) {
console.log(error);
reject(error);
} else {
console.log("Server is ready to take our messages");
resolve(success);
}
});
});
const filePath = path.join(__dirname, "../../../../components/form/html/template.html");
const source = fs.readFileSync(filePath, "utf-8").toString();
const template = handlebars.compile(source);
const replacements = {
name: name,
email: email,
phone: phone,
message: message,
};
const htmlToSend = template(replacements);
await new Promise((resolve, reject) => {
if (!firstName) {
try {
const emailRes = transporter.sendMail({
from: email,
to: {process.env.NEXT_MYEMAIL},
subject: `Email von ${name}`,
html: htmlToSend,
});
console.log("Message Sent", emailRes.messageId);
res.status(200).json(req.body);
} catch (err) {
console.log("GEHT NET", err);
}
} else {
res.status(403).json(req.body);
}
});
console.log(req.body, "Test");
};
Package JSON:
{
"name": "default_next",
"version": "0.1.0",
"private": true,
"main": "index.js",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"export": "next build && next export",
"lint": "next lint"
},
"dependencies": {
"#amir04lm26/react-modern-calendar-date-picker": "^1.0.1",
"#palmabit/react-cookie-law": "^0.7.0",
"#portabletext/react": "^1.0.6",
"#sanity/asset-utils": "^1.3.0",
"#sanity/client": "^3.3.2",
"#sanity/image-url": "^1.0.1",
"animate.css": "^4.1.1",
"axios": "^0.27.2",
"framer-motion": "^7.1.0",
"handlebars": "^4.7.7",
"next": "12.1.6",
"next-sanity-image": "^3.2.1",
"next-useragent": "^2.8.0",
"nodemailer": "^6.7.8",
"react": "18.2.0",
"react-animate-on-scroll": "^2.1.5",
"react-collapsed": "^3.3.2",
"react-cookie-consent": "^8.0.1",
"react-device-detect": "^2.2.2",
"react-dom": "18.2.0",
"react-hook-form": "^7.34.2",
"react-icons": "^4.4.0",
"react-loader-spinner": "^5.3.3",
"react-modern-calendar-datepicker": "^3.1.6",
"react-spring": "^9.4.5",
"react-sticky": "^6.0.3",
"react-use-scroll-snap": "0.0.4",
"react-visibility-sensor": "^5.1.1",
"swiper": "^8.3.0"
},
"devDependencies": {
"autoprefixer": "^10.4.7",
"eslint": "8.17.0",
"eslint-config-next": "12.1.6",
"eslint-plugin-react": "^7.30.1",
"postcss": "^8.4.14",
"tailwindcss": "^3.1.3"
}
}
I declared all env variables in Vercel.
The Error logs in the Component File, but the path to the API is correct.
I tried hardcoding the Api path, checking all the env variables on Vercel, changing the path to see the Error Message (Error 404).
Ran it again locally, everything works there.
SOLUTION:
I looked into the Vercel functions logs and got an error for the handlebars template file. File not found. Removed handlebars from API and it worked!
I am using nodejs, nestjs, supertest, mongodb, mongoose.
My tests run is completed successfully, using command
npm run test:e2e from default nestjs package.json config.
When I execute my separate test in debug mode, from vscode debug view it fails with error of disposed mongodb connection. The test has mutliple async requests calls which I await it returns after request is called and starts executing jest teardown afterEach and afterAll which is kinda strange for me.
My launch.json
{
"type": "node",
"request": "launch",
"name": "Jest Current File e2e tests",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"args": [
"${fileBasenameNoExtension}",
"--config",
"${workspaceFolder}/test/jest-e2e.json"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"disableOptimisticBPs": true,
"windows": {
"program": "${workspaceFolder}/node_modules/jest/bin/jest"
}
}
jest-e2e.json
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
app.e2e-spec.ts
describe('app (e2e)', () => {
let app: INestApplication;
let connection: Connection;
let dbInitializer: DbInitializer;
beforeAll(async () => {
const moduleFixture: TestingModule = await createAndCompileTestingModule();
app = moduleFixture.createNestApplication();
await configApp(app);
await app.init();
});
afterAll(async () => {
await app.close();
});
beforeEach(async () => {
const configService = app.get(ConfigService);
const logger = new MyLogger(configService);
connection = app.get(CustomConnectionService).getConnection();
dbInitializer = new DbInitializer(connection, logger);
await dbInitializer.seedDb();
});
afterEach(async () => {
await connection.db.dropCollection(dbInitializer.articleCollectionName);
await connection.db.dropCollection(dbInitializer.userCollectionName);
});
it('/ (POST) creates article', async function () {
expect.assertions(9);
const userToLogin = {
username: 'leane1Gra',
password: 'cft0id32',
};
const httpServer = app.getHttpServer();
const responseLogin = await request(httpServer)
.post(`/${LoginEndPoint}`)
.set('Accept', 'application/json')
.send(userToLogin);
expect(responseLogin.statusCode).toBe(201);
const userLoginResponse = responseLogin.body as UserLoginResponse;
const resposeGetUser = await request(app.getHttpServer())
.get(`/${UsersEndpoint}/by-username`)
.query({ username: userToLogin.username });
expect(resposeGetUser.statusCode).toBe(200);
const userFindByUsernameResponse =
resposeGetUser.body as MappedUserResponse;
const articleToCreate = {
title: 'article a',
subtitle: 'subtitle a',
description: 'description a',
category: 'history',
ownerId: userFindByUsernameResponse.id,
};
const response = await request(httpServer)
.post(`/${ArticlesEndpoint}`)
.set('Authorization', `Bearer ${userLoginResponse.user_jwt}`)
.set('Accept', 'application/json')
.send(articleToCreate);
expect(response.statusCode).toBe(201);
expect(response.headers['content-type']).toMatch(/json/);
const { updatedUser, newArticle } = response.body as CreateArticleResponse;
expect(newArticle.id).toBeTruthy();
expect(newArticle.title).toBe(articleToCreate.title);
expect(updatedUser.id).toBeTruthy();
expect(updatedUser.articleIds).toContain(newArticle.id);
expect(updatedUser.numberOfArticles).toBe(
userFindByUsernameResponse.numberOfArticles + 1,
);
});
}
I am adding a video of test run in debug mode
Add the timeout after your it test. The max value for timeout is 32 bit signed integer.
You can debug your test during specified timeout without jest teardown called.
it('/ (POST) creates article', async function () {
//test body omitted
},147483647)
We are building Chat Bot project using Actions on google SDK, we often face
one issue while testing our Bot on console it always show one response, but
on mobile sometime it shows double response for same Bot request response.
Attached are screen shots for same Bot flow, one is on mobile and other is
using console, please guide us what is wrong with it, we monitor our code again and again it send only one response.
index.js
const express = require('express');
const bodyParser = require('body-parser');
const routes = require('./app/router/routes');
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(routes);
const PORT = 8080;
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}...`);
});
routes.js
const express = require('express');
const router = new express.Router();
const {app} = require('../controller/googleAssistantSample');
router.post('/test12543/gooogleWebhook', app);
module.exports = router;
googleAssistantSample.js
const {
conversation,
Simple,
Suggestion,
} = require('#assistant/conversation');
const app = conversation();
app.handle('mainMenu', (conv) => {
conv.add(new Simple({
speech: 'Please select Informasi for producing issue.',
text: 'Please select Informasi for producing issue.',
}));
conv.add(new Suggestion({
title: 'Informasi',
}));
});
app.handle('information', (conv) => {
conv.add(new Simple({
speech: 'Next please select Safe Banking for producing issue.',
text: 'Next please select Safe Banking for producing issue.',
}));
conv.add(new Suggestion({
title: 'Safe Banking',
}));
});
app.handle('safeInfo', (conv) => {
conv.add(new Simple({
speech: 'Please check issue.',
text: 'Please check issue.',
}));
});
module.exports = {
app,
};
package.json
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"#assistant/conversation": "^3.7.1",
"body-parser": "^1.19.0",
"express": "^4.17.1"
}
}
Testing on mobile image
Testing on Actions console image
I am running my blockchain project which is election system using blockchain using hyperledger fabric and IBM blockchain Platform VSCode Extension (runnning my network locally). I am successfull to run connect to local fabric and my gateway is running successfully(image attached). Also all docker containers of peer,orderer,certificate authority are running (image attached).
connected to local fabric and gateway
all docker containers running successfuly
The problem which I am facing is that when (after connecting to local fabric and gateway) I am running invoke.js file using the command "node invoke.js" I am encountering above error. following picture link shows how I am running invoke.js
trying to run invoke.js but facing error
following is my config.json file code
{
"connection_file": "fabric_connection.json",
"appAdmin": "admin",
"appAdminSecret": "adminpw",
"orgMSPID": "org1MSP",
"caName": "ca",
"userName": "V1",
"gatewayDiscovery": { "enabled": true, "asLocalhost": true }
}
and following is my fabric-connection.json file code
{
"certificateAuthorities": {
"ca.org1.example.com": {
"caName": "ca",
"url": "http://localhost:17090"
}
},
"client": {
"connection": {
"timeout": {
"orderer": "300",
"peer": {
"endorser": "300"
}
}
},
"organization": "Org1MSP"
},
"name": "ca.org1.example.com",
"organizations": {
"Org1MSP": {
"certificateAuthorities": [
"ca.org1.example.com"
],
"mspid": "Org1MSP",
"peers": [
"Org1Peer1"
]
}
},
"peers": {
"Org1Peer1": {
"url": "grpc://localhost:17091"
}
},
"version": "1.0.0"
}
and following is my invoke.js file complete code
//Import Hyperledger Fabric 1.4 programming model - fabric-network
'use strict';
const { FileSystemWallet, Gateway } = require('fabric-network');
const path = require('path');
const fs = require('fs');
//connect to the config file
const configPath = path.join(process.cwd(), './config.json');
const configJSON = fs.readFileSync(configPath, 'utf8');
const config = JSON.parse(configJSON);
// connect to the connection file
const ccpPath = path.join(process.cwd(), './ibpConnection.json');
const ccpJSON = fs.readFileSync(ccpPath, 'utf8');
const connectionProfile = JSON.parse(ccpJSON);
// A wallet stores a collection of identities for use
const walletPath = path.join(process.cwd(), './wallet');
const wallet = new FileSystemWallet(walletPath);
console.log(`Wallet path: ${walletPath}`);
const peerIdentity = 'admin';
async function queryAll() {
try {
let response;
// Check to see if we've already enrolled the user.
const userExists = await wallet.exists(peerIdentity);
if (!userExists) {
console.log('An identity for the user ' + peerIdentity + ' does not exist in the wallet');
console.log('Run the registerUser.js application before retrying');
response.error = 'An identity for the user ' + peerIdentity + ' does not exist in the wallet. Register ' + peerIdentity + ' first';
return response;
}
//connect to Fabric Network, but starting a new gateway
const gateway = new Gateway();
//use our config file, our peerIdentity, and our discovery options to connect to Fabric network.
await gateway.connect(connectionProfile, { wallet, identity: peerIdentity, discovery: config.gatewayDiscovery });
//connect to our channel that has been created on IBM Blockchain Platform
const network = await gateway.getNetwork('mychannel');
//connect to our insurance contract that has been installed / instantiated on IBM Blockchain Platform
const contract = await network.getContract('voteChainDemo');
//submit transaction to the smart contract that is installed / instnatiated on the peers
console.log('calling contract.evaluateTransaction, with args');
response = await contract.submitTransaction('queryAll');
response = JSON.parse(response.toString());
console.log(`response from evaluateTransaction: ${(response)}`)
console.log('Transaction has been submitted');
// Disconnect from the gateway.
await gateway.disconnect();
} catch (error) {
console.error(`Failed to submit transaction: ${error}`);
}
}
// let args = ["V1"]
// args = args.toString();
queryAll();
and I see following logs when I view docker container logs of peer0.org1.example.com
enter image description here
following is the code of ibpconnection.json file
{
"name": "mychannel",
"description": "Network on IBP v2",
"version": "1.0.0",
"client": {
"organization": "org1MSP"
},
"organizations": {
"org1MSP": {
"mspid": "org1MSP",
"peers": [
"173.193.112.109:17091"
],
"certificateAuthorities": [
"173.193.112.109:7054"
]
}
},
"orderers": {
"173.193.112.109:7050": {
"url": "grpcs://173.193.112.109:17097",
"tlsCACerts": {
"pem": "-----BEGIN CERTIFICATE-----\nMIICJzCCAc6gAwIBAgIUCZxOyrvnwM/IG/3zQ9opnOE/gBEwCgYIKoZIzj0EAwIw\nZTELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMRQwEgYDVQQK\nEwtIeXBlcmxlZGdlcjEPMA0GA1UECxMGRmFicmljMRYwFAYDVQQDEw1PcmRlcmVy\nQ0EtdGxzMB4XDTE5MDYxNDIwNDcwMFoXDTM0MDYxMDIwNDcwMFowZTELMAkGA1UE\nBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMRQwEgYDVQQKEwtIeXBlcmxl\nZGdlcjEPMA0GA1UECxMGRmFicmljMRYwFAYDVQQDEw1PcmRlcmVyQ0EtdGxzMFkw\nEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOXI7XkoPBn7a9Q1x2S8SpmilQBalhorq\nCo96GChxQU0HJX/1qRPNN72CKx2YS/ksl+eOaHe/+pH32S5VWZLxaKNcMFowDgYD\nVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFIdV28CJ\nPozrl6hpxVkKpNdmAO7vMBUGA1UdEQQOMAyHBK3BcG2HBApM2GAwCgYIKoZIzj0E\nAwIDRwAwRAIgTOPmbGXzIL8SriNT/x8XdBLoTbpEVd/HIpv9nf0bWysCIBvOppOp\nvINgCydCwV1FTbP5tuqYxuShVTAba1h9ZZmm\n-----END CERTIFICATE-----\n"
}
}
},
"peers": {
"173.193.112.109:17091": {
"url": "grpcs://173.193.112.109:17093",
"tlsCACerts": {
"pem": "-----BEGIN CERTIFICATE-----\nMIICIzCCAcqgAwIBAgIUbY5U1xnvvSqJ61CgeMp9/qu448owCgYIKoZIzj0EAwIw\nYzELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMRQwEgYDVQQK\nEwtIeXBlcmxlZGdlcjEPMA0GA1UECxMGRmFicmljMRQwEgYDVQQDEwtWb3RlckNB\nLXRsczAeFw0xOTA2MTQyMDQwMDBaFw0zNDA2MTAyMDQwMDBaMGMxCzAJBgNVBAYT\nAlVTMRcwFQYDVQQIEw5Ob3J0aCBDYXJvbGluYTEUMBIGA1UEChMLSHlwZXJsZWRn\nZXIxDzANBgNVBAsTBkZhYnJpYzEUMBIGA1UEAxMLVm90ZXJDQS10bHMwWTATBgcq\nhkjOPQIBBggqhkjOPQMBBwNCAASFv8sUAUfTvn8AJ/fiqrk0wdoMaKlG38nU6HZB\nkdUgFWZH9vnlTTBT77+GYRXuv78lg7ttus6DEAJE0X1xDd27o1wwWjAOBgNVHQ8B\nAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQUHuwEDf9d4vrv\nZM+qveoS9PV8/5cwFQYDVR0RBA4wDIcErcFwbYcECkzYYDAKBggqhkjOPQQDAgNH\nADBEAiBjynyKK+Bo4WX3wQII1nk2BU8OaYAuBvpTS/pPROdX+QIgSsLzKWuR7dFN\n50KrbM4ayRuaFBOFL88FflKxaRjQels=\n-----END CERTIFICATE-----\n"
},
"grpcOptions": {
"ssl-target-name-override": "173.193.112.109"
}
}
},
"certificateAuthorities": {
"173.193.112.109:7054": {
"url": "https://173.193.112.109:17090",
"caName": "ca",
"tlsCACerts": {
"pem": "-----BEGIN CERTIFICATE-----\r\nMIICezCCAeSgAwIBAgIJNQli68LG70HNMA0GCSqGSIb3DQEBBQUAMHUxGDAWBgNV\r\nBAMTDzE3My4xOTMuMTEyLjEwOTELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRo\r\nIENhcm9saW5hMRAwDgYDVQQHEwdSYWxlaWdoMQwwCgYDVQQKEwNJQk0xEzARBgNV\r\nBAsTCkJsb2NrY2hhaW4wHhcNMTkwNjE0MjA0NDM2WhcNMjAwNjEzMjA0NDM2WjB1\r\nMRgwFgYDVQQDEw8xNzMuMTkzLjExMi4xMDkxCzAJBgNVBAYTAlVTMRcwFQYDVQQI\r\nEw5Ob3J0aCBDYXJvbGluYTEQMA4GA1UEBxMHUmFsZWlnaDEMMAoGA1UEChMDSUJN\r\nMRMwEQYDVQQLEwpCbG9ja2NoYWluMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB\r\ngQCXKBfHLyQfavboQU0y/3S4jlqh6vQgXZKeAMliXfigf6aLG/3Oc4pxuQxBccB6\r\nAiYTFZdShTy2Usx5GsDf5PWxfD4vJ8FWzAGlIYmVqseKXXjsQvwMlCMyS9K2NaDo\r\n9tXwvHz8Sgncq7KccseVYwX4FFpSQWZsIV27Y2xkMZ9bVQIDAQABoxMwETAPBgNV\r\nHREECDAGhwStwXBtMA0GCSqGSIb3DQEBBQUAA4GBAG1+VZNSQdm0DX9CbZzx9zbx\r\nnTEHyrhVTgZs5YuUvZX8BErYfJFxuPBsXhOpQan/L9y+a7C/Caac4WJ/l/l34e5M\r\nG1Hn603wkHpR0UFuGCikdctm+6iHUVro5CMfQlEPIqaJSTFb7Ju5aeeerHnkvQx8\r\nBShP1pNsvsyOctmFhQCQ\r\n-----END CERTIFICATE-----\r\n"
}
}
}
}
In my project which is pretty much having the same things as you, I did not specified the orderers in my connection config file. I am not sure if it is required in your scenario.
Besides, I think the issue is caused by incorrect urls specified in your ibpconnection.jsonfile.
orderers should have "url": "grpcs://173.193.112.109:7050"
AND
peers should have "url": "grpcs://173.193.112.109:17091"
Hope this will fix the error. Good luck!!!!
I am new to pwa and service worker, I have done the adding the manifest.json and service worker file in app. It is also showing the popup for save to home screen,
But it is not working in offline mode.
offline (using dev tool in chrome) below console error message.
The FetchEvent for "https://www.iservefinancial.com/" resulted in a network error response: the promise was rejected.
Promise.then (async)
(anonymous) # sw.js:23
sw.js:1 Uncaught (in promise) TypeError: Failed to fetch
I have also tried by changing the different urls
by trying using absolute urls like '/public/images/iserve-logo-blue.png'
and also tried by giving direct urls like in code snippet.
it is loading the resources in offline in localhost but failed to load on live server.(using chrome dev tool for offline test).
Code of the service worker file
const BASE_URL = "https://www.iservefinancial.com/";
self.addEventListener('install', function (event) {
console.log('sw installed');
event.waitUntil(
caches.open('static').then(function (cache) {
cache.addAll([
BASE_URL,
BASE_URL + 'public/js/jquery-3.2.1.min.js',
BASE_URL + 'public/images/logos/iserve-logo-blue.png',
'https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css',
BASE_URL + 'public/css/fontawesomeak.css',
BASE_URL + 'public/css/questrial-font.css'
]);
})
);
});
self.addEventListener('activate', function () {
console.log('sw Activated');
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(function (res) {
if (res) {
return res
} else {
return fetch(event.request)
}
})
);
});
The below code i have included in PHP view File
<link rel="manifest" href="<?php echo $baseurl; ?>public/scripts/manifest.json">
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js', {
scope: '.'
}).then(function () {
console.log('Service Worker Registered');
});
}
</script>
Manifest.json
{
"name": "iServeFinancial.Com",
"short_name": "iServeFinancial",
"start_url": "https://www.iservefinancial.com/",
"display": "standalone",
"orientation": "portrait",
"background_color": "#fff",
"theme_color": "#1770a3",
"icons": [{
"src": "https://www.iservefinancial.com/public/images/logos/iserve-logo-blue.png",
"sizes": "120x36",
"type": "image/png"
},
{
"src": "https://www.iservefinancial.com/public/images/logos/progressive.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "https://www.iservefinancial.com/public/images/logos/progressive1.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "https://www.iservefinancial.com/public/images/logos/progressive2.png",
"sizes": "256x256",
"type": "image/png"
},
{
"src": "https://www.iservefinancial.com/public/images/logos/progressive2.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
The page should work in online as well as offline.
It seems your service worker is not in the same level with your root path.
Your sw.js is registered in /iserve/, and you try to cache js file in /public/js/.
Also you are using http://localhost/iserve/sw.js which I think is not the same domain as your live website, should be change into /iserve/sw.js or /sw.js depends on your response header.
If you want to serve your sw.js in different folder, you should add Service-Worker-Allowed:'/' in your response header.
Also here is the code how I cache my response to make web offline.
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.open(CURRENT_CACHES).then(function(cache) {
return fetch(event.request)
.then(function(response) {
cache.put(event.request, response.clone());
return response;
})
.catch(() =>
cache.match(event.request).then(function(response) {
return response || caches.match('/');
}),
);
}),
);
});