How to make my SvelteKit API work in production (Github Pages)? - github-pages

Background
I have my project deployed to Github Pages here: https://zeddrix.github.io/jw-guitar-tabs, so I have this in my svelte.config.js file:
kit: {
...
paths: {
base: '/jw-guitar-tabs'
},
appDir: 'internal',
...
}
I have this in my __layout.svelte:
<script lang="ts">
import { base } from '$app/paths';
...
const fetchFromDBAndStore = async (category: SongCategoriesType) => {
const res = await fetch(`${base}/api/categories/original-songs`);
const data = await res.json();
console.log(data);
...other code...
};
...I have my code here that uses this data...
</script>
<Layout><slot /></Layout>
Side note: I put it in this file so that this runs on any page, but I have a code to make sure that this doesn't run if I already have the data. This is not the issue.
This calls on the file: src/routes/api/categories/original-songs.ts:
import fetchSongsDB from '$utils/fetchSongsDB';
export const get = async () => fetchSongsDB('originals');
And this fetchSongsDB function fetches the songs from my database.
Everything is working fine in development mode when I run npm run dev and even in preview mode when I run npm run preview after build, of course, in localhost:3000/jw-guitar-tabs.
Issue
However, on the static github page at https://zeddrix.github.io/jw-guitar-tabs, I get this:
It serves the 404 Github Page as the response. I guess it's because it can't find the src/routes/api/categories/original-songs.ts file. But of course Github will not find this file because the deployed folder to gh-pages is the build folder so I don't have this original file route anymore.
How would I solve this?

