Related
I'm using next-auth credentials provider for authentication in a next.js project, it works fine in development but when I deployed the website to production I got 401 error code with the following response {url: "https://sub.domain.com/api/auth/error?error="} as I tried to login.
Everything is working fine in localhost and authentication is smooth with no errors. Wher is the error in my code?
My next-auth config
// /api/auth/[...nextauth].ts
export const authOptions: NextAuthOptions = {
secret: process.env.NEXTAUTH_SECRET ?? 'supersecret',
adapter: PrismaAdapter(prisma),
providers: [
CredentialsProvider({
id: 'admin-login',
name: 'Admin login',
credentials: {
email: {
label: 'Email',
type: 'email',
placeholder: 'test#test.com',
},
password: { label: 'Mot de passe', type: 'password' },
},
authorize: async (credentials, _request) => {
try {
const { data: user } = await axios.post(
`${process.env.APP_URL}/api/auth/admin/login`,
credentials
);
return user;
} catch (err) {
throw new Error(
(err as AxiosError<{ message: string }>).response?.data.message
);
}
},
}),
CredentialsProvider({
id: 'room-login',
name: 'Room login',
credentials: {
roomId: { label: 'Id de la chambre', type: 'text' },
password: { label: 'Mot de passe', type: 'password' },
},
authorize: async (credentials, _request) => {
try {
const { data: room } = await axios.post(
`${process.env.APP_URL}/api/auth/room/login`,
credentials
);
return room;
} catch (err) {
throw new Error(
(err as AxiosError<{ message: string }>).response?.data.message
);
}
},
}),
],
callbacks: {
async signIn() {
return true;
},
async redirect({ url, baseUrl }) {
if (url.startsWith('/')) return `${baseUrl}${url}`;
else if (new URL(url).origin === baseUrl) return url;
return baseUrl;
},
async jwt({ token, user }) {
if (user) {
token.id = user.id;
token.role = (user.role as Role) ?? 'GUEST';
}
return token;
},
async session({ session, token }) {
const sess: Session = {
...session,
user: {
...session.user,
id: token.id as number | string,
role: token.role as Role,
},
};
// console.log('SESSION: ', sess)
return sess;
},
},
session: {
strategy: 'jwt',
},
jwt: {
secret: process.env.JWT_SECRET ?? 'supersecret',
maxAge: 10 * 24 * 30 * 60, // 30 days
},
pages: {
signIn: '/auth/login',
signOut: '/auth/login',
newUser: '/api/auth/register',
error: '/auth/login',
},
debug: process.env.NODE_ENV === 'development',
};
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
return NextAuth(req, res, authOptions);
}
The issue was caused by the SSL certificate that I had to install on the domain for next-auth to work.
I am learning vuejs and i am working on my first project which is a social network, and i want to implement a like button that call the api to add a like or remove it if the user has already liked it. It does work in my backend but i can't make it work in the front.
I need to send the userId and add or remove the like when i click on the button
This is the data
data() {
return {
post: {
file: "",
content: "",
likes: 0,
},
showModal: false,
showModifyPost: false,
user: {
firstname: "",
lastname: "",
_id: "",
},
};
},
the last method i tried
likePost(id) {
axios
.post('http://127.0.0.1:3000/api/post/like/' + id, {
headers: {
Authorization: "Bearer " + localStorage.getItem("token"),
},
})
.then(() => {
console.log("response", response);
this.user._id = response.data._id;
if(post.usersLiked == user._id) {
this.post.likes += 0
} else if (post.usersLiked != user._id) {
this.post.likes += 1
};
})
.catch((error) => console.log(error));
}
and this is the model
const postSchema = mongoose.Schema({
userId: { type: String, required: true, ref: "User" },
content: { type: String, required: true, trim: true },
imageUrl: { type: String, trim: true },
likes: { type: Number, default: 0 },
usersLiked: [{ type: String, ref: "User" }],
firstname: {type: String, required: true, trim: true },
lastname: {type: String, required: true, trim: true },
created_at: { type: Date},
updated_at: { type: Date }
});
Any idea what is wrong ? Thank you !
.then(() => { // you missed value response from Promise here
this.user._id = response.data._id;
if(post.usersLiked == user._id)
})
Do you mean this.post.usersLiked === user._id I suppose, so post within your data options should be
post: {
file: "",
content: "",
likes: 0,
usersLiked: false,
// something else reflect to your post schema
},
i want to implement a like button that call the api to add a like or remove it if the user has already liked it
By saying that you just need a simple boolean value to do this
likePost(id) {
axios
.post('http://127.0.0.1:3000/api/post/like/' + id, {
headers: {
Authorization: "Bearer " + localStorage.getItem("token"),
},
})
.then((response) => {
// Just need to toggle state
this.$set(this.post, 'usersLiked', this.post.usersLiked !== response?.data?._id)
})
.catch((error) => console.log(error));
}
Found the answer, i changed the axios method to this
likePost(id) {
let userId = localStorage.getItem('userId');
axios
.post('http://127.0.0.1:3000/api/post/like/' + id, { userId }, {
headers: {
Authorization: "Bearer " + localStorage.getItem("token"),
},
})
.then((response) => {
console.log(response.data);
this.getAllPost();
})
.catch((error) => console.log(error));
}
i also made a few changes to the data
data() {
return {
posts: [],
post: {
file: "",
content: "",
},
showModal: false,
showModifyPost: false,
user: {
firstname: "",
lastname: "",
_id: "",
},
};
},
and i also made some changes on the controller
exports.ratePost = (req, res, next) => {
console.log(req.body.userId)
//using findOne function to find the post
Post.findOne({ _id: req.params.id }).then(post => {
if (!post.usersLiked.includes(req.body.userId)) {
// making a object with $inc and $push methods to add a like and to add the user's id
let toChange = {
$inc: { likes: +1 },
$push: { usersLiked: req.body.userId },
};
// we update the result for the like
Post.updateOne({ _id: req.params.id }, toChange)
// then we send the result and the message
.then(post =>
res
.status(200)
.json(
{ message: "Liked !", data: post }
)
)
.catch(error => res.status(400).json({ error }));
} else if (post.usersLiked.includes(req.body.userId)) {
// using the updateOne function to update the result
Post.updateOne(
{ _id: req.params.id },
// we use a pull method to take off a like
{ $pull: { usersLiked: req.body.userId }, $inc: { likes: -1 } }
)
.then(post => {
// then we send the result and the message
res
.status(200)
.json(
{ message: "Post unliked", data: post }
);
})
.catch(error => res.status(400).json({ error }));
}
});
};
I have a postAsset action in my vuex store like so
async postAsset({dispatch}, asset) {
const f = await dispatch('srcToFile', asset);
asset[0].files.fileList = f;
const fileData = asset[0].files.fileList;
const detailData = asset[0].detail;
const fData = new FormData();
fData.append('Name', asset[0].name);
Object.keys(detailData).forEach((key) => {
fData.append(`Detail.${key}`, detailData[key]);
});
for (var i = 0; i < fileData.length; i++) {
fData.append('Files', fileData[i]);
}
await axios({
method: 'post',
url: 'https://localhost:5001/api/Assets',
data: fData,
headers: {
'Content-Type': undefined
}
})
.then(function(response) {
console.warn(response);
})
.catch(function(response) {
console.warn(response);
});
}
It is successfully posting to my api backend and to the database.
The issue that I am running into is that after I make the first post it posts the previous data and the new data I do not know why it is doing this. I did add await to the axios call but that just slowed it down it is still posting two times after the first and im sure if i keep posting it will continue to post the previous ones into the db again and again. Im at a loss as to what is going on so reaching out for some assistance to see if I can get this resolved.
examples of what it looks like in the db
does anyone have any advice for me so I can get this fixed? I should only be getting one item posted at a time that is the desired result. I have gone through my inputs and put in .prevent to stop them from clicking twice but I don't think it is that .. this is like it is saving the data and reposting it all at once each time I add a new record .
UPDATE:
the code that calls the action
populateAssets ({ dispatch }, asset) {
return new Promise((resolve) => {
assets.forEach((asset) => {
commit('createAsset', asset);
);
dispatch('postAsset', asset);
resolve(true);
});
},
the populate assets populates a list with a completed asset.
and asset is coming from the srcToFile method
that converts the files to a blob that I can post with
async srcToFile(context, asset) {
const files = asset[0].files.fileList;
let pmsArray = [];
for (let f = 0; f < files.length; f++) {
var data = files[f].data;
let name = files[f].name;
let mimeType = files[f].type;
await fetch(data)
.then(function(res) {
const r = res.arrayBuffer();
console.warn('resource ', r);
return r;
})
.then(function(buf) {
console.warn('buffer: ', [buf]);
let file = new File([buf], name, { type: mimeType });
pmsArray.push(file);
});
}
console.warn(pmsArray);
return pmsArray;
},
asset is an array from my add asset component
structure of asset
name: '',
detail: {
category: '',
manufacturer: '',
model: '',
serialNumber: '',
purchasePlace: '',
quantity: 1,
acquiredDate: '',
purchasePrice: '',
currentValue: '',
condition: '',
assetLocation: '',
retiredDate: '',
description: ''
},
files: {
fileList: []
}
hope this helps out some
the whole store file
import Vue from 'vue'
import Vuex from 'vuex'
import { states } from '../components/enums/enums'
import { getField, updateField } from 'vuex-map-fields'
import axios from 'axios'
Vue.use(Vuex);
const inventory = {
namespaced: true,
strict: true,
state: {
assets: {
items: []
},
categories: [],
manufacturers: [],
assetLocations: [],
conditions: ['New', 'Fair', 'Good', 'Poor']
},
getters: {
assetItems: state => state.assets.items,
getAssetById: (state) => (id) => {
return state.assets.items.find(i => i.id === id);
},
conditions: (state) => state.conditions,
categories: (state) => state.categories,
manufacturers: (state) => state.manufacturers,
assetLocations: (state) => state.assetLocations
},
mutations: {
createAsset (state, assets) {
state.assets.items.push(assets);
},
createCategories (state, category) {
state.categories.push(category);
},
createManufacturers (state, manufacturer) {
state.manufacturers.push(manufacturer);
},
createLocations (state, locations) {
state.assetLocations.push(locations);
}
},
actions: {
addToCategories ({ commit }, categories) {
commit('createCategories', categories);
},
addToManufacturers ({ commit }, manufacturers) {
commit('createManufacturers', manufacturers);
},
addToLocations ({ commit }, locations) {
commit('createLocations', locations);
},
populateAssets ({ dispatch }, asset) {
//return new Promise((resolve) => {
// assets.forEach((asset) => {
// commit('createAsset', asset);
// });
dispatch('postAsset', asset);
// resolve(true);
//});
},
addAsset ({ dispatch, /*getters*/ }, newAsset) {
//let assetCount = getters.assetItems.length;
//newAsset.id = assetCount === 0
// ? 1
// : assetCount++;
dispatch('populateAssets', [newAsset]);
},
async srcToFile(context, asset) {
const files = asset[0].files.fileList;
let pmsArray = [];
for (let f = 0; f < files.length; f++) {
var data = files[f].data;
let name = files[f].name;
let mimeType = files[f].type;
await fetch(data)
.then(function(res) {
const r = res.arrayBuffer();
console.warn('resource ', r);
return r;
})
.then(function(buf) {
console.warn('buffer: ', [buf]);
let file = new File([buf], name, { type: mimeType });
pmsArray.push(file);
});
}
console.warn(pmsArray);
return pmsArray;
},
async postAsset({ dispatch }, asset) {
const f = await dispatch('srcToFile', asset);
asset[0].files.fileList = f;
const fileData = asset[0].files.fileList;
const detailData = asset[0].detail;
const fData = new FormData();
fData.append('Name', asset[0].name);
Object.keys(detailData).forEach((key) => {
fData.append(`Detail.${key}`, detailData[key]);
});
for (var i = 0; i < fileData.length; i++) {
fData.append('Files', fileData[i]);
}
await axios({
method: 'post',
url: 'https://localhost:5001/api/Assets',
data: fData,
headers: { 'Content-Type': undefined }
})
.then(function(response) {
console.warn(response);
})
.catch(function(response) {
console.warn(response);
});
}
}
};
const maintenance = {
state: {
backup: []
},
strict: true,
getters: {},
mutations: {},
actions: {}
};
const assetProcessing = {
namespaced: true,
state: {
currentAsset: {
id: 0,
name: '',
detail: {
category: '',
manufacturer: '',
model: '',
serialNumber: '',
purchasePlace: '',
quantity: 1,
acquiredDate: '',
purchasePrice: '',
currentValue: '',
condition: '',
assetLocation: '',
retiredDate: '',
description: ''
},
files: {
fileList: []
}
},
filePosition: -1,
selectedItem: -1,
state: states.view,
isNewAsset: false
},
getters: {
getField,
getOpenAsset (state) {
return state.currentAsset
},
getSelectedAsset: (state, getters, rootState, rootGetters) => (id) => {
if (state.isNewAsset) return state.currentAsset
Object.assign(state.currentAsset, JSON.parse(JSON.stringify(rootGetters['inventory/getAssetById'](!id ? 0 : id))));
return state.currentAsset
},
appState: (state) => state.state,
getCurrentPosition (state) {
return state.filePosition
},
selectedAssetId: (state) => state.selectedItem
},
mutations: {
updateField,
setAsset (state, asset) {
Object.assign(state.currentAsset, asset)
},
setFiles (state, files) {
Object.assign(state.currentAsset.files, files)
},
newAsset (state) {
Object.assign(state.isNewAsset, true)
Object.assign(state.currentAsset, {
id: 0,
name: '',
detail: {
category: '',
manufacturer: '',
model: '',
serialNumber: '',
purchasePlace: '',
quantity: 1,
acquiredDate: '',
purchasePrice: '',
currentValue: '',
condition: '',
assetLocation: '',
retiredDate: '',
description: ''
},
files: {
fileList: []
}
})
},
updateSelectedItem (state, id) {
Vue.set(state, 'selectedItem', id);
},
updateState (state, newState) {
Vue.set(state, 'state', newState);
}
},
actions: {}
};
export const store = new Vuex.Store({
modules: {
inventory: inventory,
maintenance: maintenance,
assetProcessing
}
})
add asset is called when the user clicks the save button on the form
addAsset () {
this.$store.dispatch('inventory/addAsset', this.newAsset) <--- this calls add asset
this.$store.commit('assetProcessing/updateState', states.view);<-- this closes the window
},
So after much debugging we found that the eventbus was firing multiple times causing the excessive posting we added
beforeDestroy() {
eventBus.$off('passAssetToBeSaved');
eventBus.$off('updateAddActionBar');
},
to the AssetAdd.vue component and it eliminated the excessive posting of the asset.
I want to thank #phil for helping me out in this.
I want to update/push to multiple mongoose mode with a single payload. E.g. I have a VolSchema, a CaseSchema and a JudgeSchema. I will like to push values to the VolSchema and the JudgeSchema whenever a new Case is being created. Already it works fine when I update only the VolSchema, but when a add the JudgeSchema, I only get the Judge model updated without the Vol model.
Model/Cases.js
const mongoose = require("mongoose");
const CasesSchema = new mongoose.Schema({
title: String,
vol: { type: mongoose.Schema.Types.ObjectId, ref: "Vol", required:
true },
judges: [{ type: mongoose.Schema.Types.ObjectId, ref: "Judge" }],
});
module.exports = mongoose.model("Cases", CasesSchema)
Models/Vol.js
const mongoose = require("mongoose");
const VolSchema = new mongoose.Schema({
vol_no: {
type: String,
unique: true,
uppercase: false,
},
cases: [{ type: mongoose.Schema.Types.ObjectId, ref: "Cases" }],
createdAt: {
type: Date,
default: Date.now,
},
});
module.exports = mongoose.model("Vol", VolSchema);
Models/Judge.js
const mongoose = require("mongoose");
const JudgeSchema = new mongoose.Schema({
name: String,
bio: String,
cases: [{ type: mongoose.Schema.Types.ObjectId, ref: "Cases" }],
});
module.exports = mongoose.model("Judge", JudgeSchema);
resolvers.js
const Vol = require("../models/Vol");
const Case = require("../models/Cases");
const Judge = require("../models/Judge");
module.exports = {
Mutation: {
addCase: async (_, { title, volID, judgesID }) => {
const newCase = await new Case({
title: title,
vol: volID,
judges: judgesID,
}).save();
const newVol = Vol.updateOne(
{ _id: volID },
{ $push: { cases: newCase } },
{ new: true },
);
const judge = Judge.updateOne(
{ _id: judgesID },
{ $push: { cases: { $each: [newCase], $position: 0 } } },
{ new: true },
);
return newVol, judge // Judge model gets updated only
return newVol // Vol gets update only
return{vol: newVol, judges: judge } // i have tried this too
},
},
};
i will like to push newCase to the Vol and also to the Judge models
Here is my best answer as I am not sure exactly what you are asking.
const Vol = require("../models/Vol");
const Case = require("../models/Cases");
const Judge = require("../models/Judge");
module.exports = {
Mutation: {
addCase: async (_, { title, volID, judgesID }) => {
const newCase = await new Case({
title: title,
vol: volID,
judges: judgesID,
}).save();
const newVol = Vol.updateOne(
{ _id: volID },
{ $push: { cases: newCase } },
{ new: true },
);
const judge = Judge.updateOne(
{ _id: judgesID },
{ $push: { cases: { $each: [newCase], $position: 0 } } },
{ new: true },
);
return newVol, judge // Judge model gets updated only
return newVol // Vol gets update only
return{vol: newVol, judges: judge } // i have tried this too
},
},
};
should look like this
const Vol = require("../models/Vol");
const Case = require("../models/Cases");
const Judge = require("../models/Judge");
module.exports = {
Mutation: {
addCase: async (_, { title, volID, judgesID }) => {
const newCase = await new Case({
title: title,
vol: volID,
judges: judgesID,
}).save();
const newVol = await Vol.updateOne(
{ _id: volID },
{ $push: { cases: newCase } },
{ new: true },
);
const judge = await Judge.updateOne(
{ _id: judgesID },
{ $push: { cases: { $each: [newCase], $position: 0 } } },
{ new: true },
);
return newCase;
},
},
};
Also because this is GraphQL it would help if I knew the return type. I would think that the return type is the Case not the newVol or judge. So in this case you would actually get weird looking results be returning something other than case. In which case the last line should read return newCase;
I have a modal component that takes some input, creates a record on the backend and then as part of the success response I would like to push data to an object on the parent scope.
I have tried emitting an event from the child on success with the data I would like to append but I can't seem to get it to fire.
When addNote() successfully completes what would be the best approach to update the "notes" array object on the parent scope with the data I get back in my component?
Vue.component('modal', {
template: '#modal-template',
data: function() {
return {correctiveAction: this.correctiveAction}
},
props: ['notes'],
methods: {
addNote: function () {
axios.get('/quality/ajax/add-note/', {
params: {
action: this.correctiveAction
}
}).then(function (response) {
// append new corrective action
app = this;
this.$emit('addingNote', response.data.data.success[0].data);
//app.notes.push(response.data.data.success[0].data);
swal({
title: "Boom!",
type: "success",
text: "Corrective Action Successfully Added",
});
}).catch()
}
}
});
var app = new Vue({
el: '#app',
data: {
segment: "",
customer: "",
product: "",
startDate: "",
endDate: "",
notes: "",
showModal: false,
correctiveAction: ""
},
delimiters: ["<%","%>"],
methods: {
refresh: function () {
location.reload();
},
getNotes: function () {
app = this
axios.get('/quality/ajax/get-notes/').then(function (response) {
// populate notes
app.notes = response.data.data.success[0].notes
}).catch()
},
removeNote: function (id, index) {
app = this
axios.get('/quality/ajax/remove-note/', {
params: {
id: id
}
}).then(function () {
// remove note from list
app.notes.splice(index, 1);
swal({
title: "",
type: "success",
text: "Corrective Action Successfully Removed",
});
}).catch(function (err) {
console.log(err)
swal({
title: "",
type: "warning",
text: "Error Deleting Corrective Action",
});
return;
});
},
generateReport: function () {
$('#loading').show();
}).catch()
}
}
});
// get all active corrective actions
app.getNotes();
Well for one, you are setting a global variable app as a result of new Vue() and then you are blowing that variable away in your addNote method by setting app = this. That changes the variable to a completely different thing.
Also, you don't show anything listening to the addingNote event.
Don't use app everywhere. Use a scoped variable.
getNotes: function () {
const self = this
axios.get('/quality/ajax/get-notes/').then(function (response) {
// populate notes
self.notes = response.data.data.success[0].notes
}).catch()
},
And change addNote.
addNote: function () {
const self = this
axios.get('/quality/ajax/add-note/', {
params: { action: this.correctiveAction}
}).then(function (response) {
// append new corrective action
self.$emit('addingNote', response.data.data.success[0].data);
swal({
title: "Boom!",
type: "success",
text: "Corrective Action Successfully Added",
});
}).catch()
}
Looks like you should also fix removeNote.