I am using node-ssh (in a firebase cloud function) to create an archive of a folder on one server and I want to send this archive to another server that is using keyboard interactive auth but I don't know how to manage this.
What I tried :
Using SSH scp command like so : scp -r my_backup.gz username#server-2.com:/backups-test/while being connected to the first server
=> But I guess (and hope) that the second server needs its password to accept the operation, and I don't know how to manage this as easily as I did with my first server with node-ssh (using tryKeyboard and onKeyboardInteractive)
Use async / await to first connect to the first server and then send the archive to the second server but everything I am referencing to in the then() of the second server's connection, belongs to the second server so I cannot ask to transfer the archive from the first server to the second one.
My code :
before implementing the connection to the second server :
var password = '***'
ssh.connect({
host: '***',
username: '***',
port: 22,
password,
tryKeyboard: true,
onKeyboardInteractive: (name, instructions, instructionsLang, prompts, finish) => {
if (prompts.length > 0 && prompts[0].prompt.toLowerCase().includes('password')) {
finish([password])
}
}
}).then((value) => {
await ssh.execCommand('touch my_backup.gz', {
cwd: '/home/directoryName'
}).then((result) => {
console.log('TOUCH OK');
if (result.stderr != '') console.log('TOUCH STDERR: ' + result.stderr);
}).catch((error) => {
ssh.dispose();
return response.status(401).send('error : ' + error);
});
await ssh.execCommand('tar --exclude=my_backup.gz -zcvf my_backup.gz .', {
cwd: '/home/directoryName'
}).then((result) => {
console.log('ARCHIVE CREATED !');
if (result.stderr != '') console.log('STDERR: ' + result.stderr);
}).catch((error) => {
ssh.dispose();
return response.status(401).send('error : ' + error);
});
ssh.dispose();
return response.status(200).send('ARCHIVE CREATED');
}).catch((error) => {
ssh.dispose();
return response.status(401).send('connection error : ' + error);
});
Thanks
I ended up using the FTP protocol via wget, simplifying the process.
Related
I'm trying to download a zip file with rn-fetch-blob, then when I got this file I unzip it with React-native-zip-archive.
It often works well, but sometimes, the "unzipFile()" function I've created can't unzip the file, like if it is corrupted.
Someone already got this problem ?
Here is my code :
downloadZipFile(res => {
unzipFile(res.path(), (boolean, path) => {
if (boolean !== false) {
db = SQLite.openDatabase({
name: "addb.sqlite",
location: "default",
createFromLocation: path
}).then(DB => {
db = DB;
db.transaction(tx => {
tx.executeSql(
"SELECT * FROM sqlite_master",
[],
(tx, results) => {
console.log("Logs sqlite_master");
const rows = results.rows;
for (let i = 0; i < rows.length; i++) {
console.log(_getCurrentDate());
datas.push({
...rows.item(i)
});
}
console.log(datas);
callback(true);
},
(tx, err) => {
console.log(err)
}
);
});
});
} else {
console.log("Can't create database");
callback(false);
}
});
});
And the functions I used :
export function downloadZipFile(callback) {
RNFetchBlob.config({
fileCache: true
})
.fetch(
"GET",
"MY LINK"
)
.then(res => {
console.log("The file saved to ", res.path());
callback(res);
})
.catch((errorMessage, statusCode) => {
// error handling
console.log(
"erreur : " + errorMessage + " and statuscode : " + statusCode
);
});
}
export function unzipFile(sourcePath, callback) {
const charset = "UTF-8";
const targetPath = "/data/user/0/com.myapp/databases/";
unzip(sourcePath, targetPath, charset)
.then(path => {
console.log(`unzip completed at ${path}`);
callback(true, path);
})
.catch(error => {
console.log("there is an error" + error);
callback(false, null);
});
}
Others informations :
The file is a database that I have to put in the "databases" folder's application. I tried to put a console.log(path) everywhere in the "unzipFile()" function to see if the file is really created when I try to unzip it, and it seems he is hereā¦ And when the file is impossible to unzip, it does the same size as the others which work.
rn-fetch-blob calls an api which copy an existant distant database and zip it as an axd file. Is there any problem with this format ? Can the api be the problem ?
The axd file created by the api is used by an existant application and seems to work correctly for the existant application. Moreover, when we download the file without rn-fetch-blob (by copying the link in my navigator), it works correctly everytime I tried.
I tried to download the file directly,the api always sent me the same file (a zip file or an axd file), and it works without problem (20 try). Can the problem be the delay to download the file ? With the api, it takes 5 or 6 seconds, without it takes 2 seconds. But I think my unzipFile() function only start when the file is downloaded, no ? And as I said, when I put a console.log(path) in the unzipFile() function, the file is here, with same size as others...
I Don't know how to make it works everytime, hope someone can help me :)
Ty !
I tried to put a for(let i = 1; i < 101; i++) to do the RNFB 100 times :
it works 97 times / 100 and 96 times /100...
Then I tried to put a timer, to be sure the file is finished to download, it works 3 times / 100...
And I deleted the timer, and now it never works anymore, or 5 times / 100...
I really Don't understand what is the problem :(
I need advise for handling errors in front-end of web application.
When I call a service to get the community according to community in web app, I want it to catch an error. For example for catching errors like 404.
There is a service for getting community according to id provided.
getCommunity(id: number) {
return this.http.get(`${this.api}/communities/` + id + ``);
}
that is called in events.ts file
setCommunityBaseUrl() {
this.listingService.getCommunity(environment.communityId).subscribe((data: any) => {
this.communityUrl = data.url + `/` + data.domain;
});
}
The id is provided in environment. Let's say there are 20 communities in total. When I provide id = 1 the events according to community = 1 appears.
export const environment = {
production: ..,
version: 'v2',
apiUrl: '...',
organization: '...',
websiteTitle: '...',
communityId: 1,
googleMapsApiKey: '...'
};
The problem is that when I provide id = null all community events are occurring | all events list in the backend is occurring.
Please, help ^^
When you subscribe you subscribe with an Observer pattern. So the first function you pass in
.subscribe(() => {} );
fires when the Observable calls .next(...)
and after that you can provide another function which will fire whenever the Observable calls .error(...)
so
.subscribe(() => {}, (error) => { handleTheError(error); } );
The this.http.get(...); returns an Observable which will fire the .error(...) on http error
We also know that this.http.get(...) completes or "errors" and it's not an endless one (a one that never completes). So you can make it a promise and manipulate on it promise like.
async getMeSomething(...) {
try {
this.mydata = await this.http.get(...).toPromise();
}
catch(error) {
handleTheError(error)
}
}
But what I really recommend is to use Swagger for your backend and then generate the API Client class with NSwagStudio so you don't have to write the client manually or adjust it or deal with error catching. I use it all the time and it saves us an enormous amount of time
Because you are using ".subscribe" you can create your own error handler and catch the errors like this, directly on the method.
This is an example on how you can use this:
constructor(
private _suiteAPIService: SuitesAPIService,
private _testcaseService: TestcaseService,
public _tfsApiService: TfsApiService,
private _notificationService: NotificationService) { }
errorHandler(error: HttpErrorResponse) {
return observableThrowError(error.message || "Server Error")
}
public something = "something";
GetTestcasesFromSuiteSubscriber(Project, BuildNumber, SuiteId) {
this._suiteAPIService.GetTestResults(Project, BuildNumber, SuiteId).subscribe(
data => {
console.log(data);
this._testcaseService.ListOfTestcases = data;
//Notofication service to get data.
this._notificationService.TestcasesLoaded();
},
error => {
//Here we write som error
return this.something;
}
);
}
I am building an Angular2 app and one of the components needs to make multiple API calls which are dependent on the previous ones.
I currently have a service which makes an API call to get a list of TV shows. For each show, I then need to call a different API multiple times to step through the structure to determine if the show exists on a Plex server.
The API documentation is here
For each show, I need to make the following calls and get the correct data to determine if it exists: (Assume we have variables <TVShow>, <Season>, <Episode>)
http://baseURL/library/sections/?X-Plex-Token=xyz will tell me:
title="TV Shows" key="2"
http://baseURL/library/sections/2/all?X-Plex-Token=xyz&title=<TVShow> will tell me: key="/library/metadata/2622/children"
http://baseURL/library/metadata/2622/children?X-Plex-Token=xyz will tell me: index="<Season>" key="/library/metadata/14365/children"
http://baseURL/library/metadata/14365/children?X-Plex-Token=xyz will tell me: index="<Episode>" which implies that the episode I have exists.
The responses are in json, I have removed a lot of the excess text. At each stage I need to check that the right fields exist (<TVShow>, <Season>, <Episode>) so that they can be used for the next call. If not, I need to return that the show does not exist. If it does, I will probably want to return an id for the show.
I have looked at lots of examples including promise, async & flatmap, but am not sure how to solve this based on the other examples I have seen.
How to chain Http calls in Angular2
Angular 2.0 And Http
Angular 2 - What to do when an Http request depends on result of another Http request
Angular 2 chained Http Get Requests with Iterable Array
nodejs async: multiple dependant HTTP API calls
How to gather the result of Web APIs on nodeJS with 'request' and 'async'
Here is what I have for getting the list of shows. (shows.service.ts)
export class ShowsHttpService {
getShows(): Observable<Show[]> {
let shows$ = this._http
.get(this._showHistoryUrl)
.map(mapShows)
.catch(this.handleError);
return shows$;
}
}
function mapShows(response:Response): Show[] {
return response.json().data.map(toShow);
}
function toShow(r:any): Show {
let show = <Show>({
episode: r.episode,
show_name: r.show_name,
season: r.season,
available : false, // I need to fill in this variable if the show is available when querying the Plex API mentioned above.
});
// My best guess is here would be the right spot to call the Plex API as we are dealing with a single show at a time at this point, but I cannot see how.
return show;
}
Here is the relevant code from the component (shows.component.ts)
public getShows():any {
this._ShowsHttpService
.getShows()
.subscribe(w => this.shows = w);
console.log(this.shows);
}
Bonus points
Here are the obvious next questions that are interesting, but not necessary:
The first API query will be much faster than waiting for all of the other queries to take place (4 queries * ~10 shows). Can the initial list be returned and then updated with the available status when it is ready.
The first Plex call to get the key="2" only needs to be performed once. It could be hard coded, but instead, can it be performmed once and remembered?
Is there a way to reduce the number of API calls? I can see that I could remove the show filter, and search through the results on the client, but this doesn't seam ideal either.
The 4 calls for each show must be done sequentially, but each show can be queried in parallel for speed. Is this achievable?
Any thoughts would be much appreciated!
Not sure if I totally understand your question, but here is what I do:
I make the first http call, then when the subscribe fires, it calls completeLogin. I could then fire another http call with its own complete function and repeat the chain.
Here is the component code. The user has filled in the login information and pressed login:
onSubmit() {
console.log(' in on submit');
this.localUser.email = this.loginForm.controls["email"].value;
this.localUser.password = this.loginForm.controls["password"].value;
this.loginMessage = "";
this.checkUserValidation();
}
checkUserValidation() {
this.loginService.getLoggedIn()
.subscribe(loggedIn => {
console.log("in logged in user validation")
if(loggedIn.error != null || loggedIn.error != undefined || loggedIn.error != "") {
this.loginMessage = loggedIn.error;
}
});
this.loginService.validateUser(this.localUser);
}
This calls the loginservice ValidateUser method
validateUser(localUser: LocalUser) {
this.errorMessage = "";
this.email.email = localUser.email;
var parm = "validate~~~" + localUser.email + "/"
var creds = JSON.stringify(this.email);
var headers = new Headers();
headers.append("content-type", this.constants.jsonContentType);
console.log("making call to validate");
this.http.post(this.constants.taskLocalUrl + parm, { headers: headers })
.map((response: Response) => {
console.log("json = " + response.json());
var res = response.json();
var result = <AdminResponseObject>response.json();
console.log(" result: " + result);
return result;
})
.subscribe(
aro => {
this.aro = aro
},
error => {
console.log("in error");
var errorObject = JSON.parse(error._body);
this.errorMessage = errorObject.error_description;
console.log(this.errorMessage);
},
() => this.completeValidateUser(localUser));
console.log("done with post");
}
completeValidateUser(localUser: LocalUser) {
if (this.aro != undefined) {
if (this.aro.errorMessage != null && this.aro.errorMessage != "") {
console.log("aro err " + this.aro.errorMessage);
this.setLoggedIn({ email: localUser.email, password: localUser.password, error: this.aro.errorMessage });
} else {
console.log("log in user");
this.loginUser(localUser);
}
} else {
this.router.navigate(['/verify']);
}
}
In my login service I make a call to the authorization service which returns an observable of token.
loginUser(localUser: LocalUser) {
this.auth.loginUser(localUser)
.subscribe(
token => {
console.log('token = ' + token)
this.token = token
},
error => {
var errorObject = JSON.parse(error._body);
this.errorMessage = errorObject.error_description;
console.log(this.errorMessage);
this.setLoggedIn({ email: "", password: "", error: this.errorMessage });
},
() => this.completeLogin(localUser));
}
In the authorization service:
loginUser(localUser: LocalUser): Observable<Token> {
var email = localUser.email;
var password = localUser.password;
var headers = new Headers();
headers.append("content-type", this.constants.formEncodedContentType);
var creds:string = this.constants.grantString + email + this.constants.passwordString + password;
return this.http.post(this.constants.tokenLocalUrl, creds, { headers: headers })
.map(res => res.json())
}
The point here in this code, is to first call the validateUser method of the login service, upon response, based on the return information, if its valid, I call the loginUser method on the login service. This chain could continue as long as you need it to. You can set class level variables to hold the information that you need in each method of the chain to make decisions on what to do next.
Notice also that you can subscribe to the return in the service and process it there, it doesn't have to return to the component.
Okay, Here goes:
public getShows():any {
this._ShowsHttpService
.getShows()
.subscribe(
w => this.shows = w,
error => this.errorMessage = error,
() => this.completeGetShows());
}
completeGetShow() {
//any logic here to deal with previous get;
this.http.get#2()
.subscribe(
w => this.??? = w),
error => this.error = error,
() => this.completeGet#2);
}
completeGet#2() {
//any logic here to deal with previous get;
this.http.get#3()
.subscribe(
w => this.??? = w),
error => this.error = error,
() => this.completeGet#3);
}
completeGet#3() {
//any logic here to deal with previous get;
//another http: call like above to infinity....
}
I want to be able to start an async task before the server starts listen to the port and gets requests,
my workaround was to invoke my task from the onListening function and when my task is done I'm binding the port to the address - but that workaround is ugly..
function onListening() {
var addr = server.address();
var bind;
// Following service gets the default plugins from the yamls and stores them in the DB.
defaultOperationsService.getOOTBOperations().then(function success() {
logger.info('Done getting default plugins operations!');
}, function err(err) {
logger.error('Error while trying to get operations' + err);
}).done(function done() {
bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
logger.debug('Listening on ' + bind);
});
}
I was introduced to a better way and probably the best way, call server.listen(port) when done:
defaultOperationsService.getOOTBOperations().then(function success(){
logger.info('Done getting default plugins operations!');
}, function err(err){
logger.error('Error while trying to get operations' + err);
}).done(function done(){
server.listen(port);
});
I need to use an in-house user management system to authenticate my users. This system also holds the user's membership to groups and roles and tenants which is most useful when doing the authorization stuff.
I looked at the code for accounts-persona but it does not work for me. Hence I deduce that I am doing something wrong.
On the server there is a new LoginHandler:
Meteor.startup( function () {
var config = Accounts.loginServiceConfiguration.findOne( {service: 'sertal'} );
if ( !config ) {
Accounts.loginServiceConfiguration.insert( { service: 'sertal' } );
}
} );
Accounts.registerLoginHandler( function ( options ) {
if ( !options.sertal && !options.assertion )
return undefined; // don't handle
var url = "http://dev.sertal.ch/checkCredential";
var request = {
params: {
uname: options.email,
pword: options.password
}
};
var result = Meteor.http.get( url, request );
if ( result.statusCode !== 200 ) {
throw new Meteor.Error( Accounts.LoginCancelledError.numericError, 'Sertal Login Failed' );
} else {
var user = result.data.userrec;
user.groups = result.data.grprec;
user.id = user._id;
return Accounts.updateOrCreateUserFromExternalService( 'sertal', user );
}
} );
On the client I use this code after the login button has been pressed:
Accounts.callLoginMethod( {
methodName: 'login',
methodArguments: {sertal: true,
email: $( '#sertal-email' ).val(),
password: $( '#sertal-password' ).val(),
resume: false
},
userCallback: function ( error ) {
if ( error ) {
console.log( "error: " + error );
} else {
$( "#sertalLoginFormDiv" ).dropdown( 'toggle' );
}
}
} );
But it does not trigger the LoginHandler. There must be something missing but I can't figure it out.
I could not find any documentation on the subject. An answer could also be to point out some documentation which explains the process.
Based on my testing, the methodArguments must be an array of objects.
Also, from what I see in the logs, if methodArguments object includes a password at the root of the object, then Meteor throws an error with "Failed Login { type: 'password',..."
I was able to make this work by putting all of the handler's arguments as part of an object.
Something like this, on the client:
loginRequest = {myLogin:{email: email, password: password}};
Accounts.callLoginMethod({
methodArguments: [loginRequest],
userCallback: callback
});
When executed on the client, meteor calls my server code:
Accounts.registerLoginHandler( function("someName", loginRequest{
if(loginRequest.myLogin){
// I get the loginRequestObject, and can attempt to sign in...
}
});
Note, I am using Meteor 1.0.