Rename original-songs.ts to original-songs.json.ts
Change the
fetch(`${base}/api/categories/original-songs`);
to
fetch(`${base}/api/categories/original-songs.json`);
That allows the adapter-static to generate the json files at build time.
(These are static files, to see changes made in mongodb will require a rebuild & redeploy to GitHub pages)
Sometimes the static adapter doesn't autodetect the endpoint.
You can help the adapter by specifying the url in svelte.config.js
kit: {
prerender: {
entries: [
"*",
"/api/categories/original-songs.json",
Bonus tip: The /favorites url redirects to /favorites/ when refreshing the page, add trailingSlash: "never", to kit to generate favorites.html instead of favorites/index.html and keep the /favorites url on refresh.

Related

React Native Expo / Deep Linking / Universal Link

I am building app using react native, Expo SDK - 41 & react-native-navigation - v5 that serves items to the users
How can I create links to be shared between users through social media apps like "WhatsApp" or "Facebook" as messages
For Example:
I found this item on "App Name". Take a look, https://myApp/itemId
When the user press the link it will open the app on specific screen to show that item (if the app installed on the receiver device), Otherwise will open the App store (Android or IOS) depend on the device to download and install the app.
And is there is any tutorial that explain the whole implementation for this feature ?
Here are the steps I used in our app:
For the steps below let's assume you own a domain https://example.com and you want to redirect all clicks on https://example.com/your-path/ to:
redirect to the app/play store if the app is not installed
opened in the browser if on a desktop device
open the app on iOS/Android if app is installed
Steps to achieve that:
You need a domain (e.g. https://example.com)
Registering Universal Links on iOS:
You have to prove to Apple that you own this domain by providing an Apple App Site Association (AASA) file from your web server.
Detailed explanation: https://docs.expo.dev/guides/linking/#universal-links-on-ios.
Note: No server-sided configuration needed for Android
After that you need to configure the domains (you specified in the AASA) in your expo app.json file (See: https://docs.expo.dev/guides/linking/#expo-configuration)
Note: The Expo docs don't show any example so I'll share one:
"expo": {
"name": "MyApp",
...,
"ios": {
...,
"associatedDomains": [
"applinks:example.com"
]
},
"android": {
...,
"intentFilters": [
{
"action": "VIEW",
"autoVerify": true,
"data": [
{
"scheme": "https",
"host": "*.example.net",
"pathPrefix": "/your-path"
}
],
"category": [
"BROWSABLE",
"DEFAULT"
]
}
]
}
}
}
Result of the steps above: if you click a link like https://example.com/your-path/ (or any subdomain of that e.g. https://example.com/your-path/your-second-path?hello=world ...),
you should be redirected to the app on iOS/Android if the apps are installed.
However if the app is not installed a browser widow opens https://example.com/your-path/
Redirecting to app stores:
You have to configure the subdomain .../your-path in a way to check which device is trying to open it when loading the site and redirect to the app store/play store - url if it is an iOS/Android device resp.
Example: in our case the domain leads to a React web app and on loading the page ( on componentDidMount ) I check User's device using navigator.userAgent
This will of course differ from framework to framework.
Here you can also choose what to show or where to redirect if the link is clicked on a desktop device.
Handle links in your iOS/Android app:
Now in Expo you need to install expo-linking via expo install expo-linkingand set a listener to handle URL links in the desired component:
import * as Linking from 'expo-linking';
componentDidMount() {
this.grabLinkOpeningApp()
this.setLinkListenerWhenOpeningApp()
}
grabLinkOpeningApp() {
//Handles link when the link is clicked and the app was already open
Linking.addEventListener('url',this.handleUrl)
}
setLinkListenerWhenOpeningApp() {
//Handles link when app is closed:
Linking.getInitialURL()
.then(url => this.handleUrl(url))
.catch(err => this.handleError(err))
}
handleUrl(url) {
//Handle your link here
}
Note: Best to use the code in a component that is opened before the actual screen components. In this case you can catch the link no matter if the user is registered or not and like that it can "survive" the registration process.
7. Create a link and share it from your app:
You can add arbitrarily many query params to the link, i.e. .../you-path/key1=value1/key2=value2....
So you can create links within your app using data however you need it for your specific use case.
If you want to share the link via the typical share dialog where the user can choose the app you he wants to share the link with (i.e. Whatsapp, Facebook, Instagram, etc.), you can either import {Share} from 'react-native' or import * as Sharing from 'expo-sharing'
(see resp. expo docs, this part is very straight forward following the docs)
In the end, sorry for the long text, but I hope it explains the steps involved well.

nuxt.js - "This page could not be found" on static page with base ./

I tried to generate a static web site with nuxt, but when i open the index.html file, it show an infinite load screen with this JS error :
fail to load element whose source is « file:///_nuxt/42185af33c638e7022a3.js ».
so, after i have search, i change router.base configuration by ./ and it throw this error :
This page could not be found
Back to the home page
but when i click on Back to the home page it showing my home page.
Anyone have an idea how to open index.html file from static build ?
i explain my project : i wish to run my app with Capacitor so i need static build work fine.
Thank by advance and my apologies for my bad english write.
I found two solution:
Solution 1 : Make your project to "universal" instead "single page app"
When you generate a static web app, it work fine when you run index.html. However, Capacitor display the page but don't recognize the router, so you can't switch page in your app.
Solution 2 : Set router in nuxt.config.js in spa
Add this configuration to nuxt.config.js :
router: {
base: './'
mode: 'hash'
}
it work when you open the index.html file in dist, but does not work with capacitor.
My request is therefore partially resolved.
Finally i have found my problem to run my app with Capacitor, Android dont like underscore.
In nuxt.config.js, just change _nuxt to nuxt
build: {
publicPath: '/nuxt/',
// ...
},

Flask/React app, downloaded binary (pkpass) file differs from the same file on the server

I'm building a React app that hits a Flask server for Stripe payment processing and Apple Wallet pass generation. My server script generates the file, and saves it to a directory, then returns the name of the pkpass file to the client, which attempts to download it and load it into Wallet.
Passes seem to be generating correctly. I can download them (sftp) and drop them on the Simulator and they open up just fine. I can un-zip them and all the necessary components seem to be there and seem valid.
However, with the web app, I cannot get them to download and install into Wallet. On my iPhone, I get a simple "Safari could not download the file" error.
If I use Safari on the desktop, the pkpass file gets downloaded to my Mac. When I drag & drop it on the simulator, I see the following error in Console:
BOM could not extract archive: Couldn't read PKZip signature
Failed to add pass: 'file:///Users/tpoulsen/Downloads/ch_1FPYBoAzcfAbJsLnXDsVRcrc.pkpass' Error Domain=PKPassKitErrorDomain Code=1 "The pass cannot be read because it isn’t valid." UserInfo={NSLocalizedDescription=The pass cannot be read because it isn’t valid., NSUnderlyingError=0x6000016dbbd0 {Error Domain=PKPassKitErrorDomain Code=1 "(null)"}}.
I used VBinDiff to compare the downloads (via sftp and Safari) and the files are quite different. So, the problem appears to be either with my Flask app or my web app.
Here's my Flask route
#application.route("/passes/<path:fname>")
def passes_proxy(fname):
"""static passes serve"""
return send_from_directory("passes", fname, mimetype="application/vnd.apple.pkpass")
And in my component:
paymentRequest.on('token', async (ev) => {
try {
const response = await fetch('https://my_server.com/charge', {
method: 'POST',
body: JSON.stringify({
token: ev.token.id,
amount: totalInCents,
description: purchasedItems.join(',\n')
}),
headers: {'content-type': 'application/json'},
});
if (!response.ok) {
throw new Error('Network response was not ok.');
}
ev.complete('success');
const pkpass = await response.json();
download(`https://my_server.com/passes/${pkpass.filename}`, pkpass.filename, "application/vnd.apple.pkpass");
} catch (error) {
throw new Error("There was a problem processing your payment");
}
});
Tech involved:
create-react-app / React
Flask with wallet-py3k for pass generation
download.js to download the generated file
Got it. I was being too fancy. Instead of using download.js to grab the file, I simply needed to:
window.location.href = `https://my_server.com/passes/${pkpass.filename}`;

React production error with service worker: invalid MIME type

I have a React app that works in development and in production, however in production I get the following error in the console:
The script has an unsupported MIME type ('text/html').
Failed to load resource: net::ERR_INSECURE_RESPONSE
registerServiceWorker.js:80 Error during service worker registration:
DOMException: Failed to register a ServiceWorker: The script has an unsupported MIME type ('text/html').
The error does not happen in development, only in the production environment. The app still works correctly in production, however I would still prefer to sort out the error.
After doing some digging, it seems that in production the service-worker.js file is requested from the original index.html file while has a MIME type of text/html, the service-worker.js file therefore does not have the correct MIME type which would need to be application/javascript.
Unfortunately even though I think I understand what the issue is, I haven't been able to fix it.
The production build was created using create-react-app and is served up by a Django backend. The index.html page containing the React app is served up as follows:
re_path('.*', TemplateView.as_view(template_name='index.html'))
Is there perhaps something on Nginx that needs changing? I would guess not since I have other production sites working correctly with respect to MIME types (however none of them are React apps requiring service workers).
It happens if you have registered some service workers. If you open Chrome developer tools you may get some errors.
Go to
Chrome Dev Tools -> Application -> Service Worker
In there if you are seeing some service worker is registered then you have to unregister it by clicking it and after that refresh the page and check.
From registrationService, I can see that:
export default function register() {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
if (publicUrl.origin !== window.location.origin) {
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
checkValidServiceWorker(swUrl);
} else {
registerValidSW(swUrl);
}
});
}
}
When its production environment, then it tries to register to registerValidSW(swUrl); where swUrl is <your domain>/service-worker.js(FYI PUBLIC_URL is set from homepage value of package.json). But I am guessing that is not a valid path, so its getting index.html from Django. That might be why you are getting this error.
Again, based on guess, I think you can do the following solution:
Serve service-worker.js in from static folder (for example url could be: /static/service-worker.js).
Update in registrationService.js: ${process.env.PUBLIC_URL}/service-worker.js to ${process.env.PUBLIC_URL}/static/service-worker.js.
See how it goes. There is another solution if you don't want to use registerService in production, just not to register that service(I am not sure if its recommended or not, but its up to you)
if (process.env.NODE_ENV != 'production') {
registerServiceWorker();
}

Serving image assets from S3 with ember-cli-deploy Lightning method

I'm trying to deploy an Ember CLI app using ember-cli-deploy and the 'lightning' deployment method (http://ember-cli-deploy.com/docs/v0.6.x/the-lightning-strategy/).
I've got a redis server to serve my index.html file. I've got my assets uploaded to S3. However my image assets don't seem to be loading properly.
In ember-cli-build.js I have:
var app = new EmberApp(defaults, {
fingerprint: {
prepend: '//path-to-my-S3-bucket/'
}
});
but for some reason images are still being served from the redis server IP. I am getting errors like "Failed to load http://redis-server-url/my-image.jpg". Javascript and CSS files are working fine from S3.
Have I missed something here? Is there another step to this configuration?
Many thanks
I would confirm that fingerprinting is enabled. It is only enabled for 'production' builds by default. You should see an md5 checksum appended to your asset file names. For example, my-image.jpg should be something like my-image-9c2cbd818d09a4a742406c6cb8219b3b.jpg
You can override the default behavior by passing the enabled option:
var app = new EmberApp(defaults, {
fingerprint: {
enabled: true,
prepend: '//path-to-my-S3-bucket/'
}
});