strongloop loopback how do I serve-static with a route? - node.js

I want to do something like
// server.js
app.use('/client', loopback.static(__dirname + '/../client'))
using middleware.json, but the example only works from the root
"files": {
"loopback#static": {
"params": "$!../client"
}
},

You have to use paths property, i.e.
"files": {
"loopback#static": {
"paths": "/client",
"params": "$!../client"
}
},
The detail is here.

I created a new file boot/routes.js
var path = require("path");
module.exports = function(app) {
app.get('/ping', function(req, res) {
res.sendFile(pt('client/index.html'));
});
};
function pt(relative) {
return path.resolve(__dirname, '../..', relative);
}

Did you try?
"files": {
"loopback#static": {
"params": "$!../../client"
}
}

Related

Index elastic problem to scroll with Typescript, and with node and Express

I use the Javascript client of elastic search to index and search my data and I have a problem to use the scroll method.
I can't set the right index but I know I have the right technique because in the DevTools console of Kibana I get the right result.
In DevTools I have the following queries that work (I execute the first request which is a search and then the second request which is a scroll by providing the scrollId returned by the first request):
GET my_index-*/_search?scroll=1m
{
"query": {
"bool": {
"filter": [{
"match": {
"field":"data"
}
},{
"range": {
"#timestamp": {
"gte": "2020-05-08T10:00:00.100Z",
"lte": "2022-05-11T10:00:00.200Z",
"format": "strict_date_optional_time"
}
}
}]
}
},
"_source": {
"includes": ["some_field1", "some_field2"]
},
"sort": [
{
"#timestamp": {
"order": "desc"
}
}
],
"size": 100
}
GET _search/scroll
{
"scroll": "1m",
"scroll_id":"DnF1ZXJ5VG ... NtUHdsQQ=="
}
These two requests return the expected result.
My backend is in Typescript and uses NodeJs with Express.
The first request, the search, works normally and returns me the right result. It is this code :
some import
...
const client = new Client({
node: configElasticClient.node
})
export const searchRouter = express.Router();
searchRouter.get('/search', async (req, res) => {
const response = await client.search<SearchResponse<HitResult>>({
index: "my_index-*",
scroll: "1m",
body: {
query: {
"bool": {
"filter": [{
"match": {
"field_to_search":"data_to_search"
}
},{
"range": {
"#timestamp": {
"gte": "2020-05-08T10:00:00.100Z",
"lte": "2022-05-11T10:00:00.200Z",
"format": "strict_date_optional_time"
}
}
}]
}
},
_source: ["some_field1", "some_field2"],
sort: [
{
"#timestamp": {
order: "desc"
}
}
],
size: 100
}
});
console.log("result: ", response);
res.json(response);
});
Then, when I make the scroll I should change my index to: "_search/scroll". But no matter what I try, when I try to access my route using scroll I get a "Cannot GET (the route)" error.
Here is the code of this second route (I put in comment the other way tried):
searchRouter.get('/search/scrollnext/:scrollId', async (req, res) => {
const response = await client.scroll<SearchResponse<HitResult>>({
scroll_id: req.params.scrollId,
scroll: "1m",
//index: "_search/scroll", wherever I put this field I get an error because the scroll method has no index field defined
});
//I also try this way :
//const response = await client.scroll<SearchResponse<HitResult>>({
// method: 'GET',
// body: {
// scroll_id: req.params.scrollId,
// scroll: "1m",
//}});
console.log("result: ", response);
res.json(response);
});
I get my scroll_id as a parameter from the first request which I fill in my second request in the scroll function.
I think that my problem comes from the scroll index which must be wrong. Does anyone know how to fix the index used by the scroll method?
Any help would be greatly appreciated :)
Finally find the answer, the syntax was good and it wasn't an index problem, I had to add an async management layer in my request like this:
searchRouter.get('/path/to/route/:scrollId', (asyncHandler(async (req, res) => {
const response = await client.scroll<YourDataType>({
scroll_id: req.params.scrollId,
scroll: "1m",
});
res.json(response);
})));
with asyncHandler looking something like this:
export function asyncHandler(fct: some_interface_to_define_request_caracteristic): RequestHandler {
return (req, res, next) => {
//Here add a try/catch layer
}
}

uploading files - nodejs with typescript and tsoa and swagger

