I have an issue that I couldn't resolve for a quite long time.
I'm making a shop using feathersJS as a backend api and Vue.js on the front and Mongodb. As I try to authenticate user on website, everything goes smoothly until "setUser" action fires and in auth service getters I get an error saying "Cannot read property 'idField' of undefined". I believe that I changed this property to "_id", wherever I could, but still this error occurs. Here are some screenshots, that may be helpful. This project is supposed to get me my first job in webDev so I would be eternally grateful for your support.
AddItem action in vuex,
setUser action in vuex
Feathers-client.js:
import feathers from '#feathersjs/feathers';
import socketio from '#feathersjs/socketio-client';
import auth from '#feathersjs/authentication-client';
import io from 'socket.io-client';
import { iff, discard } from 'feathers-hooks-common';
import feathersVuex from 'feathers-vuex';
const socket = io('http://localhost:3030', { transports: ['websocket'] });
const feathersClient = feathers()
.configure(socketio(socket))
.configure(auth({ storage: window.localStorage, debug: false }))
.hooks({
before: {
all: [
iff(
context => ['create', 'update', 'patch'].includes(context.method),
discard('__id', '__isTemp'),
),
],
},
});
export default feathersClient;
// Setting up feathers-vuex
const {
makeServicePlugin, makeAuthPlugin, BaseModel, models, FeathersVuex,
} = feathersVuex(
feathersClient,
{
idField: '_id', // Must match the id field in your database table/collection
whitelist: ['$regex', '$options'],
},
);
export {
makeAuthPlugin, makeServicePlugin, BaseModel, models, FeathersVuex,
};
auth service file:
import { makeAuthPlugin } from '../../feathers-client';
export default makeAuthPlugin({
userService: 'api/users',
entityIdField: '_id',
});
The issue here was the name of the service, which as a plugin in vuex was under a name of "users" not "api/users". The solution was to change in servicePlugin options nameStyle: "path" instead of "short"
Related
I am making a sign up page with 3 providers (Twitter, Facebook and Instagram) using next-auth and prisma with mongoDB. The issue appears when I try to sign up with any of the providers. Here is my nextauth.js file.
import NextAuth from "next-auth"
import { PrismaAdapter } from "#next-auth/prisma-adapter"
import { PrismaClient } from '#prisma/client';
import InstagramProvider from "next-auth/providers/instagram";
import TwitterProvider from "next-auth/providers/twitter";
import FacebookProvider from "next-auth/providers/facebook";
const prisma = new PrismaClient();
export default NextAuth({
adapter: PrismaAdapter(prisma),
providers: [
InstagramProvider({
clientId: process.env.INSTAGRAM_CLIENT_ID,
clientSecret: process.env.INSTAGRAM_CLIENT_SECRET
}),
TwitterProvider({
clientId: process.env.TWITTER_CLIENT_ID,
clientSecret: process.env.TWITTER_CLIENT_SECRET,
version: "2.0",
}),
FacebookProvider({
clientId: process.env.FACEBOOK_CLIENT_ID,
clientSecret: process.env.FACEBOOK_CLIENT_SECRET
}),
],
session: {
strategy: 'jwt',
},
});
I have tried to reinstall all the dependencies, because I don't see what else could be the problem. At first I thought it was the dependencies so I reinstall all the dependencies.
The problem is in your adapter in the [nextauth].js file or wherever you are declaring the prisma instance.
Check out those similar discussions:
https://github.com/nextauthjs/next-auth/discussions/4152
Integrating Prisma Adapter with Next Auth - Unexpected token 'export'
The issue actually comes from the prisma schema. I fixed it after reading the next auth documentation about prisma and mongoDB.
I git clone & copy MUI Nextjs example project & start from there.
From NextAuth portal, they said I can just copy mongodb adapter setup here and basically it will work well out of the box. I placed this file in this path: /src/lib/mongodb.js
Here I'm using CredentialsProvider. Basically I'm using my own form for login & authentication process.
Here my /pages/api/auth/[...nextauth].js file:
import { MongoDBAdapter } from "#next-auth/mongodb-adapter";
import NextAuth from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';
import clientPromise from "../../../src/lib/mongodb";
export default NextAuth({
secret: process.env.SECRET,
adapter: MongoDBAdapter(clientPromise),
providers: [
CredentialsProvider({
async authorize(credentials) {
const { email, password } = credentials
if(email !== 'test#test.com' || password !== 'password123') {
throw new Error('User does not exists. Please make sure you insert the correct email & password.')
}
return {
id: 1,
name: 'Tester',
email: 'test#test.com'
}
}
})
],
callbacks: {
redirect: async ({ url, baseUrl }) => {
return baseUrl
}
}
})
What I understood, I can just straight away use this adapter & it will create 4 models/tables (User, Session, Account, VerificationToken) by default. So I don't need to create them myself. Ref doc here
According to the NextAuth MongoDB Adapter documentation, I just need to specify the MONGODB_URI in .env.local.
so here my /.env.local file content:
NEXTAUTH_URL=http://localhost:3000
MONGODB_URI=mongodb+srv://<username>:<password>#rest.lvnpm.mongodb.net/<database_name>?retryWrites=true&w=majority
SECRET=someSecret
NODE_ENV=development
So currently, it does nothing at all. I don't need to specify session.strategy to database since by default NextAuth will recognized that if I use adapter option.
What do I need to do here to make this work? Any helps is appreciated. Here my github project
I just found out that in NextAuth, if I use CredentialsProvider. I won't be able to persist data using database strategy. You may go here to NextAuth documentation itself to know why
I have been fiddling with moving a tutorial I did in Vue to Nuxt. I have been able to get everything working, however I feel I'm not doing it the "proper way". I have added the Nuxt axios module, but wasnt able to get it working, so I ended up just using the usual axios npm module. Here is my store:
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(Vuex)
Vue.use(VueAxios, axios)
export const state = () => ({
events: []
})
export const mutations = {
setEvents: (state, events) => {
state.events = events
}
}
export const actions = {
loadEvents: async context => {
let uri = 'http://localhost:4000/events';
const response = await axios.get(uri)
context.commit('setEvents', response.data)
}
}
I would like to know how to re-write this store using the #nuxtjs/axios module. I also didnt think I'd need to import vuex here, but if I dont, my app doesn't work.
Thanks for any help!
Using the #nuxtjs/axios module, you can configure axios inside your nuxt.config.js:
// nuxt.config.js
export default {
modules: [
'#nuxtjs/axios',
],
axios: {
// proxy: true
}
}
You can use it inside your store (or components) with this.$axios
// In store
{
actions: {
async getIP ({ commit }) {
const ip = await this.$axios.$get('http://icanhazip.com')
commit('SET_IP', ip)
}
}
}
I've added ionic cloud services to my app and want to use the native FaceBook authentication.
import { FacebookAuth } from '#ionic/cloud-angular';
this.facebookAuth.login()
When running this function on an Android phone, as expected I get the Facebook prompt to ask if my app can get permissions to read profile and email. When I click YES, the function returns an empty ERROR object:
Object {}
I'm sure I am catching it right, because when I choose CANCEL on the FB prompt, I get this error object:
Object {errorCode: "4201", errorMessage: "User cancelled dialog"}
Note: I'm using remote web inspector in chrome to see the full console. Unfortunately, as this requires a real device I can not Plunker this. However, I hope someone has an idea why this could happen. I have followed all these steps, including the FB developer settings, the hash and the ionic.io settings.
I have done that and it's working fine on the real device.If you have any question, please comment below.
Play with Git Repo
app.module.ts
import { CloudSettings, CloudModule } from '#ionic/cloud-angular';
const cloudSettings: CloudSettings = {
'core': {
'app_id': 'd32c02d2'
},
'auth': {
'facebook': {
'scope': ['public_profile', 'email']
}
}
};
#NgModule({
declarations: [
],
imports: [
CloudModule.forRoot(cloudSettings)
],
bootstrap: [IonicApp],
entryComponents: [
],
providers: [
]
})
export class AppModule { }
home.html
<button ion-button block type="button" (click)="fbLogin()">Fb Login</button>
home.ts
import { Component } from '#angular/core';
import { FacebookAuth, User } from '#ionic/cloud-angular';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
constructor(public facebookAuth: FacebookAuth, public user: User) {
}
fbLogin() {
this.facebookAuth.login();
}
}
Ok it's quite silly, but the first root cause was that the FB user I was trying to log in with was not registered as a tester. Apparently in that case, an empty error is returned by the plugin.
After adding as a tester, I received a real error (Andoird manifest dod not allow internet access). After fixing that issue, again I'm getting an empty error.
So my assumption is that some of the errors returned by FB are not well communicated by the plugin and so any other FB error can be causing this issue.
UPDATE 23/04: There seems to be a change from FB side, now the FB login screen did not succeed but gave an error about the hashing key. After fixing that issue, the FB login is working now.
I'm using Yeoman, Grunt, and Bower, to construct a platform for building a frontend independently of a a backend. The idea would be that all of my (AngularJS) controller, services, factories, etc live in this project, and get injected afterwards into my serverside codebase based off the result of grunt build.
My question is:
How can I mock endpoints so that the Grunt server responds to the same endpoints as my (Rails) App will?
At the moment I am using:
angular.module('myApp', ['ngResource'])
.run(['$rootScope', function ($rootScope) {
$rootScope.testState = 'test';
}]);
And then in each of my individual services:
mockJSON = {'foo': 'myMockJSON'}
And on every method:
if($rootScope.testState == 'test'){
return mockJSON;
}
else {
real service logic with $q/$http goes here
}
Then after grunt build, testState = 'test' gets removed.
This is clearly a relatively janky architecture. How can I avoid it? How can I have Grunt respond to the same endpoints as my app (some of which have dynamic params) apply some logic (if necessary), and serve out a json file (possibly dependent on path params)?
I've fixed this issue by using express to write a server that responds with static json.
First I created a directory in my project called 'api'. Within that directory I have the following files:
package.json:
{
"name": "mockAPI",
"version": "0.0.0",
"dependencies": {
"express": "~3.3.4"
}
}
Then I run npm install in this directory.
index.js:
module.exports = require('./lib/server');
lib/server.js:
express = require('express');
var app = express();
app.get('/my/endpoint', function(req, res){
res.json({'foo': 'myMockJSON'});
});
module.exports = app
and finally in my global Gruntfile.js:
connect: {
options: {
port: 9000,
hostname: 'localhost',
},
livereload: {
options: {
middleware: function (connect, options) {
return [
lrSnippet,
mountFolder(connect, '.tmp'),
mountFolder(connect, yeomanConfig.app),
require('./api')
];
}
}
},
Then the services make the requests, and the express server serves the correct JSON.
After grunt build, the express server is simply replaced by a rails server.
As of grunt-contrib-connect v.0.7.0 you can also just add your custom middleware to the existing middleware stack without having to manually rebuild the existing middleware stack.
livereload: {
options: {
open: true,
base: [
'.tmp',
'<%= config.app %>'
],
middleware: function(connect, options, middlewares) {
// inject a custom middleware into the array of default middlewares
middlewares.push(function(req, res, next) {
if (req.url !== '/my/endpoint') {
return next();
}
res.writeHead(200, {'Content-Type': 'application/json' });
res.end("{'foo': 'myMockJSON'}");
});
return middlewares;
}
}
},
See https://github.com/gruntjs/grunt-contrib-connect#middleware for the official documentation.
Alternatively you can use the grunt-connect-proxy to proxy everything that is missing in your test server to an actual backend.
It's quite easy to install, just one thing to remember when adding proxy to your livereload connect middleware is to add it last, like this:
middleware: function (connect) {
return [
lrSnippet,
mountFolder(connect, '.tmp'),
mountFolder(connect, yeomanConfig.app),
proxySnippet
];
}
grunt-connect-prism is similar to the Ruby project VCR. It provides an easy way for front end developers to record HTTP responses returned by their API (or some other remote source) and replay them later. It's basically an HTTP cache, but for developers working on a Single Page Application (SPA). You can also generate stubs for API calls that don't exist, and populate them the way you want.
It's useful for mocking complex & high latency API calls during development. It's also useful when writing e2e tests for your SPA only, removing the server from the equation. This results in much faster execution of your e2e test suite.
Prism works by adding a custom connect middleware to the connect server provided by the grunt-contrib-connect plugin. While in 'record' mode it will generate a file per response on the filesystem with content like the following:
{
"requestUrl": "/api/ponies",
"contentType": "application/json",
"statusCode": 200,
"data": {
"text": "my little ponies"
}
}
DISCLAIMER: I'm the author of this project.
You can use Apache proxy and connect your REST server with gruntjs.
Apache would do this:
proxy / -> gruntjs
proxy /service -> REST server
you would use your application hitting Apache and angular.js application would think that is talking with itself so no cross domain problem.
Here is a great tutorial on how to set this up:
http://alfrescoblog.com/2014/06/14/angular-js-activiti-webapp-with-activiti-rest/
Just my alternative way that based on Abraham P's answer. It does not need to install express within 'api' folder. I can separate the mock services for certain files. For example, my 'api' folder contains 3 files:
api\
index.js // assign all the "modules" and then simply require that.
user.js // all mocking for user
product.js // all mocking for product
file user.js
var user = function(req, res, next) {
if (req.method === 'POST' && req.url.indexOf('/user') === 0) {
res.end(
JSON.stringify({
'id' : '5463c277-87c4-4f1d-8f95-7d895304de12',
'role' : 'admin'
})
);
}
else {
next();
}
}
module.exports = user;
file product.js
var product = function(req, res, next) {
if (req.method === 'POST' && req.url.indexOf('/product') === 0) {
res.end(
JSON.stringify({
'id' : '5463c277-87c4-4f1d-8f95-7d895304de12',
'name' : 'test',
'category': 'test'
})
);
}
else {
next();
}
}
module.exports = product;
index.js just assigns all the "modules" and we simply require that.
module.exports = {
product: require('./product.js'),
user: require('./user.js')
};
My Gruntfile.js file
connect: {
options: {
port: 9000,
// Change this to '0.0.0.0' to access the server from outside.
hostname: 'localhost',
livereload: 35729
},
livereload: {
options: {
open: true,
middleware: function (connect) {
return [
connect.static('.tmp'),
connect().use(
'/bower_components',
connect.static('./bower_components')
),
connect.static(appConfig.app),
require('./api').user,
require('./api').product,
];
}
}
}