I'm trying to run an action in my store that initializes an authenticated user. It checks to see if there is a JWT stored in local storage, then sets that JWT to the store. Since there is no local storage on the server side I use the Nuxt helper "process.client" in my middleware. When the named middleware runs when I visit an authorized route it's only running once on the server, and not running again on the client. I could add it as SSR false in the Nuxt config, but I don't want it to run on every page load... is there something I'm missing?
// middlware/check-auth.ts
import { Middleware } from '#nuxt/types'
import { useCustomerStore } from '#/store/customer'
const checkAuth: Middleware = ({ $pinia }) => {
const { initAuth } = useCustomerStore($pinia)
if (process.client) {
initAuth()
}
}
export default checkAuth
// pages/account/details
// page calling middleware
<script lang="ts">
import { defineComponent } from '#nuxtjs/composition-api'
export default defineComponent({
middleware: ['check-auth', 'auth']
})
</script>
<template>
<coming-soon></coming-soon>
</template>
<style module lang="postcss"></style>
// store/customer.ts
// action being called in middleware
actions: {
initAuth(): void {
const accessToken = localStorage.getItem('accessToken')
if (!accessToken) {
return
}
this.accessToken = accessToken
},
}
Related
I have followed this tutorial
https://joyofcode.xyz/using-websockets-with-sveltekit
and implemented a basic websocket server that sends a message to the client when he connects.
I want to also send a message when someone submits a form.
// socketIo.ts.js
import { Server } from 'socket.io';
let io
export function injectSocketIO(server) {
io = new Server(server);
io.on('connection', (socket) => {
socket.emit('eventFromServer', 'Hello, World 👋')
});
console.log('SocketIO injected');
}
export function send(sms){
io.emit('eventFromServer', sms);
}
export function getIO(){return io}
//vite.config.ts
import { sveltekit } from '#sveltejs/kit/vite';
import type { UserConfig } from 'vite';
import { injectSocketIO } from './socketIo.js';
import { Server } from 'socket.io'
const webSocketServer = {
name: 'webSocketServer',
configureServer(server) {
injectSocketIO(server.httpServer);
},
}
const config: UserConfig = {
plugins: [sveltekit(), webSocketServer]
};
export default config;
//my form action
import { send } from './socketIo.ts'
export const actions = {
default: async({ request }) =>{
console.log(send('da'))
I'm getting this error
Cannot read properties of undefined (reading 'emit')
TypeError: Cannot read properties of undefined (reading 'emit')
at Module.send (/home/runner/sveltekit-cpp/socketIo.ts:17:5)
at default (/home/runner/sveltekit-cpp/src/routes/+page.server.ts:10:16)
at call_action (file:///home/runner/sveltekit-cpp/node_modules/#sveltejs/kit/src/runtime/server/page/actions.js:204:9)
at handle_action_request (file:///home/runner/sveltekit-cpp/node_modules/#sveltejs/kit/src/runtime/server/page/actions.js:134:22)
at render_page (file:///home/runner/sveltekit-cpp/node_modules/#sveltejs/kit/src/runtime/server/page/index.js:65:26)
at async resolve (file:///home/runner/sveltekit-cpp/node_modules/#sveltejs/kit/src/runtime/server/index.js:247:17)
at async respond (file:///home/runner/sveltekit-cpp/node_modules/#sveltejs/kit/src/runtime/server/index.js:298:20)
at async file:///home/runner/sveltekit-cpp/node_modules/#sveltejs/kit/src/exports/vite/dev/index.js:418:22
I have tried exporting a send function from my socketIo file but it didn't work
I am trying to do Spotify Authentication using Client(React) and Server, the logging in works for a second then the page refreshes immediately after logging in and logs the user out. Anyone knows where might be the problem?
Here is my code:
server.js:
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const SpotifyWebApi = require('spotify-web-api-node');
const app = express();
app.use(cors()) // to handle the cross-origin requests
app.use(express.json()); // to parse JSON bodies
const port = process.env.PORT || 8000;
const credentials = {
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
redirectUri: process.env.REDIRECT_URI || "http://localhost:3000"
};
app.post('/refresh', (req, res) => {
const refreshToken = req.body.refreshToken;
// console.log("Hii");
let spotifyApi = new spotifyWebApi({
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
redirectUri: process.env.REDIRECT_URI,
refreshToken,
});
spotifyApi
.refreshAccessToken()
.then((data) => {
// console.log(data.body);
res.json({
accessToken: data.body.access_token,
expiresIn: data.body.expires_in,
})
})
.catch((err) => {
console.log(err);
res.sendStatus(400);
});
});
app.post('/login', (req,res) => {
// Get the "code" value posted from the client-side and get the user data from the spotify api
let spotifyApi = new spotifyWebApi(credentials)
const code = req.body.code
spotifyApi.authorizationCodeGrant(code).then((data) => {
// Returning the User's Data in the json formate
res.json({
accessToken : data.body.access_token,
refreshToken : data.body.refresh_token,
expiresIn : data.body.expires_in
})
})
.catch((err) => {
console.log(err);
res.sendStatus(400)
})
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
Client side:
useAuth.js:
import React from 'react';
import { useEffect, useState } from 'react';
import axios from "axios"
export default function useAuth(code) {
const [accessToken, setAccessToken] = useState();
const [refreshToken, setRefreshToken] = useState();
const [expiresIn, setExpiresIn] = useState();
useEffect(() => {
axios
.post("/login", {code})
.then((res) => {
window.history.pushState({}, null, "/");
console.log(res.data);
setAccessToken(res.data.accessToken);
setRefreshToken(res.data.refreshToken);
setExpiresIn(res.data.expiresIn);
})
.catch(() => {
window.location = "/";
});
}, [code]);
useEffect(() => {
if (!refreshToken || !expiresIn) {
return;
}
let interval = setInterval(() => {
axios
.post("/refresh", {refreshToken})
.then((res) => {
setAccessToken(res.data.accessToken);
setExpiresIn(res.data.expiresIn);
})
.catch(() => {
window.location = "/";
});
}, (expiresIn - 60) * 1000);
return () => clearInterval(interval)
}, [refreshToken, expiresIn]);
return accessToken;
}
spotifyConfig.js:
const authEndpoint = "https://accounts.spotify.com/authorize";
const redirectUri = "http://localhost:3000";
const clientId = "ea28d4ba34f34b44b59c640052c6e098";
const scopes = [
"streaming",
"playlist-modify-public",
"ugc-image-upload",
"user-read-email",
"user-read-private",
"user-read-currently-playing",
"user-read-recently-played",
"user-read-playback-state",
"user-modify-playback-state"
];
export const loginUrl = `${authEndpoint}?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&scope=${scopes.join(
"%20"
)}&show_dialog=true`;
App.js:
import './App.css';
import Dashboard from './pages/Dashboard';
import Login from './components/Login';
const code = new URLSearchParams(window.location.search).get('code')
function App() {
return (
<div>
{code ? <Dashboard code={code}/> : <Login/>}
</div>
);
}
export default App;
👉🏽 this page appears a second then logs out Dashboard.js:
import React, {useEffect} from 'react';
import useAuth from '../useAuth';
import SpotifyWebApi from "spotify-web-api-node";
const spotifyApi = new SpotifyWebApi({
clientId: "ea28d4ba34f34b44b59c640052c6e098",
});
export default function Dashboard({code}) {
const accessToken = useAuth(code);
useEffect(() => {
if (!accessToken) return;
spotifyApi.setAccessToken(accessToken);
spotifyApi.getMe().then(data => {
console.log(data);
})
}, []);
return (
<div>
This is the home page 🏠
</div>
)
}
Login.js:
import React from 'react';
import { loginUrl } from '../spotifyConfig';
export default function Login() {
return (
<div>
<a href={loginUrl}>
<button>LOGIN WITH SPOTIFY</button>
</a>
<div className="links">
<p>
⚠ When joining or creating a Queue, open Spotify to be able to queue up tracks
</p>
</div>
</div>
)
}
I experienced a similar issue. Here are a few steps that helped me to resolve it and many of the subsequent issues I encountered.
Run your IDE's debugger and set break points for your /login request. Also, check whether your environment variables are getting set as you intend (if running VSCode you can learn how to set this up here). In particular, make sure your credentials
clientId: process.env.CLIENT_ID, clientSecret: process.env.CLIENT_SECRET, redirectUri: process.env.REDIRECT_URI
are correct. If your environment variables are not being loaded you won't be able to create a new SpotifyWebApi instance (check out dotenv file is not loading environment variables). Another easy way to check if this is the problem is to hard code your values temporarily.
Test your server /login endpoint independently before running the client to see whether the endpoint is returning 400 or another error when executing requests to Spotify.
Make sure you keep your devtools console open in your browser so you can identify any failed requests you're making to the Spotify API and consider setting devtools to preserve logs in case the page refreshing is deleting them.
If you are running both your client and server from inside VSCode, try instead running them both in new shells outside of VSCode.
I was having a similar issue, having followed this tutorial, and then this YouTube tutorial.
What I realised was that my App component was being rendered twice, which was causing everything to be called twice, including the login endpoint. I was able to verify this using console.log in the endpoint and seeing if the log appeared twice. As the same Spotify code was being used twice in SpotifyWebApi.authorizationCodeGrant, this was what was causing the error.
I was able to trace the issue to the React.StrictMode being enabled, which must have happened when using the create-react-app command. Verify whether the tags appear in your index.js file. For more information, check this StackOverflow answer: My React Component is rendering twice because of Strict Mode
I have a file exchange Next.js app that I would like to deploy. In development whenever file is dropped, the app stores the file in root public folder, and when the file is downloaded the app takes it from there as well using the <a> tag with href attribute of uploads/{filename}. This all works pretty well in development, but not in production.
I know that whenever npm run build is run, Next.js takes the files from public folder and the files added there at runtime will not be served.
The question is are there any ways of persistent file storage in Next.js apart from third party services like AWS S3?
Next.js does allow file storage at buildtime, but not at runtime. Next.js will not be able to fulfill your file upload requirement. AWS S3 is the best option here.
next in node runtime is NodeJS, thus If your cloud provider allows create persistent disk and mount it to your project, then you can do it:
e.g. pages/api/saveFile.ts:
import { writeFileSync } from 'fs';
import { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { path = null } = req.query;
if (!path) {
res.status(400).json({ name: 'no path provided' })
} else {
// your file content here
const content = Date.now().toString();
writeFileSync(`/tmp/${path}.txt`, content);
res.json({
path,
content
})
}
}
/tmp works almost in every cloud provider (including Vercel itself), but those files will be lost on next deployment; instead you should use your mounted disk path
pages/api/readFile.ts
import { readFileSync } from 'fs';
import { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { path = '' } = req.query;
if (!path) {
res.status(400).json({ name: 'wrong' })
} else {
res.send(readFileSync(`/tmp/${path}`));
}
}
Live running example:
fetch('https://eaglesdoc.vercel.app/api/writefile?path=test')
.then(res => res.text()).then(res => console.log(res)).then( () =>
fetch('https://eaglesdoc.vercel.app/api/readfile?path=test'))
.then(res => res.text()).then(res => console.log(res))
I'm using NextJs 10.0.5 with next-i18next 8.1.0 to localize my application. As we all know nextJs 10 has subpath routing for internationalized routing. In addition, I need to change the page names by language. For example, I have a contact-us file inside the pages folder. When I change the language to Turkish, I have to use localhost:3000/tr/contact-us. However, I want to use localhost:3000/bize-ulasin to access the contact-us page when the language is Turkish. So there are two URLs and only one page file.
It works when I use custom routing with express js in the server.js file. However, when I want to access the "locale" variable within the getStaticProps function in the contact-us file, I cannot access it. The getStaticProps function returns undefined for "locale" variable when I use localhost:3000/bize-ulasin URL.
server.js
const { createServer } = require("http");
const { parse } = require("url");
const next = require("next");
const app = next({ dev: process.env.NODE_ENV !== "production" });
const handle = app.getRequestHandler(app);
app.prepare().then(() => {
createServer((req, res) => {
const parsedUrl = parse(req.url, true);
const { pathname, query } = parsedUrl;
if (pathname === "/bize-ulasin") {
app.render(req, res, "/contact-us", query);
}else{
handle(req, res, parsedUrl);
}
}).listen(3000, (err) => {
if (err) throw err;
console.log("> Ready on http://localhost:3000");
});
});
/pages/contact-us-file
import { Fragment } from "react";
import Head from "next/head";
import { useTranslation } from "next-i18next";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
const ContactUs = () => {
const { t } = useTranslation("common");
return (
<Fragment>
<Head>
<title>Contact-Us</title>
</Head>
</Fragment>
);
};
export const getStaticProps = async ({ locale }) => {
console.log(locale); // When I use the URL localhost: 3000/bize-ulasin, it returns undefined.
return {
props: {
...(await serverSideTranslations(locale, ["common"])),
},
};
};
export default ContactUs;
How can I access the "locale" variable with getStaticProps? Or, how can I use the following URLs with the same page file?
->localhost:3000/contact-us
->localhost:3000/bize-ulasin
I also faced the same problem today. That's how I solved the issue.
First of all, delete the server.js file. With Next.JS 10, using server.js will create conflict with the i18n routes and you won't be able to get the locale data in getStaticProps.
NextJS has a beautiful method named rewrites. We will use that instead of our server.js file. For example, if you have a page named contact-us-file, we can rewrite our next.config.js file as
const { i18n } = require('./next-i18next.config')
module.exports = {
i18n,
async rewrites() {
return [
{
source: '/contact-us',
destination: '/en/contact-us-file',
},
{
source: '/bize-ulasin',
destination: '/tr/contact-us-file',
},
]
},
}
As you are already using Next-i18next, I hope you are familiar with the file that I am importing.
Now If you try to navigate localhost:3000/contact-us and localhost:3000/bize-ulasin you should be able to access your contact us page.
Well, I'm starting with nuxt and I have following routes:
/home
/dashboard
/login
I want to protect the /dashboard, but only for users logged in with a token in Cookie.
Then i created a middleware
/middleware/auth.js
import Cookie from 'js-cookie'
export default function({ req, redirect }) {
if (process.server) {
if (!req.headers.cookie) return redirect('/login')
const jwtCookie = req.headers.cookie.split(';').find(c => c.trim().startsWith('jwt='))
if (!jwtCookie) return redirect('/login')
} else {
const jwt = Cookie.get('jwt')
if (!jwt) { window.location = '/login' }
}
}
and register the middleware in my layout or dashboard page
<script>
export default {
middleware: 'auth',
}
</script>
when I access /dashboard apparently works perfectly
but the problem is that the middleware is being registered globally, it is running on all pages, all routes
So when you access /home that is a published page, if you do not have the cookie, you end up being redirected to login page
anyone help?
How about creating a condition based on the route.path param ?
export default function({ req, redirect, route }) {
if (!route.path.includes('dashboard')) { // if path doesn't include "dashboard", stop there
return;
}
if (process.server) {
if (!req.headers.cookie) return redirect('/login')
const jwtCookie = req.headers.cookie.split(';').find(c => c.trim().startsWith('jwt='))
if (!jwtCookie) return redirect('/login')
} else {
const jwt = Cookie.get('jwt')
if (!jwt) { window.location = '/login' }
}
}
Therefore you still benefit from the pre-render middleware system.
You probably have registered your middleware/auth.js in your nuxt.config.js.
When you register a middleware in nuxt.config.js, you're registering it globally, meaning it will be called for every route change.
Docs:
https://nuxtjs.org/guide/routing#middleware
In my opinion, you should call them plugin, because of
middleware called by each route changed also you can't use middleware in layout and subComponent, you can use it as plugin and call it manually everywhere also it's reactive and runtime.
path: /plugind/auth.js
import Cookie from 'js-cookie';
export default function({ req, redirect }) {
if (process.server) {
if (!req.headers.cookie) return redirect('/login')
const jwtCookie = req.headers.cookie.split(';').find(c =>
c.trim().startsWith('jwt='))
if (!jwtCookie) return redirect('/login')
} else {
const jwt = Cookie.get('jwt')
if (!jwt) { window.location = '/login'
}
}
}