I am using Varnish for caching a mobile and desktop version of my site and then displaying the right one depending on User-agent strings using https://github.com/varnishcache/varnish-devicedetect. But when testing the site with https://www.google.com/webmasters/tools/mobile-friendly/ I get the desktop site. Would it make sense to fork the varnish-devicedetect and add the user agent google uses when visiting the site? Or is there some other solution that would work better?
I know that it would not be a problem if the site would be responsive but that is right now not an option.
Use this one:
(subset from https://github.com/varnishcache/varnish-devicedetect)
sub detectbot {
unset req.http.X-Bot-Detected;
# mobile bots
if (req.http.User-Agent ~ "\(compatible; Googlebot-Mobile/2.1; \+http://www.google.com/bot.html\)"
|| (req.http.User-Agent ~ "iPhone" && req.http.User-Agent ~ "\(compatible; Googlebot/2.1; \+http://www.google.com/bot.html")) {
set req.http.X-Bot-Detected = "mobile-bot";
}
# other bots
elsif (req.http.User-Agent ~ "(?i)(ads|google|bing|msn|yandex|baidu|ro|career|seznam|)bot"
|| req.http.User-Agent ~ "(?i)(baidu|jike|symantec)spider"
|| req.http.User-Agent ~ "(?i)scanner"
|| req.http.User-Agent ~ "(?i)(web)crawler") {
set req.http.X-Bot-Detected = "bot";
}
}
save it as detect-bot.vcl (in the same directory as your varnish default.vcl)
then at the top of your default.vcl
include "detect-bot.vcl";
Then add the following parts in your .vcl
backend mobile {
.host = "10.0.0.1";
.port = "80";
}
sub vcl_recv {
# add a header "X-Bot-Detected" when this request was done by a bot
call detectbot;
}
sub vcl_recv {
# call some detection engine
if (req.http.X-UA-Device ~ "^mobile-bot" ) {
set req.backend = mobile;
}
}
sub vcl_hash {
if (req.http.X-UA-Device) {
hash_data(req.http.X-UA-Device);
}
}
This example sends the requests to another backend. Depending on how stuff works on your setup, you need to adapt the last part. see https://www.varnish-cache.org/docs/4.1/users-guide/devicedetection.html for more examples
Related
we use Next.js with server-side rendering (SSR) and we'd like to wrap our app with Capacitor.js so that we can ship it to both Android and iOS devices. Sadly, this only seems to be possible when using static site generation (SSG) and I cannot find anything on how to even attempt to make SSR work.
A couple of threads seem to hint that this is possible: How to package a hosted web app with Ionic Capacitor (not sure what type of rendering is used here) and https://github.com/MicrosoftDocs/cordova-docs/blob/master/articles/getting-started/create-a-hosted-app.md (this was for Cordova). I have never used Nuxt.js before but some hints that it could be possible with that framework as well here: Using Capacitor 3 with Nuxtjs SSR
Essentially, is it possible to use Capacitor's Webview to display a hosted app instead of having to first build it statically? Could this be something that Capacitor 3 now allows?
Should this be impossible, what would be your recommendation for having a mobile app knowing that we have built our web (and mobile responsive) product in Next.js, with heavy usage of getServerSideProps (i.e. SSR). Any help would be greatly appreciated.
I am currently investigating the same.
Looking to wrap an existing responsive web application based on nextjs using SSR.
in this blog I read in the comments that you can essentially point capacitor to any existing URL (so also your already running website).
I have not tested it though yet. My biggest questions right now would be:
Could we still use native functions (such as push notifications, which is one of the main reasons of bundling it as an app) then? problably would need to integrate the required libs in my existing web app and check if we are running in capacitor environment somehow.
How does Apple handle such cases upon submissions? I have read that they do reject apps that mainly just wrap existing web sites without offering any app-like advantages.
I managed to do it in react and accomplish it in next in react I had to find injected native-bridge.js which is in npm package #capacitor/android/capacitor/src/main/java/com/getcapacitor/Bridge.java
in the function below and after coping output of the below function I had to disable bridgeJS or if you want the whole function
private JSInjector getJSInjector() {
try {
String globalJS = JSExport.getGlobalJS(context, config.isLoggingEnabled(), isDevMode());
String bridgeJS = "";
// String bridgeJS = JSExport.getBridgeJS(context);
String pluginJS = JSExport.getPluginJS(plugins.values());
String cordovaJS = JSExport.getCordovaJS(context);
String cordovaPluginsJS = JSExport.getCordovaPluginJS(context);
String cordovaPluginsFileJS = JSExport.getCordovaPluginsFileJS(context);
String localUrlJS = "window.WEBVIEW_SERVER_URL = '" + localUrl + "';";
return new JSInjector(globalJS, bridgeJS, pluginJS, cordovaJS, cordovaPluginsJS, cordovaPluginsFileJS, localUrlJS);
} catch (Exception ex) {
Logger.error("Unable to export Capacitor JS. App will not function!", ex);
}
return null;
}
in my js files then I import it which is hard but works
const script = document.createElement('script')
script.onload = rendertodom
script.onerror = rendertodom
script.src = '/native-bridge.js'
document.body.append(script)
function rendertodom () {
import('./App').then(({default:App})=> {
ReactDOM.render(
<App/>,
document.getElementById('root')
)
})
}
the struggling part in next is rendering dynamically where a plugin is needed like the app above or importing native bridge if on native before importing rest of app
I use a function for knowing its android from capacitor which I can't do before native so function is in index
if(isNativ(window)) load capcitor // sudo code from real code above
const isNative = (win) => {
let _a, _b;
if (win === null || win === void 0 ? void 0 : win.androidBridge) {
return true;
}
else return !!((_b = (_a = win === null || win === void 0 ? void 0 : win.webkit) === null || _a === void 0 ? void 0 : _a.messageHandlers) === null || _b === void 0 ? void 0 : _b.bridge);
};
I want to implement File Upload function like Dropbox. I use Appcelerator. This function can upload file from Acrobat, iBook, Work, Excel, Drive etc. And make in on iOS.
I have researched on Appcelerator but could not find any solution for this.
I dont know how to access to local storage on iOS or working with another app by Appcelerator.
Can you give me any suggestion about this problem
Thank you very much
You should register CFBundleDocumentTypes in your tiapp.xml ios element, which works the same as modifying the Info.plist in Xcode would for an Obj-C or Swift app. Once you have that completed, you can listen for the resume event in your app, and look at Ti.App.getArguments() to see if your app was launched by choosing "Open In" from another app. You can also look at the folder Inbox inside of Ti.Filesystem.applicationDataDirectory to see if there are any new files in there -- that's where iOS will place them when sharing them to your app.
Your code for handling the document could look like this (in your resume handler):
var cmd = Ti.App.getArguments(),
inboxFiles = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory, 'Inbox').getDirectoryListing() || [];
if (inboxFiles.length > 0) {
inboxFiles = inboxFiles.sort(byLastCreated).map(toTiFile);
if (!cmd.url) {
cmd.url = inboxFiles[0].getNativePath();
}
if (inboxFiles.length > 1) {
for (var i = inboxFiles.length - 1; i >= 1; i--) {
inboxFiles[i].deleteFile();
}
}
}
if (cmd && cmd.url && cmd.url.indexOf('file://') === 0) {
// TODO: Do something interesting with cmd.url.
}
I'm working on an AngularJS project with the Play Framework 2.2. I'm supposed to develop a mobile version for the web application (not responsive, its part of a given uni project). For the desktop version I'm loading the index page with:
def index(any: String) = Assets.at(path = "/public", file = "app/html/index.html")
which works fine. Detection of the mobile browser works as well by examining the user agent in a Scala Action.
I changed the above code as follows to get the request header:
def index(any: String) = Action { implicit request: RequestHeader =>
if(isMobile(request)) {
// result for mobile version
}
else //result for desktop version
}
However, I don't know how to serve the different asset files as result type.
Any help is appreciated.
If I understand your question correctly, you wish to serve different files from Assets.at() based on your isMobile test, but can't work out how to get the types to line up?
Assets.at() returns an Action[AnyContent] which is at its simplest a function from Request[AnyContent] to Future[Result].
So knowing this, we just need a couple of tweaks to your index function and everything fits:
def index(any: String) = Action.async { request: Request[AnyContent] =>
if(isMobile(request)) {
Assets.at(path = "/public", file = "mobile.html").apply(request)
} else {
Assets.at(path = "/public", file = "desktop.html")(request)
}
}
Explanations:
The inner call returns a Future[Result] so we've become an Action.async
implicit is not needed here so I dropped it
An Action needs to be given a Request not a RequestHeader so I changed that
I'm showing both .apply(request) and just (request) - they are exactly the same
Depending on the request uri I want to forward the requests with varnish to different servers using data from a mysql database. The database contains 2 fields: name and base_url. If the request goes to /forwards/%name% I want to forward the request to the server/backend defined by base_url. Me first try was to set an existing backend with VRT_SetHdr, but this does not work:
VRT_SetHdr(sp, HDR_REQ, "\010backend:", my_backend, vrt_magic_string_end);
Maybe it's possible to set a header like X-backend and then do the selection with ordinary VCL?
I also want to define the backends on runtime using the same data from mysql. Is this possible anyway?
As you say, you can switch backends in VCL if a header is set, provided that every backends are pre-declared in VCL:
vcl_recv {
# ...
if ( req.http.X-backend ) {
set req.backend = req.http.X-backend;
}
# ...
}
EDIT:
As #Bhaskar has pointed in his comment, an additional if is needed for each backend due to varnish structure assignment restrictions. Something like:
vcl_recv {
# ...
if ( req.http.X-backend ) {
if ( req.http.X-backend == "predefined" ) {
set req.backend = "predefined";
}
}
# ...
}
I have an AngularJS app which I'd like to get indexed properly on Google.
I wrote a client that scrapes the sites for links and then downloads the pages with Phantomjs making snapshots. This all works fine. What I'm having a problem with is serving those snapshots to the Google bot.
For some reason, the Google bot appends ?_escaped_fragment= to my URLs. As an example, http://me.com/about gets changed to http://me.com/about?_escaped_fragment=. I've verified this in the access logs.
I'm trying to catch this request and serve the Google bot the snapshot with this config:
location / {
if ($args ~ "_escaped_fragment_=") {
rewrite ^ /snapshots/$1;
}
}
However, requesting this URL: http://me.com/about?_escaped_fragment= always results in a 404. Same with the other pages.
The snapshots are stored in /snapshots, relative to the root of the website. They're named after their pages, following directory structure, so http://me.com/business/register has a snapshot in /snapshots/business/register.html.
What can I do to get these snapshots to work?
Thanks.
Ok first let me explain why google uses ?_escaped_fragment_, This is used for websites that rely on ajax, and mark their page with hashes, like for example if you have http://example.com/gallery/#!image1 and each time the user changes to the next image you update the hash to image2, image3, but if the user goes directly to http://example.com/gallery/#!image50 your javascript uses that hash to load the 50th image directly instead of image1 ( servers can't see the hash part, only javascript can ).
So google uses this _excaped_fragment_ to tell the server which page it's trying to cache.
For more explanation use this link
As for why you get a 404 error, I think because you used a $1 without using a capturing block, The right rule would be something like this
location / {
if ($args ~ "_escaped_fragment_=(.*)") {
rewrite ^ /snapshots/$1;
}
}
But I don't think this will fix your problem, because according to your example, you didn't use hashes, you used the uri of the page, so i would rewrite the rule to something like this
location / {
# try snapshot, if not found try direct file.
try_files snapshots$request_uri.html $uri;
}
Here is what I have in nginx and it is working fine, you might need to add a special one for index.html (i.e. when accessing the root of your website)
if ($args ~ "_escaped_fragment_=/(.+)/?") {
set $path $1;
rewrite ^ /snapshots/$path.html;
break;
}
location /snapshots/ {
internal;
alias /var/www/snapshots/;
}
So http://me.com/?_escaped_fragment_=/about will access /var/www/snaphots/about.html
Don't forget this meta tag as well in your page if you use html pushstate instead of hashbangs:
meta(name="fragment", content="!")