I am new in meteor js and web app is created in meteor. I need to create API's for mobile app and native and web app
will share the same database. This is not clear to me from where I need to start to create API
for the native app? This is my login route which I am using for the web app.
Path of web app login route
socialapp\socialappv1\app\lib\routes.js
Router.route('login', {
name: 'login',
controller: 'LoginController',
where: 'client'
});
and to create API I have created a server.js file in socialapp\socialappv1\app\server\ directory and I am trying to create API to register a user.
Router.route('/register/',{where: 'server'})
.post(function(){
//console.log(this.request.body);
//return false;
let user = {
email : this.request.body.email,
username : this.request.body.username,
password : this.request.body.password,
};
});
const userId = Accounts.createUser(user);
if(userId)
{
console.log("Register");
}else{
console.log("Not Register");
}
});
Is there any other way to create rest API e.g. to call controllers or is this correct to start API?
I think your code may be trying to set up client side routes (not sure which router you are using).
You need to add server side routes (and you can use express for this), and the handler needs to attach to the Meteor environment
This is some code I have written to handle payment confirmations coming to the server: (server side code of course)
import { Meteor } from 'meteor/meteor'
import express from 'express'
import bodyParser from 'body-parser'
const debug = require('debug')('b2b:server-payments')
async function acceptPayment(req, res) {
// We need to bind to the Meteor environment for this to work.
Meteor.bindEnvironment(() => {
debug('/payment hook', req.body)
try {
// Handle the data that's in req.body ...
:
:
} catch (e) {
console.error(e)
}
})()
res.status(200).json({ status: 'ok' }) // Change this if your data isn't JSON
}
export function setupPaymentsApi() {
debug('Setting up payment hooks')
const app = express()
app.use(bodyParser.json({ extended: false }))
app.post('/payment', acceptPayment)
app.get('/api', (req, res) => {
res.status(200).json({ message: 'B2B Payments API' }) // Shouldn't call this, just for testing for now
})
WebApp.connectHandlers.use(app)
}
Related
I'm trying to make users profiles dynamic in appwrite app. I want each user profile page to be accessible to all users so it goes like this (www.appname.com/users/{userid}).
I'm very new to node JS but i managed to install appwrite SDK for node and created a seperate folder for node and when i run the below code in node it gets me the user as expected in the terminal.
const sdk = require("node-appwrite");
// Init SDK
let client = new sdk.Client();
let users = new sdk.Users(client);
client
.setEndpoint("http://localhost/v1") // Your API Endpoint
.setProject("myProjectId") // Your project ID
.setKey(
"mykey"
); // Your secret API key
let promise = users.get("myUserId");
promise.then(
function (response) {
console.log(response);
},
function (error) {
console.log(error);
}
);
But I want to be able to use Vuejs to call out this outcome! I want to be able to use (users.get) from Vue component. How can I make this happen?
here is what I have tried till now:
I have created UserService.js file and added the below function to grab users.get from node Js
import users from "../../../api/server";
export async function getUser(userId) {
let promise = users.get(userId);
promise.then(
function (response) {
console.log(response);
},
function (error) {
console.log(error);
}
);
}
And I called it from my VueJS component
<script>
import { getUser } from "../../services/UserService";
export default {
name: "Profile",
props: ["id"],
data() {
return {
userprfile: false,
};
},
mounted() {
this.getUser();
},
methods: {
getUser() {
getUser(this.id).then((response) => {
console.log(response);
});
},
},
};
</script>
But it doesn't work
All I want is a way that allows me to use appwrite nodeJS SDK in my vueJS component. I need to be able to pass it the userID and get back the user in VueJS component
UPDATE:
The below code works and I can get now retrieve the data from appwrite NodeJS SDK to my browser but the problem is that I want this to be dynamic. I need a way to pass on UserID from vue to NodeJS sdk and retrieve the data.
const express = require("express");
const path = require("path");
const app = express(),
bodyParser = require("body-parser");
port = 3080;
// place holder for the data
const sdk = require("node-appwrite");
// Init SDK
let client = new sdk.Client();
let users = new sdk.Users(client);
client
.setEndpoint("http://localhost/v1") // Your API Endpoint
.setProject("myProjectID") // Your project ID
.setKey(
"MySecretApiKey"
); // Your secret API key
app.use(bodyParser.json());
app.use(express.static(path.join(__dirname, "../appwrite-app/build")));
app.get("/v1/users", (req, res) => {
console.log("api/users called!");
let promise = users.get("userId");
promise.then(
function (response) {
res.json(response);
},
function (error) {
console.log(error);
}
);
});
app.listen(port, () => {
console.log(`Server listening on the port::${port}`);
});
It looks like you are trying to use a node only module on the client (browser). You cannot use any module on the client that uses native node modules - in this case fs.
So what you need to do from your frontend application is send a request to your server application (API). On the API do any file system/database retrieval, then send the results back to the client.
It's very common to write the backend/frontend as separate applications - in separate folders and even store in separate repositories.
You should also never expose any secret keys on the client.
There may also be some confusion about the term 'client'. Most of the time it's used to refer to an application run in a web browser but you also get node sdk's which are 'clients' of the services they use - like node-appwrite in your case.
i am trying to lock up my swagger documentation, i am using express basic auth for this, when i try to access the documentation endpoint it just throws a HTTP ERROR 401 without asking me to provide a username and password.
app.use("/api-docs",basicAuth({
challenge: true,
users: { 'me': 'openforme' } }),
swaggerUi.serve,
swaggerUi.setup(yaml.parse(swaggerfile))
);
when i remove the basicauth middleware i can access the endpoint, what i want is a popup to into the username and password.
use the Nub-auth package
const express = require('express');
const nubAuth = require('nub-auth');
app = express();
app.use(['/v1'], nubAuth({
challenge: true,
users: { 'admins': 'admin', 'users': 'user', 'guest': 'guested' }
}));
app.get('/api', (req, res) => {
res.send('Hello World welcome to API endpoints free !');
});
app.get("/v1", (req, res) => {
res.send("Hello World welcome to API endpoints closed !");
});
from my investigation edge does not have the basic auth feature enabled.
I am logging users in via their domain Google accounts using passport.js. This works great, but now I need to give this application access to a few Google API's (drive, sheets, etc).
When a user logs in, a message appears in the logs, that makes it seem like passport has all the required info:
info: [06/Jun/2019:21:24:37 +0000] "302 GET /auth/callback?code=** USER ACCESS TOKEN HERE **&scope=email%20profile%20https://www.googleapis.com/auth/drive.file%20https://www.googleapis.com/auth/spreadsheets%20https://www.googleapis.com/auth/userinfo.email%20https://www.googleapis.com/auth/userinfo.profile%20https://www.googleapis.com/auth/drive HTTP/1.1" [46]
This is achieved by passing the appended scopes via passport.authenticate(), which presents the user with the "Grant access to these things on your Google account to this app?" screen :
//Initial auth call to Google
router.get('/',
passport.authenticate('google', {
hd: 'edmonds.wednet.edu',
scope: [
'email',
'profile',
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/spreadsheets'
],
prompt: 'select_account'
})
);
However, when I go and try to call an API with something like:
const {google} = require('googleapis');
const sheets = google.sheets({version: 'v4', auth});
router.post('/gsCreate', function(req,res,next){
sheets.spreadsheets.create({
// Details here.....
});
});
I get nothing but errors (the current one is debug: authClient.request is not a function)
My question is: Is it possible for me to use a setup like this, asking the user to log in and grant permissions once, and then somehow save that to their user session via passport?
I had the same question, but I was able to access Google Gmail API functionalities along with Passport.js user authentication by specifying 'scopes' using the following process.
First, create a file to setup the passport-google-strategy in nodejs as follows.
passport_setup.js
const passport = require('passport')
const GoogleStrategy = require('passport-google-oauth20')
const fs = require("fs");
const path = require('path');
//make OAuth2 Credentials file using Google Developer console and download it(credentials.json)
//replace the 'web' using 'installed' in the file downloaded
var pathToJson = path.resolve(__dirname, './credentials.json');
const config = JSON.parse(fs.readFileSync(pathToJson));
passport.serializeUser((user, done) => {
done(null, user.id)
})
passport.deserializeUser((id, done) => {
const query = { _id: id }
Users.findOne(query, (err, user) => {
if (err) {
res.status(500).json(err);
} else {
done(null, user)
}
})
})
//create a google startergy including following details
passport.use(
new GoogleStrategy({
clientID: config.installed.client_id,
clientSecret: config.installed.client_secret,
callbackURL: config.installed.redirect_uris[0]
}, (accessToken, refreshToken,otherTokenDetails, user, done) => {
//in here you can access all token details to given API scope
//and i have created file from that details
let tokens = {
access_token: accessToken,
refresh_token: refreshToken,
scope: otherTokenDetails.scope,
token_type: otherTokenDetails.token_type,
expiry_date:otherTokenDetails.expires_in
}
let data = JSON.stringify(tokens);
fs.writeFileSync('./tokens.json', data);
//you will get a "user" object which will include the google id, name details,
//email etc, using that details you can do persist user data in your DB or can check
//whether the user already exists
//after persisting user data to a DB call done
//better to use your DB user objects in the done method
done(null, user)
})
)
Then create your index.js file in nodejs for API route management and to call send method of Gmail API.
Also, run the following command to install "google-apis"
npm install googleapis#39 --save
index.js
const express = require("express")
//import passport_setup.js
const passportSetup = require('./passport_setup')
const cookieSeesion = require('cookie-session');
const passport = require("passport");
//import google api
const { google } = require('googleapis');
//read credentials file you obtained from google developer console
const fs = require("fs");
const path = require('path');
var pathToJson_1 = path.resolve(__dirname, './credentials.json');
const credentials = JSON.parse(fs.readFileSync(pathToJson_1));
//get Express functionalities to app
const app = express();
// **Middleware Operations**//
//cookie encryption
app.use(cookieSeesion({
name:'Reserve It',
maxAge: 1*60*60*1000,
keys: ['ranmalc6h12o6dewage']
}))
//initialize passort session handling
app.use(passport.initialize())
app.use(passport.session())
app.use(express.json());
//**API urls**//
//route to authenticate users using google by calling google stratergy in passport_setup.js
//mention access levels of API you want in the scope
app.get("/google", passport.authenticate('google', {
scope: ['profile',
'email',
'https://mail.google.com/'
],
accessType: 'offline',
prompt: 'consent'
}))
//redirected route after obtaining 'code' from user authentication with API scopes
app.get("/google/redirect", passport.authenticate('google'), (req, res) => {
try {
//read token file you saved earlier in passport_setup.js
var pathToJson_2 = path.resolve(__dirname, './tokens.json');
//get tokens to details to object
const tokens = JSON.parse(fs.readFileSync(pathToJson_2));
//extract credential details
const { client_secret, client_id, redirect_uris } = credentials.installed
//make OAuth2 object
const oAuth2Client = new google.auth.OAuth2(client_id,
client_secret,
redirect_uris[0])
// set token details to OAuth2 object
oAuth2Client.setCredentials(tokens)
//create gmail object to call APIs
const gmail = google.gmail({ version: 'v1', auth: oAuth2Client })
//call gmail APIs message send method
gmail.users.messages.send({
userId: 'me',//'me' indicate current logged in user id
resource: {
raw: //<email content>
}
}, (err, res) => {
if (err) {
console.log('The API returned an error: ' + err)
throw err
}
console.log('Email Status : ' + res.status)
console.log('Email Status Text : ' + res.statusText)
})
res.status(200).json({ status:true })
} catch (err) {
res.status(500).json(err)
}
})
app.listen(3000, () => { console.log('Server Satrted at port 3000') })
You can separate the routes in the index.js file to different files for clarity using express.Router()
If you want to call another Google API service just change this code segment and code below that;
const gmail = google.gmail({ version: 'v1', auth: oAuth2Client })
gmail.users.messages.send(....Send Method internal implementation given above....)
For Google Drive:
const drive = google.drive({version: 'v3', auth: oAuth2Client});
drive.files.list(...Refer "Google Drive API" documentation for more details....)
I believe you can't use passport.js for three-legged oauth for APIs like Sheets or Drive.
Have a look at the Using OAuth for web servers documentation instead.
user835611 has the correct answer, as that page explains everything quite nicely. However, if you still need more, the below link really helped me to understand how this works.
https://github.com/googleapis/google-auth-library-nodejs#oauth2
I am working on a speech-to-text web app using the IBM Watson Speech to text API. The API is fetched on the click of a button. But whenever I click the button. I get the above-mentioned error. I Have stored my API key and URL in a .env file.
I tried a lot but keep on getting this error. Please Help me out as I am new to all this.
I got server.js from the Watson Github Repo
Server.js
'use strict';
/* eslint-env node, es6 */
const env = require('dotenv');
env.config();
const express = require('express');
const app = express();
const AuthorizationV1 = require('watson-developer-cloud/authorization/v1');
const SpeechToTextV1 = require('watson-developer-cloud/speech-to-text/v1');
const TextToSpeechV1 = require('watson-developer-cloud/text-to-speech/v1');
const vcapServices = require('vcap_services');
const cors = require('cors');
// allows environment properties to be set in a file named .env
// on bluemix, enable rate-limiting and force https
if (process.env.VCAP_SERVICES) {
// enable rate-limiting
const RateLimit = require('express-rate-limit');
app.enable('trust proxy'); // required to work properly behind Bluemix's reverse proxy
const limiter = new RateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
delayMs: 0 // disable delaying - full speed until the max limit is reached
});
// apply to /api/*
app.use('/api/', limiter);
// force https - microphone access requires https in Chrome and possibly other browsers
// (*.mybluemix.net domains all have built-in https support)
const secure = require('express-secure-only');
app.use(secure());
}
app.use(express.static(__dirname + '/static'));
app.use(cors())
// token endpoints
// **Warning**: these endpoints should probably be guarded with additional authentication & authorization for production use
// speech to text token endpoint
var sttAuthService = new AuthorizationV1(
Object.assign(
{
iam_apikey: process.env.SPEECH_TO_TEXT_IAM_APIKEY, // if using an RC service
url: process.env.SPEECH_TO_TEXT_URL ? process.env.SPEECH_TO_TEXT_URL : SpeechToTextV1.URL
},
vcapServices.getCredentials('speech_to_text') // pulls credentials from environment in bluemix, otherwise returns {}
)
);
app.use('/api/speech-to-text/token', function(req, res) {
sttAuthService.getToken(function(err, token) {
if (err) {
console.log('Error retrieving token: ', err);
res.status(500).send('Error retrieving token');
return;
}
res.send(token);
});
});
const port = process.env.PORT || process.env.VCAP_APP_PORT || 3002;
app.listen(port, function() {
console.log('Example IBM Watson Speech JS SDK client app & token server live at http://localhost:%s/', port);
});
// Chrome requires https to access the user's microphone unless it's a localhost url so
// this sets up a basic server on port 3001 using an included self-signed certificate
// note: this is not suitable for production use
// however bluemix automatically adds https support at https://<myapp>.mybluemix.net
if (!process.env.VCAP_SERVICES) {
const fs = require('fs');
const https = require('https');
const HTTPS_PORT = 3001;
const options = {
key: fs.readFileSync(__dirname + '/keys/localhost.pem'),
cert: fs.readFileSync(__dirname + '/keys/localhost.cert')
};
https.createServer(options, app).listen(HTTPS_PORT, function() {
console.log('Secure server live at https://localhost:%s/', HTTPS_PORT);
});
}
App.js
import React, {Component} from 'react';
import 'tachyons';
//import WatsonSpeech from 'ibm-watson';
var recognizeMic = require('watson-speech/speech-to-text/recognize-microphone');
class App extends Component {
onListenClick = () => {
fetch('http://localhost:3002/api/speech-to-text/token')
.then(function(response) {
return response.text();
}).then(function (token) {
var stream = recognizeMic({
token: token, // use `access_token` as the parameter name if using an RC service
objectMode: true, // send objects instead of text
extractResults: true, // convert {results: [{alternatives:[...]}], result_index: 0} to {alternatives: [...], index: 0}
format: false // optional - performs basic formatting on the results such as capitals an periods
});
stream.on('data', function(data) {
console.log('error 1')
console.log(data);
});
stream.on('error', function(err) {
console.log('error 2')
console.log(err);
});
//document.querySelector('#stop').onclick = stream.stop.bind(stream);
}).catch(function(error) {
console.log('error 3')
console.log(error);
});
}
render() {
return(
<div>
<h2 className="tc"> Hello, and welcome to Watson Speech to text api</h2>
<button onClick={this.onListenClick}>Listen to Microphone</button>
</div>
);
}
}
export default App
Since the only code you show is fetching an authorisation token then I guess that that is what is throwing the authentication failure. I am not sure how old the code you are using is, but the mechanism you are using was used when the STT service credentials are userid / password. The mechanism became unreliable when IAM keys started to be used.
Your sample is still using watson-developer-cloud, but that has been superseded by ibm-watson. As migrating the code to ibm-watson will take a lot of rework, you can continue to use watson-developer-cloud.
If do you stick with watson-developer-cloud and you want to get hold of a token, with an IAM Key then use:
AuthIAMV1 = require('ibm-cloud-sdk-core/iam-token-manager/v1'),
...
tokenService = new AuthIAMV1.IamTokenManagerV1({iamApikey : apikey});
...
tokenService.getToken((err, res) => {
if (err) {
...
} else {
token = res;
...
}
});
I stuck with one problem.
I`m trying integrate PayPal button with meteor app. But for full functionality i need to handle with IPN.
Because i have to know at least transaction status.
I already have business account and i turned on IPN on path:
http://domein.com/ipn
I tried use PayPal documentation, but it doesn't help too.
I spend two days and still can't find anything helpful.
Maybe someone know how to implement IPN listener in meteor app?
EDIT: Update for Meteor 1.3+
I published a package that vastly simplifies the process. The package is planefy:paypal-ipn-listener.
First, install then package:
$ meteor add planefy:paypal-ipn-listener
Then, in server code:
import { IpnListener } from 'meteor/planefy:paypal-ipn-listener';
const listener = new IpnListener({
path: '/mypath',
allow_sandbox: true // in development
});
listener.onVerified((err, ipn) => console.log(ipn));
listener.onError((err) => console.log(err));
See the readme for more options.
ORIGINAL ANSWER
I did a lot of head scratching trying to figure this out too.
First, add the following packages, if you don't already have them.
meteor add meteorhacks:npm
meteor add meteorhacks:picker
If you are using iron:router, then you actually don't need need meteorhacks:picker (See update at bottom)
Create a package.json in your application root (if it doesn't already exist), and add the following to tell meteorhacks:npm which npm packages need to be installed:
{
"paypal-ipn" : "3.0.0",
"body-parser": "1.14.1",
}
In server code, configure Picker to properly parse JSON requests/responses:
const bodyParser = Meteor.npmRequire('body-parser');
Picker.middleware(bodyParser.urlencoded({ extended: false }));
Picker.middleware(bodyParser.json());
Also in server code, define a Picker route to handle incoming IPN messages
Picker.route('/ipn', function(params, req, res, next) {
res.writeHead(200);
const ipn = Meteor.npmRequire("paypal-ipn");
// create a wrapped version of the verify function so that
// we can verify synchronously
const wrappedVerify = Async.wrap(ipn,"verify");
let verified = false;
// only handle POSTs
if (req.method === "POST") {
// PayPal expects you to immeditately verify the IPN
// so do that first before further processing:
try {
//second argument is optional, and you'll want to remove for production environments
verified = wrappedVerify(req.body, {"allow_sandbox" : true});
} catch (err) {
// do something with error
}
if (verified === 'VERIFIED') {
let payment = req.body;
// do stuff with payment
}
}
res.end();
});
Update: If you are using iron:router, you don't need to use Picker. You can just define a server only router directly with iron:router, like so:
Router.map(function () {
this.route('ipn', {
path: '/ipn',
where: 'server',
action: function() {
var ipn = Meteor.npmRequire("paypal-ipn");
var wrappedVerify = Async.wrap(ipn, "verify");
var request = this.request;
var verified;
if (request.method !== 'POST') {
this.next();
} else {
try {
verified = wrappedVerify(request.body, {"allow_sandbox" : true});
} catch (error) {
//do something with error
}
if (verified === "VERIFIED") {
var payment = request.body;
//do something with payment
}
this.next();
}
}
});
});