Is it possible with Sanity? scheduled external API calls to Sanity - sanity

Here is what I would like to accomplish?
1- Make a fetch call to a news API everynight
2- Pull latest headlines
3- Import to Sanity...
Does Sanity let you do that? Does it have a cron job?
Thank you,

Sanity does not provide any native way to do that, but you could create cron jobs with an external serverless provider that does the functionality you're looking for 🤔
Firebase scheduled functions are an example of provider, but there are many more options. I suggest taking a look at Serverless framework examples.
After you figure out what you'll use, then use Sanity's HTTP API or one of their API clients (#sanity/client for Javascript) to add the data to it. You'll need a token to do so, as POST requests are protected behind an auth wall (take a look at their Authentication docs).
Here's an example of what that could look like in Javascript:
// For Javascript - using the #sanity/client npm package
const allHeadlines = await pullHeadlines(/* ... */);
// Create a single transaction that will handle
// the creation of all headline documents
const transaction = sanityClient.transaction();
for (const headline of allHeadlines) {
transaction = transaction.create({ title: headline.title, ...headline });
}
transaction
.commit()
.then((res) => {
console.log("All headlines created");
// Finish the cron job here
})
.catch((error) => {
console.error("Couldn't create the headline documents", error);
// Maybe retry it here?
});
Hope that helps, Timur 🙌

Related

electron.js and sql - correct way to set it up?

I am new to electron.js - been reading the documentation and some similar post here:
How do I make a database call from an Electron front end?
Secure Database Connection in ElectronJS Production App?
Electron require() is not defined
How to use preload.js properly in Electron
But it's still not super clear how to properly implement a secure SQL integration. Basically, I want to create a desktop database client. The app will connect to the remote db and users can run all kind of predefined queries and the results will show up in the app.
The documentation says that if you are working with a remote connection you shouldn't run node in the renderer. Should I then require the SQL module in the main process and use IPC to send data back and forth and preload IPCremote?
Thanks for the help
Short answer: yes
Long answer:
Allowing node on your renderer poses a big security risk for your app. It is best practices in this case to create pass a function to your preloader. There are a few options you can use to do this:
Pass a ipcRenderer.invoke function wrapped in another function to your renderer in your preload. You can then invoke a call to your main process which can either send info back via the same function or via sending it via the window.webContents.send command and listening for it on the window api on your renderer. EG:
Preload.js:
const invoke = (channel, args, cb = () => {return}) => {
ipcRenderer.invoke(channel, args).then((res) => {
cb(res);
});
};
const handle = (channel, cb) => {
ipcRenderer.on(channel, function (Event, message) {
cb(Event, message);
});
};
contextBridge.exposeInMainWorld("GlobalApi", {
invoke: invoke,
handle:handle
});
Renderer:
let users
window.GlobalApi.handle("users", (data)=>{users=data})
window.GlobalApi.invoke("get", "users")
or:
let users;
window.GlobalApi.invoke("get", "users", (data)=>{users=data})
Main:
ipcMain.handle("get", async (path) => {
let data = dbFunctions.get(path)
window.webContents.send(
path,
data
);
}
Create a DB interface in your preload script that passes certain invocations to your renderer that when called will return the value that you need from your db. E.G.
Renderer:
let users = window.myCoolApi.get("users");
Preload.js:
let get = function(path){
let data = dbFuncions.readSomeDatafromDB("path");
return data; // Returning the function itself is a no-no shown below
// return dbFuncions.readSomeDatafromDB("path"); Don't do this
}
contextBridge.exposeInMainWorld("myCoolApi", {
get:get
});
There are more options, but these should generally ensure security as far as my knowledge goes.

Vue PWA caching routes in advance

I'm hoping someone can tell me if I'm barking up the wrong tree. I have built a basic web app using Vue CLI and included the PWA support. Everything seems to work fine, I get the install prompt etc.
What I want to do, is cache various pages (routes) that user hasn't visited before, but so that they can when offline.
The reason here is that I'm planning to build an app for an airline and part of that app will act as an in flight magazine, allowing users to read various articles, however the aircrafts do not have wifi so the users need to download the app in the boarding area and my goal is to then pre cache say the top 10 articles so they can read them during the flight.
Is this possible? and is PWA caching the right way to go about it? Has anyone does this sort of thing before?
Thanks in advance
To "convert" your website to an PWA, you just need few steps.
You need to know that the service worker is not running on the main thread and you cant access for example the DOM inside him.
First create an serviceworker.
For example, go to your root directory of your project and add a javascript file called serviceworker.js this will be your service worker.
Register the service worker.
To register the service worker, you will need to check if its even possible in this browser, and then register him:
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/serviceworker.js').then(function(registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope');
}, function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
});
}
In vue.js you can put this inside mounted() or created() hook.
If you would run this code it will say that the service worker is successfully registered even if we havent wrote any code inside serviceworker.js
The fetch handler
Inside of serviceworker.js its good to create a variable for example CACHE_NAME. This will be the name of your cache where the cached content will be saved at.
var CACHE_NAME = "mycache_v1";
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.open(CACHE_NAME).then(function(cache) {
return cache.match(event.request).then(function (response) {
return response || fetch(event.request).then(function(response) {
cache.put(event.request, response.clone());
return response;
});
});
})
);
});
Everytime you make a network request your request runs through the service worker fetch handler here first. You need to response with event.respondWith()
Next step is you first open your cache called mycache_v1 and take a look inside if there is a match with your request.
Remember: cache.match() wont get rejected if there is no match, it just returns undefined because of that there is a || operator at the return statement.
If there is a match available return the match out of the cache, if not then fetch() the event request.
In the fetch() you save the response inside the cache AND return the response to the user.
This is called cache-first approach because you first take a look inside the cache and in case there is no match you make a fallback to the network.
Actually you could go a step further by adding a catch() at your fetch like this:
return response || fetch(event.request).then(function(response) {
cache.put(event.request, response.clone());
return response;
})
.catch(err => {
return fetch("/offline.html")
});
In case there is nothing inside the cache AND you also have no network error you could response with a offline page.
You ask yourself maybe: "Ok, no cache available and no internet, how is the user supposed to see the offline page, it requires internet connection too to see it right?"
In case of that you can pre-cache some pages.
First you create a array with routes that you want to cache:
var PRE_CACHE = ["/offline.html"];
In our case its just the offline.html page. You are able to add css and js files aswell.
Now you need the install handler:
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
return cache.addAll(PRE_CACHE);
})
);
});
The install is just called 1x whenever a service worker gets registered.
This just means: Open your cache, add the routes inside the cache. Now if you register you SW your offline.html is pre-cached.
I suggest to read the "Web fundamentals" from the google guys: https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook
There are other strategies like: network-first
To be honest i dont know exactly how the routing works with SPAs because SPA is just 1 index.html file that is shipped to the client and the routing is handled by javascript you will need to check it out witch is the best strategie for your app.

