I'm trying to setup a authenticated connection to Firebase using WebKit.
The below code "works" in a sense that I receive the data from Firebase Realtime Database as expected.
index.svelte
<script context="module">
// import serviceAccount from '../private/my-firebase-private-key.json'
import { google } from 'googleapis'
let firebaseAccessToken
const serviceAccount = {
"type": "service_account",
"project_id": "my-test-project-xxxxxx",
"private_key_id": "0d33XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"private_key": "-----BEGIN PRIVATE KEY-----\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-----END PRIVATE KEY-----\n",
"client_email": "firebase-adminsdk-xxxxx#my-test-project-xxxxxx.iam.gserviceaccount.com",
"client_id": "9999999999999999999999",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-xxxxxx%40my-test-project-xxxxxx.iam.gserviceaccount.com"
}
async function firebaseAuth() {
let scopes = [
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/firebase.database',
]
let jwtClient = new google.auth.JWT(
serviceAccount.client_email,
null,
serviceAccount.private_key,
scopes
)
return new Promise((
resolve,
reject
) =>
jwtClient.authorize((error, tokens) => {
if (error) {
console.log('Error making request to generate access token:', error)
reject(error)
} else if (tokens.access_token === null) {
console.log(
'Provided service account does not have permission to generate access tokens'
)
reject(error)
} else {
firebaseAccessToken = tokens.access_token
resolve(tokens.access_token)
}
})
)
}
// export async function load({ page, fetch, session, context }) {
export async function load({ fetch }) {
await firebaseAuth()
// console.log(`firebaseAccessToken: ${firebaseAccessToken}`)
const res = await fetch(
'https://myfirebasedatabase.app/jsondb.json',
{
headers: { 'Authorization': `Bearer ${firebaseAccessToken}` },
}
)
const data = await res.json()
if (!res.ok) {
return {
status: res.status,
error: new Error('Could not fetch data'),
}
}
return { props: { myData : data } } <-- DATA is RECEIVED
}
</script>
<script>
export let myData
onMount(() => {
console.log(myData) <---------------- NEVER EXECUTED
})
</script>
The problem is that the UI shows the following error ...
500: Module "stream" has been externalized for browser compatibility and cannot be accessed in client code.
... handled by this __error.svelte file
<script context="module">
export function load({ error, status }) {
return {
props: {
title: `${status}: ${error.message}`
}
};
}
</script>
<script>
export let title;
</script>
<h1>{title}</h1>
I feel like it has something to do with Vite or another configuration stuff but I have too little experience with svelte to be sure. Did you have this kind of error previously?
Thanks for your help.
Side note: the code codes from NodeJs Authenticate REST Requests Documentation from Google and uses the Google APIs NodeJS Library
I have the same error only writen only this line of code
import { google } from 'googleapis'
get the same error 500.
But when add this config for vite server in svelte.config.js file
//svelte.config.js
vite: {
optimizeDeps: {
exclude: ['googleapis']
}
}
get this error:
The requested module '/node_modules/googleapis/build/src/index.js?v=9d844668' does not provide an export named 'google'
I've seen in the browser/devtools network option, when i use exclude de size of index.js file is 70 Kb, but when is use include the size is 46 Mb.
I think is purely error of vite server to try load node_modules in webbrowser of client side.
If i found a solution for this error surely share the solution
Regards!.
Related
This question is more about how Vercel serverless functions work. I have requested an express.js with MongoDB and taking around 0.078 seconds to at most 1.856 seconds.
getServerSideProps works perfect on LocalHost. However, whenever I use the getServerSideProps, I get the error
[GET] /articles/637a08a20218e2e3c841e8d7
22:58:07:13
2022-11-20T20:58:17.290Z 881d4090-532d-42df-9ee2-dd759a6eae08 Task timed out after 10.02 seconds
Of course, getStaticPaths and getStaticProps works perfectly but I'll be having around 100000 dynamic routes to create which isn't scalable. I'm already using SWR for index pages (list of articles with pagination) but I can't use it for dynamic pages because it's bad for SEO
My question is, what's the cause of this timeout?
API: Express hosted on AWS Elastic Beanstalk, MongoDB Atlas serverless
Next.js
// [articleID].tsx
export const getServerSideProps: GetServerSideProps = async (context) => {
const { article } = context.params;
const article = await getFetcher(`/articles/${articleID}`);
if (!article) {
return {
notFound: true,
};
}
return {
props: { article },
};
};
Reusable fetch
async function getFetcher(path: string) {
if (!path) return;
const url = `${API_BASE_URL}${path}`;
// headers
const headers = {
Authorization: AUTHORIZED_ACCESS_TOKEN,
"Content-type": "application/json",
};
const config = { headers };
const response = await axios
.get(url, config)
.then((res) => res.data)
.catch((error) => {
console.log("GET Resource Error");
console.log(error);
return { success: false, error, message: error.message };
});
return response;
}
Here is my situation. I am trying to set up a Next.js project with an Express.js back-end. I did set up the back-end as a regular one not as a custom server according to Next.js documentation. So I am not sure if I am already making a mistake by setting up the back-end as a regular one. I am trying to fetch data from a back-end endpoint http://localhost:3500/api/v1/list using axios and it works well. But when I am trying to implement React-Query on the first load I am getting the right data from the back-end but when it is trying to re-fetch for some reason it is hitting the wrong end-point http://localhost:3600/api/v1/list and getting the 404 Not Found error. It looks like it is switching the port from 3500 to 3600 which is a front-end port. Here you will find the link to the repository and here is the code. Let me know if I am doing something wrong,
page/sell.js
import axios from 'axios';
import { useQuery } from 'react-query';
export default function SellPage({ response }) {
const { data, isLoading, error } = useQuery('sells', getPosts, {
initialData: response,
});
console.log('data useQuery: ', data);
if (isLoading) return 'Loading...';
if (error) return error.message;
return (
<div>
<p>Hello SellPage!!{data.message}</p>
</div>
);
}
export async function getServerSideProps(context) {
const response = await getPosts();
console.log('response', response);
if (!response) {
return {
notFound: true,
};
}
return {
props: { response }, // will be passed to the page component as props
};
}
async function getPosts() {
console.log('HOST: ', process.env.HOST);
const { data } = await axios.request({
baseURL: process.env.HOST,
url: '/api/v1/list',
method: 'get',
});
return data;
}
_app.js
import React from 'react';
import { QueryClient, QueryClientProvider } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools';
const queryClient = new QueryClient();
function MyApp({ Component, pageProps }) {
const queryClientRef = React.useRef();
if (!queryClientRef.current) {
queryClientRef.current = new QueryClient();
}
return (
<>
<QueryClientProvider client={queryClientRef.current}>
<Component {...pageProps} />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
</>
);
}
export default MyApp;
I don't see a next.config.js in your repo, so I guess the env variables are not bundled in the js and in the end you url looks like localhost: or localhost:undefined which the browser default to the port your client is served.
Try add next.config.js
module.exports = {
env: {
HOST: process.env.HOST,
},
}
SEE: https://nextjs.org/docs/api-reference/next.config.js/environment-variables
Another way is to use public runtime variables
module.exports = {
publicRuntimeConfig: {
// Will be available on both server and client
port: 3500,
},
};
// Then
import getConfig from 'next/config';
// Only holds serverRuntimeConfig and publicRuntimeConfig
const { publicRuntimeConfig } = getConfig();
console.log(publicRuntimeConfig.port);
SEE: https://nextjs.org/docs/api-reference/next.config.js/runtime-configuration
But note that runtime config would impact optimization and you might get larger bundle in the end so you might want to try build time variables first.
if you get 404
i think that you reached out the server but no API's name matched
so try to test the API first on postman or alike
however if console.log('HOST: ', process.env.HOST);
prints http://localhost:3600 then do the following
in your .env file try to rename PORT TO SERVER_PORT or whatever
HOSTNAME=localhost
SERVER_PORT=3500
HOST=http://$HOSTNAME:$SERVER_PORT
i'm not sure but maybe ur frontend serve bash hold PORT env val as 3600
I am using nuxt and would like to use this library: https://github.com/nuxt-community/recaptcha-module. But I don't understand how to verify that the user has passed the check. The example doesn't tell me too much (https://github.com/nuxt-community/recaptcha-module/blob/master/example/v3/pages/index.vue). Could someone show me how to do it correctly?
This example is only half the story. It returns a Recaptcha V3 token on the client-side.
This must then be sent to the serverside and verified using your secret key.
This is done by sending a post to this URL:
const url = `https://www.google.com/recaptcha/api/siteverify?secret=${secretKey}&response=${token}`;
You do not want to allow this secret key on the client side.
To achieve this in Nuxt, assuming version 2.13+, you can utilise privateRuntimeConfig in your nuxt config.
This will allow you to link a .env file to be injected only on the server side.
For this use case, a privateRuntimeConfig like this should suffice:
privateRuntimeConfig: {
secretKey: process.env.GOOGLE_SECRET
}
Once you have done this, you will be able to access these variables as part of this.$config within your Nuxt application - in this case this.$config.secretKey when calling the Recaptcha verify endpoint.
For more information check out the Nuxt blog
Use https://github.com/nuxt-community/recaptcha-module, in your nuxt.config.js
modules: [
'#nuxtjs/recaptcha',
],
recaptcha: {
hideBadge: true,
siteKey: "ABC...", // Better would be from 'process.env.API_KEY' and with '.env' file
version: 2, // Or 3
},
Keep in mind that modules, that's not the same as buildModules (sometimes it might confuse due to similar naming).
Here is a working implementation for ReCaptcha V3:
package.json
"dependencies": {
"#nuxtjs/axios": "^5.13.6",
"#nuxtjs/recaptcha": "^1.0.4",
"h3": "^0.3.9",
},
Note the h3 version. I wasn't able to get it working with a newer version of that because the library is converted to EJS/mjs and TypeScript, which conflicts with Nuxt. Transpiling h3 didn't fix it. It may work with Nuxt V3+.
nuxt.config.js
modules: [
['#nuxtjs/recaptcha', {
siteKey: process.env.RECAPTCHA_SITE_KEY,
version: 3,
}],
],
serverMiddleware: [
{ path: '/api/check-token', handler: '~/middleware/recaptcha' },
],
middleware/recaptcha.js
import { useBody } from 'h3';
import axios from 'axios';
export default async (req, res) => {
res.setHeader('Content-Type', 'application/json');
try {
const { token } = await useBody(req);
if (!token) {
res.end(
JSON.stringify({
success: false,
message: 'Invalid token'
})
);
return;
}
axios.get(`https://www.google.com/recaptcha/api/siteverify?secret=${process.env.RECAPTCHA_SECRET_KEY}&response=${token}`).then((answer) => {
if (answer.status) {
res.end(
JSON.stringify({
success: true,
message: 'Token verified'
})
);
} else {
res.end(
JSON.stringify({
success: false,
message: 'Invalid token'
})
);
}
});
} catch (e) {
console.log('ReCaptcha error:', e);
res.end(
JSON.stringify({
success: false,
message: 'Internal error'
})
);
}
};
.env
RECAPTCHA_SITE_KEY=gosei478htosvei478tvoei478tvge
RECAPTCHA_SECRET_KEY=ios47eos487t6es4897gtv6es487
index.vue
async mounted() {
try {
await this.$recaptcha.init();
} catch (err) {
throw new Error(`index# Problem initializing ReCaptcha: ${err}.`);
}
},
beforeDestroy() {
this.$recaptcha.destroy();
},
methods: {
async submitContactForm() {
try {
const token = await this.$recaptcha.execute('contact')
const formData = {
email: this.contactForm.email,
firstname: name.firstName,
lastname: name.lastName,
phone: this.contactForm.phone,
band_name: this.contactForm.band_name,
initial_message: this.contactForm.message,
}
// note: use POST request
const recaptcha = await this.$axios.post('/api/check-token', { token });
console.log('recaptcha', recaptcha.data);
if (recaptcha.data.success) {
const result = await this.$axios.post(process.env.CONTACT_FORM_API, formData);
// cleanup logic
} else {
// handle error case
}
} catch (err) {
// handle errors
}
},
},
You can read more here: https://www.npmjs.com/package/#nuxtjs/recaptcha
Note the section where it says
Server Side
When you send data + token to the server, you should verify the token on the server side to make sure it does not requested from a bot. You can find out how to verify token on the server side by looking at the server middleware inside v2 example. (The server side is same for both versions)
The above server-side middleware comes from there. It is important to use the version of h3 that I suggest because you need it to access useBody(req). I tried for several hours to find another way to read the request body but it proved too difficult. Your results may vary in a newer version of Nuxt. I suggest trying the newest version of h3 and if that fails with errors when building the application, try the older version.
It is critically important to not expose the ReCaptcha secret key, and this solution keeps it secret in the server-side.
A more optimal solution might be to use your actual server and make an endpoint for validating ReCaptcha tokens. This above solution allows you to do it purely client-side, assuming you are using SSR.
I'm pretty new to react and I'm trying to implement a service worker at the moment.
Actually I always get an error 'Uncaught SyntaxError: Unexpected token 'export'' in my "serviceworker.js" class.
Here's my main.tsx file.
import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css';
import App from './app/app';
import * as registerServiceWorker from './serviceworker/serviceworker';
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker.register();
And thats my "serviceworker.js" file.
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
)
export function register(config) {
if ('serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
// const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
// if (publicUrl.origin !== window.location.origin) {
// // Our service worker won't work if PUBLIC_URL is on a different origin
// // from what our page is served on. This might happen if a CDN is used to
//
// return;
// }
window.addEventListener('load', () => {
const swUrl = `/serviceworker/serviceworker.js`;
if (isLocalhost) {
// This is running on localhost. Let's check if a service worker still exists or not.
checkValidServiceWorker(swUrl, config);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, '
);
});
} else {
// Is not localhost. Just register service worker
registerValidSW(swUrl, config);
}
});
}
}
function registerValidSW(swUrl, config) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
'New content is available and will be used when all ' +
'tabs for this page are closed. See /CRA-PWA.'
);
// Execute callback
if (config && config.onUpdate) {
config.onUpdate(registration);
}
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
// Execute callback
if (config && config.onSuccess) {
config.onSuccess(registration);
}
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl)
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type');
if (
response.status === 404 ||
(contentType === null)
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl, config);
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
});
}
}
Any idea what I did wrong here?
I already added Babel as suggested in this thread react export Unexpected token but the error didn't disappear.
I already tried to export it via modules.export, but no sucess either.
Thanks in advance!
EDIT:
Thats what my ".babelrc" looks like:
{
"presets": ["#babel/preset-react"],
"plugins": [
"babel-plugin-transform-export-extensions",
"transform-es2015-modules-commonjs"
]
}
Thats what my ".babelrc" looks like:
{
"presets": ["#babel/preset-react"],
"plugins": [
"babel-plugin-transform-export-extensions",
"transform-es2015-modules-commonjs"
]
}
This problem occurs because you are trying to use the same file as the service worker you are registering it with. Because of this, the browser cannot figure out which service worker features you need.
Use for example this content for service-worker.js in your public folder:
self.addEventListener('push', (event) => {
const data = event.data.json();
console.log('New notification', data);
event.waitUntil(
self.registration.showNotification(data.title, {
body: data.description,
icon: data.icon,
})
);
});
I'm attempting to add an Axios plugin to Nuxt as described here, but it doesn't seem to work.
This is my plugins/axios.js file...
export default function({ $axios }) {
console.log('Im in the axios plugin')
$axios.defaults.baseURL = `https://localhost:5001/api`
$axios.defaults.headers = {
Accept: 'application/json',
'Content-Type': 'application/json'
}
$axios.onRequest((config) => {
console.log('Making request to ' + config.url)
})
}
This is my nuxt.config.js
plugins: ['~/plugins/axios'],
modules: ['#nuxtjs/axios']
And this is where I use Axios in a file called services/BookService.js:
import axios from 'axios'
export default {
getBooks() {
return axios.get('/Home')
},
getBooksFiltered(payload) {
return axios.post('/Home/Filters', payload)
}
}
I get the console.log('Im in the axios plugin') from within my plugin, but nothing else. $axios.onRequest doesn't appear to run, and the baseURL doesn't appear to be set correctly when getBooksFiltered is triggered. I get a 404 when it tried to hit the address http://localhost:3000/Home/Filters. As described in my plugin, the address should be https://localhost:5001/api/Home/Filters
I've also tried the following in my nuxt.config.js, but it doesn't work:
axios: {
baseURL: 'https://localhost:5001/api'
}
Any ideas?
Edit
I've modified my services/BookService.js based on the suggestion below to the following...
export default {
getBooks(axios) {
console.log('Im in getBooks')
return axios.get('/Home')
}
}
My action request that makes my api call is the following....
import BookService from '~/services/BookService.js'
export const fetchBooks = (context) => {
console.log('Im in fetchBooks action')
return BookService.getBooks(this.$axios)
.then((response) => {
context.commit('SET_BOOKS', response.data.booksList)
})
.catch((error) => {
console.log(error)
})
}
And my method in my component that calls the actions...
async fetch({ store, error }) {
try {
console.log('Im in index -> fetch')
await store.dispatch('fetchBooks')
} catch (e) {
error({
statusCode: 503,
message: 'Unable to fetch books at this time'
})
}
}
I'm aware that I may be mixing async/await with promises incorrectly but I don't believe it's the cause of this issue.
Console returns the following...
My network tab contains a single request to http://localhost:3000/ which seems incorrect. It should be https://localhost:5001/api/Home based on the plugin and the address specified in the action. It is also never entering $axios.onRequest
The axios-module sets up an Axios instance on the Nuxt app instance. When you import Axios from axios, and use it directly, you're not using the previously setup Axios instance.
To fix the issue, you could either reference the preconfigured Axios instance from window.$nuxt.$axios (only in the browser), or setup your service to take an Axios instance as a parameter:
// services/BookService.js
export default axios => ({
getBooks() {
return axios.get('/Home')
},
getBooksFiltered(payload) {
return axios.post('/Home/Filters', payload)
}
})
// store.js
import BookService from '~/services/BookService.js'
export default {
actions: {
async getBooks({ commit }) {
const books = await new BookService(this.$axios).getBooks()
commit('SET_BOOKS', books)
}
}
}
Another solution from nuxt-community/axios-module #28:
~/plugins/axios-port.js
import { setClient } from '~/services/apiClient'
export default ({ app, store }) => {
setClient(app.$axios)
}
~/services/apiClient.js
let client
export function setClient (newclient) {
client = newclient
}
// Request helpers
const reqMethods = [
'request', 'delete', 'get', 'head', 'options', // url, config
'post', 'put', 'patch' // url, data, config
]
let service = {}
reqMethods.forEach((method) => {
service[method] = function () {
if (!client) throw new Error('apiClient not installed')
return client[method].apply(null, arguments)
}
})
export default service
Use:
import apiClient from '~/services/apiClient'
export default {
async current () {
return apiClient.get('...')
}
}
In my case I exported a customized axios instance as the doc suggested in my axios.js
export default function ({ $axios }, inject) {
const api = $axios.create({
baseURL:'/api'
})
// Inject to context as $api
inject('api', api)
}
Then use this.$api.get or this.$api.post in your getBook service
The above one works for me
As I have just tested, in each request we should use $axios.
Example: this.$axios.get('....'), or in another context this.$nuxt.$axios.get('...');
Because axios extension use with the app context instance, if we import, it will create a new instance which plugin cannot extend.
I have put test code on stackblitz: here
It seems you need to yarn add #nuxtjs/axios or npm install #nuxtjs/axios like the setup instruction here before it can work: https://axios.nuxtjs.org/setup
I haven't experienced with nuxt yet but I don't think by adding some line of code into some js file without actually installing will make the package available into your repo.