I build a web api with nodejs and express. I used tsoa to generate my routes and a swagger.json file.
I want to be able to upload files via the swagger landing page. I followed the instructions on this page: https://tsoa-community.github.io/docs/file-upload.html
My tsoa.json looks like this:
{
"entryFile": "src/app.ts",
"noImplicitAdditionalProperties": "throw-on-extras",
"controllerPathGlobs": [
"src/**/*.controller.ts"
],
"spec": {
"basePath" : "/",
"description": "",
"outputDirectory": "dist/api/public",
"specVersion": 3,
"specMerging": "recursive",
"paths": {
"/admin/commands/create": {
"post": {
"consumes": [
"multipart/form-data"
],
"parameters": [
{
"in": "formData",
"name": "randomFileIsHere",
"required": true,
"type": "file"
}
]
}
}
}
},
"routes": {
"routesDir": "src/api"
}
}
My controller:
#Tags("Admin Commands")
#Route("admin/commands")
export class AdminCommandController extends Controller
{
#Post('create')
public async CreateCommand(#Request() request: express.Request): Promise<void>
{
try{
await this.handleFile(request);
}
catch {
throw new Error("Error uploading file.");
}
console.log("No error");
}
private handleFile(request: express.Request): Promise<any> {
const multerSingle = multer().single("randomFileIsHere");
return new Promise((resolve, reject) => {
multerSingle(request, undefined, async (error: Error) => {
if (error) {
reject("error");
}
resolve("file will be in request.randomFileIsHere");
});
});
}
}
but when I use the tsoa swagger command to generate the the swagger.json the multipart/form-data is not added to my swagger.json. If I manually add the multipart/form-data to my swagger.json the input field on my swagger landing page is a text field instead of a file field.
"paths": {
"/admin/commands/create": {
"post": {
"operationId": "CreateCommand",
"consumes": [
"multipart/form-data"
],
"parameters": [
{
"in": "formData",
"name": "randomFileIsHere",
"required": true,
"type": "file"
}
],
"responses": {
"204": {
"description": "No content"
}
},
"tags": [
"Admin Commands"
],
"security": []
}
}
}
What am I missing or doing wrong? Furthermore, if this would work, how do i extract the file from the request. The documentation says the file will be added to the randomFileIsHere property of the request object, but if i call request.randomFileIsHere it will obviously throw an error.
I also tried to follow official documents. I was able to save the uploaded file, but codes didnt looked clean to me. Then I realised there is a #UploadedFile decorator. You can add it and read file directly.
#Post("profile-photo")
public async uploadProfilePhoto(#UploadedFile() profilePhoto) {
console.log(profilePhoto)
}
Using a HTTP testing tool ,like Postman, define a field named "profilePhoto" and select a file. You should be able to see it on server terminal.
I think that you can do something like that:
import fs from 'fs';
#Post("profile-photo")
public async uploadProfilePhoto(#UploadedFile('formInputNameForPhoto') profilePhoto) {
fs.writeFileSync('./photo', profilePhoto.buffer);
}
Here is the answer which helped me understand how can I save "ready" multer file object.

Null value in model.findById when I make a get request [mongodb]

Problem
Hi dev,
I have the problem that when I try to make a get request to the series by id it shows me null.
I have noticed from the Atlas Mongos platform that I created the collection but it does not show me the data, only the structure of the scheme shows me
Function.js
const fs = require('fs');
const fetch = require('node-fetch');
const BASE_URL = " http://localhost:8081/api/v1/"
async function getSeries() {
return new Promise((resolve , reject) =>{
setTimeout(() => {
const res = require('./simple_database/series/1.json' , 'utf8');
resolve(res)
}, 1000);
})
}
module.exports = {
getSeries
}
Router
The route allseries allows me to access all the content. What I want to do is pass that content to the SeriesModel, maybe it is there where I have the problem that the data is not being inserted correctly.
In the route series/:id is where the null value is returning to me
const express = require('express');
const router = express.Router();
const f = require('./function');
const SeriesModel = require('./models/series');
router.get('/allseries', (req, res) => {
f.getSeries().then((series) =>{
res.status(200).json({
series
})
}).then((doc) =>{
SeriesModel.insertMany(doc , function(err , docs){
if(err){
console.error(err)
}else{
console.log(docs);
console.info('%d serie were successfully stored.', docs.length);
}
})
})
});
router.get('/series/:id' , (req , res , next) =>{
const id = req.params.id;
SeriesModel.findById(id)
.exec()
.then((doc) =>{
console.log("From database " , doc);
res.status(200).json(doc)
}).catch((err) =>{
console.error(err);
res.status(500).json({error: err})
})
})
module.exports = router;
Model/series.js
const mongoose = require('mongoose');
const serieSchema = mongoose.Schema({
"_id": {
"$oid": {
"type": "ObjectId"
}
},
"series_id": {
"type": "String"
},
"aggregateRating": {
"reviewCount": {
"type": "Number"
},
"ratingCount": {
"type": "Number"
},
"#type": {
"type": "String"
},
"ratingValue": {
"type": "Number"
}
},
"episodes": {
"1x": {
"07 Ghost": {
"type": [
"Mixed"
]
}
}
},
"metadata": {
"description": {
"type": "String"
},
"url": {
"type": "String"
},
"image": {
"type": "String"
},
"type": {
"type": "String"
},
"id": {
"type": "String"
},
"name": {
"type": "String"
}
},
"1x": {
"07 Ghost": {
"type": [
"Mixed"
]
}
}
});
module.exports = mongoose.model("cr_series" , serieSchema);
It is because findById takes it's parameter in form of object like this
SeriesModel.findById({_id:id})
You need to tell your query to which json object you want to match your incoming object.

How to print req.user.id in each log statement in node js using log4js

Currently, I am using express with log4js module like this, in any route file:
var log = require('log4jslogger.js').LOG;
log.info('this is log statement');
logger.js
var log4js = require('log4js');
log4js.configure(__base + "log4jsconfig.json");
var logger = log4js.getLogger('default');
Object.defineProperty(exports, "LOG", {
value : logger,
});
logs4jsconfig.json
{
"appenders": {
"out": {
"type": "stdout"
},
"default": {
"type": "dateFile",
"filename": "logs/default",
"pattern": "-yyyy-MM-dd.log",
"alwaysIncludePattern": true,
"keepFileExt": true
}
},
"categories": {
"default": {
"appenders": ["out",
"default"],
"level": "trace"
}
}
}
I want to add logged [username] or [unauthenticated] in all log statements. which I can get like req.user.id.
How can we do this instead of adding this to all log statements?
In log4js docs, I found a use of layout using a token but did not get clearly how to form AuthLibrary.currentUser()
const currentUser = require("./authLibrary").currentUser;
log4js.configure({
replaceConsole: true,
appenders: {
stdout: {
type: 'console',
layout: {
type: "pattern",
pattern: "%d %p %c %x{user} %m%n",
tokens: {
user: function (logEvent) {
return `[auth:${currentUser().UserName || '-'} / ${currentUser().UserId || '-'}]`;
},
},
},
}
},
categories: { default: { appenders: ['stdout'], level: 'info' }, },
});
// authLibrary.js
let user = {};
const setCurrentUser = (_user) => {
user = _user;
}
const currentUser = () => user;
module.exports = { currentUser, setCurrentUser };
// test
(async (err, req, res, next) => {
const token = req.get('token')
const user = await authService(token)
req.currentUser = user;
require("../utils/authLibrary").setCurrentUser(user);
})(null, { get: (x) => "1697E29ADE8DA9F6F84E214CF66E" }, null, null)

How to call Web3js function from Nodejs file

I have the following working web3js code, Calling App.createContract() on button click works well on a webpage, however I want to call App.createContract() or similar from another Nodejs controller. Infact what I am thinking of is making an API in Node which could call the web3js function and returns a JSON result back to the caller. Can you please help me how to import my web3js file and call the function from Node Controller? Thanks
import "../stylesheets/app.css";
import { default as Web3} from 'web3';
import { default as contract } from 'truffle-contract';
import { default as CryptoJS} from 'crypto-js';
var accounts;
var account;
var shLogABI;
var shLogContract;
var shLogCode;
var shLogSource;
window.App = {
start: function() {
var self = this;
web3.eth.getAccounts(function(err, accs) {
if (err != null) {
alert("There was an error fetching your accounts.");
return;
}
if (accs.length == 0) {
alert("Couldn't get any accounts! Make sure your Ethereum client is configured correctly.");
return;
}
accounts = accs;
console.log(accounts);
account = accounts[0];
web3.eth.defaultAccount= account;
shLogSource= "pragma solidity ^0.4.6; contract SHLog { struct LogData{ string FileName; uint UploadTimeStamp; string AttestationDate; } mapping(uint => LogData) Trail; uint8 TrailCount=0; function AddNewLog(string FileName, uint UploadTimeStamp, string AttestationDate) { LogData memory newLog; newLog.FileName = FileName; newLog.UploadTimeStamp= UploadTimeStamp; newLog.AttestationDate= AttestationDate; Trail[TrailCount] = newLog; TrailCount++; } function GetTrailCount() returns(uint8){ return TrailCount; } function GetLog(uint8 TrailNo) returns (string,uint,string) { return (Trail[TrailNo].FileName, Trail[TrailNo].UploadTimeStamp, Trail[TrailNo].AttestationDate); } }";
web3.eth.compile.solidity(shLogSource, function(error, shLogCompiled){
shLogABI = JSON.parse(' [ { "constant": false, "inputs": [ { "name": "TrailNo", "type": "uint8" } ], "name": "GetLog", "outputs": [ { "name": "", "type": "string" }, { "name": "", "type": "uint256" }, { "name": "", "type": "string" } ], "payable": false, "type": "function" }, { "constant": false, "inputs": [ { "name": "FileName", "type": "string" }, { "name": "UploadTimeStamp", "type": "uint256" }, { "name": "AttestationDate", "type": "string" } ], "name": "AddNewLog", "outputs": [], "payable": false, "type": "function" }, { "constant": false, "inputs": [], "name": "GetTrailCount", "outputs": [ { "name": "", "type": "uint8" } ], "payable": false, "type": "function" } ]');
shLogContract = web3.eth.contract(shLogABI);
});
});
},
createContract: function()
{
shLogContract.new("", {from:account, gas: 3000000}, function (error, deployedContract){
if(deployedContract.address)
{
document.getElementById("contractAddress").value=deployedContract.address;
document.getElementById("fileName").value = '';
document.getElementById("uploadTimeStamp").value = '';
document.getElementById("attestationDate").value = '';
}
})
},
addNewLog: function()
{
var contractAddress = document.getElementById("contractAddress").value;
var deployedshLog = shLogContract.at(contractAddress);
var fileName = document.getElementById("fileName").value;
var uploadTimeStamp = document.getElementById("uploadTimeStamp").value;
var attestationDate = document.getElementById("attestationDate").value;
deployedshLog.AddNewLog(fileName, uploadTimeStamp, attestationDate, function(error){
console.log(error);
})
},
getLog: function()
{
try{
var contractAddress = document.getElementById("contractAddress").value;
var deployedshLog = shLogContract.at(contractAddress);
deployedshLog.GetTrailCount.call(function (error, trailCount){
deployedshLog.GetLog.call(trailCount-1, function(error, returnValues){
document.getElementById("fileName").value= returnValues[0];
document.getElementById("uploadTimeStamp").value = returnValues[1];
document.getElementById("attestationDate").value=returnValues[2];
})
})
}
catch (error) {
document.getElementById("fileName").value= error.message;
document.getElementById("uploadTimeStamp").value = error.message;
document.getElementById("attestationDate").value= error.message;
}
}
};
window.addEventListener('load', function() {
if (typeof web3 !== 'undefined') {
console.warn("Using web3 detected from external source. If using MetaMask, see the following link. Feel free to delete this warning. :) http://truffleframework.com/tutorials/truffle-and-metamask")
window.web3 = new Web3(web3.currentProvider);
} else {
console.warn("No web3 detected. Falling back to http://localhost:8545. You should remove this fallback when you deploy live, as it's inherently insecure. Consider switching to Metamask for development. More info here: http://truffleframework.com/tutorials/truffle-and-metamask");
// fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail)
window.web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}
App.start();
});
Simple example for web3js 1.0.0.beta*
1) Add web3js to your package.json on a server side:
"web3": "^1.0.0-beta.27"
2) Require and init web3 with some provider:
const Web3 = require('web3');
const web3SocketProvider = new Web3.providers.WebsocketProvider('ws://0.0.0.0:8546');
const web3Obj = new Web3(web3Provider);
Now you can use your web3 object as usual:
async function getAccounts() {
let accounts = await web3Obj.eth.getAccounts();
}

Resources