how do i authenticate my vuejs app using azure active directory and get the security groups to which the user belong to

i saw something similar here: How do you authenticate a VueJS app with Azure AD?
but it did not work for me...
my problem is that after authenticating the user at login - i still needed to get the users security groups and that information was not received using the graph-api described in the above mentioned post
thank you for any help
it was something that took me a long time to figure out so im posting my findings here, hopfully this will help someone:
this was a hard one for me so im posting here - hopfully this will save some time to someone:
my problem was that i need not only to authenticate my vuejs app with azure-ad, but i need also to get the security groups to which the user is belonging to.
to achive this, this is what i done:
i used the vue-adal sample app mentioned above( you can find it in: https://github.com/survirtual/vue-adal ) - under sample folder.
but i still had to make some changes to make it behave the way i need. the problam was that after logging in with my user the sample app used windows.net graph api for retrieving user info with the token from the user authentication, so i had to change in main.js this:
const graphApiBase = `https://graph.windows.net`
const graphApiResource = '00000002-0000-0000-c000-000000000000'
to this:
const graphApiBase = `https://graph.microsoft.com/v1.0`
const graphApiResource = '00000003-0000-0000-c000-000000000000'
in addition, inside the return url component i had to change the axios query to get the security groups to which the user belongs to..so i changed this (in the home.vue file):
async getUserInfo () {
let res = await this.$graphApi.get(`me`, {
params: {
'api-version': 1.6
}
})
to this:
async getUserInfo () {
let res = await this.$graphApi.post(`/me/getMemberGroups`, {
securityEnabledOnly: true
})
console.log(res)
return res.data
}
and then the data that i received back from the api contained the security groups to which the user belongs to...

How to implement Auth0 server-side with Nuxtjs?

I have a Nuxt app with authentication already running in universal mode.
I'm trying to convert the authentication service to Auth0. I'm following the Vue quickstart, but I discovered that auth0-js is a client side library since it uses a lot of 'window'-stuff that is not available on the server-side of Nuxt.
However, I got it kind of working by making it a client-side plugin and wrap all functions (that is calling the authservice in the lifecycle hooks) in a process.client check. It works "kind of" because when going to the protected page whilst not logged in, it flashes the page before being redirected to login page (since its rendered on the server-side as well, but the check only happens once it's delivered on the client side I presume).
My question now is: What can I do in order to add the check to server-side as well? (or at least make sure that the protected pages isn't flashed before being redirected).
What I've tried so far:
Saving the payload and the logged-in state in the store and check in some custom middleware, but that didn't do the trick.
Also, it seems to me that #nuxt/auth is outdated or something and the nuxt auth0 example as well. It uses auth0-lock while I'm using the new auth0 universal.
Anyone have suggestions on how to solve this issue? Thanks in advance!
not sure if this will be any help and have only answered a few questions (other account long time ago).
Update.. I read my answer then the question title (I think my answer does cover some of your context), but in regards to the title you could also look at using auth as a plugin. You can then handle stuff there before the page is hit.
I am not sure how your code is implemented, but this may help (hopefully).
If you are not using Vuex, I strong recommend it. Nuxt Vuex Store Guide
// index/store.js
// At least have the store initialized, but its most likely going to be used..
// page.vue
<template>
...
<div v-else-if="!$auth.loggedIn">
{{ test }}
</div>
...
...
data() {
if (!this.$auth.loggedIn) {
const test = 'Only this will load, no flash'
return { test }
}
}
$auth.loggedIn is built in, I read it ..somewhere.. in the docs
This will solve the no flash issue, you can also take advantage of a loader screen and asyncData to check the state before rendering the view to avoid a flash and populate data if it hangs.
You could also try using Vuex Actions, I am currently playing with these 2 in the process of where I am now. Learning about nuxtServerInit()
// store/index.js
import axios from 'axios'
export const actions = {
nuxtServerInit ({commit}, {request}) {
// This is good if you have the user in your request or other server side stuff
if (request.user) commit('SET_USER', request.user)
},
async GET_USER({ commit }, username) {
const user = await axios.get(`/user/${username}`)
if (user) commit('SET_USER', user)
}
}
export const mutations = {
SET_USER(state, user) {
// simple set for now
state.auth.user = user || null
}
}
The second one is combined using the fetch() method on the page itself.
// page.vue
async fetch({ $auth, store }) {
await store.dispatch('GET_USER', $auth.$state.user)
}
Now you can call $auth.user in your code as needed.
$auth.user is another built in I read ..somewhere..
You can also call $auth.user with the $auth.loggedIn to check if user exists on top of being logged in $auth.user && $auth.loggedIn.
It may be this.$auth.<value> depending on where you are trying to reference it.
I learned the asyncData() gets call first and logs in my server, then data() logs values in the server console as well (false, null), but in my Brave console they're undefined, i'd like an answer to that lol
I have been struggling with trying to get Auth0 to work how I wanted with JWTs, but as I kept crawling I found useful bits along the way (even in old demos such as the one you mentioned, just nothing with the lock stuff...). Also in terms of express and my API in general... Anyways, hope this helped (someone).

vue server side rendering and data population

Im currently refactoring an app and converting all my base code into vue. One of my requirements is to do server side rendering.
I have been follow vue ssr example along with hacker news example to help me understand ssr.
I do have however a question for which I cant find any good answer, and before further development, I want to make sure we are doing the right thing.
I want to know if its a good practice to have some actions in a vue store calling an api for server side rendering.
All the examples I have found deal with a real external endpoint they connect and perform request. But that is not the setup we have.
We do have a "normal" express app with its own endpoints; so, for example, express router looks a bit like this:
// This is our post model, it lives on the same app, so its not a external API
app.get('/posts', (req, res) => Posts.getPosts());
// Resolve request for SSR rendering, pretty much the same as in [vue ssr example](https://ssr.vuejs.org/guide/routing.html#routing-with-vue-router)
app.get(
const context = { url: req.url }
createApp(context).then(app => {
renderer.renderToString(app, (err, html) => {
if (err) {
if (err.code === 404) {
res.status(404).end('Page not found')
} else {
res.status(500).end('Internal Server Error')
}
} else {
res.end(html)
}
})
})
);
This part works fine on both client and server. If you request something to /posts you get your response back.
To have a more modular code, we are using vuex stores; being one of the actions called fetchPosts and this action is the responsible of fetching the current posts in the view.
...
actions: {
fetchPosts({ commit }) {
return $axios.get('/posts').then((response) => {
commit('setPosts', {
posts: response.data
});
});
}
},
...
I believe for client side this is good, but when rendering on the server, this is probably not optimal.
Reason being axios performing an actual http request, which will also have to implement auth mechanism, and in general, really poor performant.
My question is: is this the recommended and standard way of doing this ?
What are other possibilities that works in server and client ?
Do people actually creates separated apps for api and rendering app ?
Thanks !
I know this is an old question, but for future visitors:
The recommended way is to leverage webpack module aliases to load a server side api for the server and a client side api for the browser. You have to share the same call signature which allows the api to be "swapped".
This of course greatly improves performance as the server side api can do direct db queries instead fetching data over http.
In essence your webpack.server.config.js should have:
resolve: {
alias: {
'create-api': './create-api-server.js'
}
}
In your webpack.client.config.js:
resolve: {
alias: {
'create-api': './create-api-client.js'
}
}
Importing create-api will now load the required api.
Have a look at https://github.com/vuejs/vue-hackernews-2.0 to see a full example.