Currently we are using Polymer for frontend. In polymer we have element “vaadin-upload”.
Issue: We are not able to fire/trace node js code. When we try to upload image then it throws POST 404 not found error.
Please check below code of Polymer and NodeJS.
Polymer:
<vaadin-upload target="http://localhost:5000/upload" method="POST" timeout="300000" headers="{'X-Custom-Header': 'value'}"></vaadin-upload>
Node Js:
var server = new Hapi.Server();
server.route({
method: 'POST',
path: '/upload',
config: {
payload:{
maxBytes:209715200,
output:'stream',
parse: false
},
handler: function (request, reply) {
var data = request.payload;
if (data.file) {
var name = data.file.hapi.filename;
var path = __dirname + "/upload/" + name;
var file = fs.createWriteStream(path);
file.on('error', function (err) {
console.error(err)
});
data.file.pipe(file);
data.file.on('end', function (err) {
var ret = {
filename: data.file.hapi.filename,
headers: data.file.hapi.headers
}
reply(JSON.stringify(ret));
})
}
}
}
});
After looking at the hapijs tutorial it looks like you need to define the port after you create the server instance.
server.connection({ port: 5000 });
Hapi will save the file to the directory for you so you don't have to handle it.
const server = new Hapi.Server();
server.connection({
port: 5000
});
server.start(function () {
console.log('server running at: ' + server.info.uri);
});
const PATH_TO_UPLOADED_FILES = '...';
server.route({
method: 'POST',
path: '/upload',
config: {
payload: {
output: 'file',
uploads: PATH_TO_UPLOADED_FILES,
parse: true,
maxBytes: 209715200
},
handler: function (request, reply) {
// file path is at: request.payload.file.path
reply('thanks');
}
}
});
Related
I built a program where a user can send request with a PDF URL then sever download it and forward into an external API endpoint. Now, the code able to download file but it hit this error when it start to read the file.
I must admit that Promises is something I hate to learn hence I used Async Function with Awaits and in other cases I used normal functions. Promises is so hard to grasp. The syntax make it hard to read.
Code is below:
const fs = require('fs');
const url = require("url");
const path = require("path");
const http = require('http')
const rp = require('request-promise');
const app = express();
const port = 8999;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.post('/upload-invoice', (req, res) => {
var parsed = url.parse(req.body.FileURL);
var filename = (path.basename(parsed.pathname));
var downloaded_file_path = `invoices/${filename}`;
function download() {
var options = {
hostname: parsed.hostname,
port: 2799,
path: parsed.path,
method: 'GET'
}
const file = fs.createWriteStream(`invoices/${filename}`);
const make_request = http.request(options, (response) => {
response.pipe(file);
});
make_request.end();
try {
setTimeout(function () {
upload()
}, 1000);
} catch (error) {
console.log('An Error occured when uploading file,'+error);
}
}
async function upload() {
const flow = "Upload Invoice"
var file_stream = fs.createReadStream(downloaded_file_path)
var options = {
method: 'POST',
strictSSL: true,
uri: 'https://endpoint.goes.here',
formData: {
'file': file_stream
},
headers: {
'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundaryzte28ISYHOkrmyQT'
},
json: req.body,
resolveWithFullResponse: true
}
try {
var response = await rp(options)
res.send(response.body)
}
catch (error) {
console.log(`An error on ${flow} flow for unknown user. Here is more info about error,
${error}
`)
res.send("Error")
}
}
download()
});
app.listen(port)
Update:
formData: {
name: filename,
file: {
value: fs.createReadStream(downloaded_file_path),
options: {
filename: filename,
contentType: 'application/pdf'
}
}
}
I've tried this code too but it output same error.
It works after removing json: req.body
My fault.
I'm trying a basic sample to authenticate a request using Hawk scheme and Hapi but the hawk plugin fails because it is trying to access a payload property that does not exist:
Error:
Server started listening on http://localhost:3000
Debug: internal, implementation, error
TypeError: Cannot read property 'payload' of undefined
at Object.authenticate (D:\TEST\node\sample3\node_modules\#hapi\hawk\lib\plugin.js:45:45)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:189:7)
The relevant Hawk plugin code where the error is generated:
...
if (request.route.settings.auth.payload) {
request.events.once('peek', (chunk) => {
...
Server code:
const Hapi = require('#hapi/hapi');
const Hawk = require('#hapi/hawk');
const credentials = {
John: {
key: 'secret',
algorithm: 'sha256'
}
};
const getCredentialsFunc = function (id) {
return credentials[id];
};
const start = async () => {
const server = Hapi.server({ port: 3000, host: 'localhost' });
await server.register(Hawk);
server.auth.strategy('default', 'hawk', { getCredentialsFunc });
server.auth.default('default');
server.route({
method: 'GET',
path: '/',
handler: function (request, h) {
return 'Welcome';
}
});
await server.start();
console.log('Server started listening on %s', server.info.uri);
};
start();
Client code:
const Request = require('request');
const Hawk = require('#hapi/hawk');
const credentials = {
id: 'John',
key: 'secret',
algorithm: 'sha256'
};
const requestOptions = {
uri: 'http://localhost:3000/',
method: 'GET',
headers: {}
};
const { header } = Hawk.client.header(requestOptions.uri, requestOptions.method, { credentials: credentials, ext: 'some-app-data' });
requestOptions.headers.Authorization = header;
Request(requestOptions, function (error, response, body) {
const isValid = Hawk.client.authenticate(response, credentials, header.artifacts, { payload: body });
console.log(`${response.statusCode}: ${body}` + (isValid ? ' (valid)' : ' (invalid)'));
});
I created a PR for this exact problem :)
https://github.com/hapijs/hawk/pull/259
How to include small icon and big icon url while creating a push notification in node js, I know that we need to send image url while sending push notification, i need code sample in node js.. I have this code..
sendNotificationToSpecific =function (token,messages) {
var sendNotification = function (data) {
var headers = {
"Content-Type": "application/json; charset=utf-8",
"Authorization": "Basic MDNlMTNjYWMTgy"
};
var options = {
host: "onesignal.com",
port: 443,
path: "/api/v1/notifications",
method: "POST",
headers: headers
};
var https = require('https');
var req = https.request(options, function (res) {
res.on('data', function (data) {
console.log("Response:");
console.log(JSON.parse(data));
});
});
req.on('error', function (e) {
console.log("ERROR:");
console.log(e);
});
req.write(JSON.stringify(data));
req.end();
};
var message = {
app_id: "awer342-d744-4787-b59a-f55c6215c491",
contents: {"en": messages},
include_player_ids: [token],
};
sendNotification(message);
};
As you can see in the docs https://documentation.onesignal.com/reference#section-appearance you can simply extend your message object like
var message = {
app_id: "xxxx",
contents: {"en": messages},
include_player_ids: [token],
small_icon: "resource_name", // can not be an url
large_icon: "http://url/ or resource_name"
}
I am trying to get node/express to send soap request to indesign server.
Posting the request from Soap.ui or Postman works fine. Loading the "soap" page in the browser errors.
I also tried the node client with a few sample scripts from the new and they work, so the install should be OK.
This is what I have so far:
router.get('/soap', function(req, res, next) {
var url = 'http://<server_ip>:8088/service?wsdl';
var args = { "IDSP:RunScriptParameters" :
{ 'scriptLanguage': 'javascript',
'scriptFile': 'C:/indesign_scripts/test.jsx'
}
};
soap.createClient(url, function(err, client){
client.Service.Service.RunScript(args, function(err, result) {
if (err) console.log(err);
console.log(result);
});
});
client.describe() returns:
{ Service:
{ Service:
{ RunScript: [Object],
BeginSession: [Object],
EndSession: [Object] } } }
I am trying to use RunScript object.
client.describe().Service.Service.RunScript:
{ input:
{ runScriptParameters:
{ scriptText: 'xsd:string',
scriptLanguage: 'xsd:string',
scriptFile: 'xsd:string',
'scriptArgs[]': [Object],
targetNSAlias: 'IDSP',
targetNamespace: 'http://ns.adobe.com/InDesign/soap/' } },
output:
{ errorNumber: 'xsd:int',
errorString: 'xsd:string',
scriptResult:
{ data: 'xsd:anyType',
targetNSAlias: 'IDSP',
targetNamespace: 'http://ns.adobe.com/InDesign/soap/' } } }
Console shows this error:
[Error: connect ECONNREFUSED 127.0.0.1:8088]
code: 'ECONNREFUSED',
errno: 'ECONNREFUSED',
syscall: 'connect',
address: '127.0.0.1',
port: 8088 }
Indesign Server wsdl could be viewed here:
https://gist.github.com/tomtaylor/1034317
I suspect this is something with args variable format.
You can fix this issue by adding line below
client.setEndpoint('http://<server_ip>:8088');
I tried to add "Access-Control-Allow-Origin" to my headers in my app file, as suggested by #Chirdeep Tomar, but I am still getting the same errors.
The workaround I came up with was to use http request or curl for ajax post.
The example with request:
var express = require('express');
var request = require('request');
var parser = require('xml2json');
var router = express.Router();
router.get('/ProductionBooks/:id', function(req, res) {
var myId = req.params.id;
var myBody = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://ns.adobe.com/InDesign/soap/"><soapenv:Body>'
+'<soap:RunScript>'
+'<runScriptParameters>'
+'<scriptLanguage>javascript</scriptLanguage>'
+'<scriptFile>C:/indesign_scripts/test.jsx</scriptFile>'
+'</runScriptParameters>'
+'</soap:RunScript>'
+'</soapenv:Body>'
+'</soapenv:Envelope>';
request({
url: 'http://192.168.0.129:8088', //URL to hit
method: 'POST',
headers: {
'Content-Type': 'application/xml',
'Content-Length': Buffer.byteLength(myBody)
},
body: myBody
}, function(error, response, body){
if(error) {
console.log(error);
} else {
console.log(response.statusCode + '\n');
var objJSON = JSON.parse(parser.toJson(body));
console.log(objJSON['SOAP-ENV:Envelope']['SOAP-ENV:Body']['IDSP:RunScriptResponse'].errorNumber);
}
});
res.end();
});
Example with curl:
var curl = require("curl");
curl.post(url, soapBody, options, function(err, response, body) {
try {
console.log(response.body);
}
catch (err) {
console.log(err);
}
});
res.end();
});
using easy-soap-request:
const soapRequest = require('easy-soap-request');
const url = 'http://[YOUR SERVER ADDRESS]:[IDS PORT]/service?wsdl';
const sampleHeaders = {
'user-agent': 'sampleTest',
'Content-Type': 'text/xml;charset=UTF-8',
};
const xml = '<?xml version="1.0" encoding="UTF-8"?>'
+'<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:IDSP="http://ns.adobe.com/InDesign/soap/">'
+'<SOAP-ENV:Body><IDSP:RunScript><IDSP:runScriptParameters>'
+'<IDSP:scriptLanguage>javascript</IDSP:scriptLanguage>'
+'<IDSP:scriptFile>[PATH TO SCRIPTS]/Hello.jsx</IDSP:scriptFile>'
+'<IDSP:scriptArgs><IDSP:name>myArg</IDSP:name><IDSP:value>Test Value</IDSP:value></IDSP:scriptArgs>'
+'</IDSP:runScriptParameters></IDSP:RunScript>'
+'<IDSP:RunScriptResponse><errorNumber>0</errorNumber><scriptResult><data xsi:type="IDSP:List"><item><data xsi:type="xsd:string">1</data></item></data></scriptResult></IDSP:RunScriptResponse>'
+'</SOAP-ENV:Body></SOAP-ENV:Envelope>';
// usage of module
(async () => {
const { response } = await soapRequest({ url: url, headers: sampleHeaders, xml: xml, timeout: 1000 }); // Optional timeout parameter(milliseconds)
const { headers, body, statusCode } = response;
console.log(headers);
console.log(body);
console.log(statusCode);
})();
Sample script (hello.jsx):
var arg = app.scriptArgs.get("myArg");
var res = "Your input: " + arg;
res;
I'm writing an API on HapiJS, and wondering how to get a global prefix. For example, all requests should be made to:
https://api.mysite.com/v0/...
So I'd like to configure v0 as a global prefix. The docs (here) don't seem to mention it -- is there a good way to do this in HapiJS?
If you put your API routing logic inside a Hapi plugin, say ./api.js:
exports.register = function (server, options, next) {
server.route({
method: 'GET',
path: '/hello',
handler: function (request, reply) {
reply('World');
}
});
next();
};
exports.register.attributes = {
name: 'api',
version: '0.0.0'
};
You register the plugin with a server and pass an optional route prefix, which will be prepended to all your routes inside the plugin:
var Hapi = require('hapi');
var server = new Hapi.Server()
server.connection({
port: 3000
});
server.register({
register: require('./api.js')
}, {
routes: {
prefix: '/v0'
}
},
function(err) {
if (err) {
throw err;
}
server.start(function() {
console.log('Server running on', server.info.uri)
})
});
You can verify this works by starting the server and visiting http://localhost:3000/v0/hello.
I was able to get it working for all routes with
var server = new Hapi.Server()
...
server.realm.modifiers.route.prefix = '/v0'
server.route(...)
Matt Harrisson's answer is the hapi way to do it using plugins.
Alternatively if you don't want to create a plugin just to add a prefix, you can by hand, add the prefix to all your routes.
For instance I went for something like this:
var PREFIX = '/v0';
var routes = [/* array with all your root */];
var prefixize = function (route) { route.path = PREFIX + route.path;return route; }
server.route(routes.map(prefixize));
Good point is that with something like this your can perform express-like mounting. ex:
var prefixize = function (prefix, route) { route.path = prefix + route.path;return route; }
server.route(adminRoutes.map(prefixize.bind({}, "/admin"))); //currying.
server.route(apiRoutes.map(prefixize.bind({}, "/api")));
For Hapi 19, 20 ... you can simply modify the route with map path before you register it.
// Example route
const routes = [
{
method: 'GET',
path: '/test',
handler: function (request) {
return {
status: 'success'
};
}
}
];
// transform /test -> /api/test
routes.map((r) => {
r.path = `/api${r.path}`;
return null;
});
// Register
server.route([
...routes
]);
you can always start your index.js like this
if (!global.PREFIX) {
global.PREFIX = '/v0';
}
this way everywhere inside your code you'll have access to PREFIX
that's how you can access to PREFIX
console.log(PREFIX); or var name = PREFIX+ "_somename";
Take a look at hapi-auto-route. This plugin automaticaly register your routes from a directory
// Directory structure
//
// node_modules/
// routes/
// home.js
// server.js
// package.json
// routes/home.js
'use strict';
module.exports = {
method: 'GET',
path: '/',
handler: (request, h) => 'Hello';
}
// server.js
'use strict';
const Hapi = require('hapi');
const server = Hapi.Server({
port: 3000,
host: 'localhost'
});
const init = async () => {
await server.register(require('hapi-auto-route'));
await server.start();
console.log(`Server is running at: ${server.info.uri}`);
};
process.on('unhandledRejection', (error) => {
console.log(error);
process.exit();
});
init()
and add prefix to it:
I’m late to this party but this came up in search results.. FWIW, I'm using this, built off of AdrieanKhisbe’s answer. It allows for setting multiple global prefixes and using sub-route prefixes (similar to how Django urls are laid out). Here is a sample with multiple route.js files and api route versions (the route handlers moved out for clarity):
/departments/routes.js
const { getDepartments, getDepartmentById } = require('./handlers');
module.exports = [
{ method: 'GET', path: '', handler: getDepartments },
{ method: 'GET', path: '/{id}', handler: getDepartmentById }
];
/users/routes.js
const { getUsersV1, getUserByIdV1, getUsersV2, getUserByIdV2 } = require('./handlers');
const userRoutesV1 = [
{ method: 'GET', path: '', handler: getUsersV1 },
{ method: 'GET', path: '/{id}', handler: getUserByIdV1 }
];
const userRoutesV2 = [
{ method: 'GET', path: '', handler: getUsersV2 },
{ method: 'GET', path: '/{id}', handler: getUserByIdV2 }
];
module.exports = { userRoutesV1, userRoutesV2 };
index.js
const Hapi = require('#hapi/hapi');
const departmentRoutes = require('./departments/routes');
const { userRoutesV1, userRoutesV2 } = require('./users/routes');
const init = async () => {
const server = Hapi.server({
port: 3000,
host: 'localhost',
});
const allRoutes = [];
const v1 = '/api/v1/';
const v2 = '/api/v2/';
const prefixer = (routeArray, apiPrefix, subRoutePrefix) => {
routeArray.map(route => {
route.path = `${apiPrefix}${subRoutePrefix}${route.path}`;
allRoutes.push(route);
});
};
prefixer(departmentRoutes, v1, 'departments');
prefixer(userRoutesV1, v1, 'users');
prefixer(userRoutesV2, v2, 'users');
server.route(allRoutes);
await server.start();
console.log('Server running on %s', server.info.uri);
};
process.on('unhandledRejection', err => {
console.log(err);
process.exit(1);
});
init();
Here is how I implemented mine
I created a helper function that takes an Array of Hapi.ServerRoute then map through it and concatenate the prefix then return the array.
The snippets are in Typescript so if you're using JavaScript just strip off the types
// Helper function
export function routerGroup (namespace: string, routes: Hapi.ServerRoute[]) {
return routes.map(r => {
r.path = namespace + r.path
return r
})
}
// Routes declarations
export default routerGroup('/v1/api', [
{
method: 'POST',
path: '/login',
options: {
validate: {
payload: Joi.object({
email: Joi.string().required().email(),
password: Joi.string().required().min(8).max(30)
})
},
auth: false
},
handler: Authentication.adminLogin
}
] as Hapi.ServerRoute[]
)
// Register routes to Hapi server
server.route(
[
...v1Routes,
...
]
)
server.realm.modifiers.route.prefix = '/api/v2'
await server.route(yourroutes);
This should work fine, however if you want to be able to parse all the routes automatically from your routes directory/file Hapi Router. You would be able to do something like this which will save you a lot of time.
await server.register({
plugin: HapiRouter,
options: {
routes: "./src/routes/product-routes.js",
},
}, {
routes: {
prefix: "/api/v1"
}
});
Your route file should look like this.
export default [{
method: "GET",
path: "/products",
options: {
tags: ["api", "Products"],
description: "Get All Products",
},
handler: () => {...}
}]