I recently tried to deploy a SailsJS app to an AWS environment, and thus I set up the relevant staging and production env files.
One persistent issue I had however was when I ran the app with:
sails lift --staging
I noticed I either got recurring redirects or it was not reachable at all.
After some further digging, I happened to come across this code located in the api/hooks/custom/index.js file:
// Next, if we're running in our actual "production" or "staging" Sails
// environment, check if this is a GET request via some other subdomain,
// for example something like `webhooks.` or `click.`. If so, we'll
// automatically go ahead and redirect to the corresponding path under
// our base URL, which is environment-specific.
// > Note that we DO NOT redirect virtual socket requests and we DO NOT
// > redirect non-GET requests (because it can confuse some 3rd party
// > platforms that send webhook requests.)
var configuredBaseSubdomain;
try {
configuredBaseSubdomain = url.parse(sails.config.custom.baseUrl).host.match(/^([^\.]+)\./)[1];
} catch (unusedErr) { /*…*/}
if ((sails.config.environment === 'staging' || sails.config.environment === 'production') && !req.isSocket && req.method === 'GET' && req.subdomains[0] !== configuredBaseSubdomain) {
sails.log.info('Redirecting GET request from `'+req.subdomains[0]+'.` subdomain...');
return res.redirect(sails.config.custom.baseUrl+req.url);
}//•
I tried to ask around on the IRC channels and Gitter.im links, but my queries 'got lost in the noise' per-say, so thought I'd just ask here and leave it up for discussion. Is there not a better way to handle this?
Deploying on an AWS environment with a URL http://ec2-xxx-xxx-xxx-xxx.locale-x.compute.amazonaws.com or just accessing via an IP address are both caught by the regex used:
/^([^\.]+)\./
Which makes deploying quite hard unless the app is in the root route (haha) of a site. Is there not another way to handle this?
For now, I've had to comment it out just so my staged app is usable/testable. I also realise that this might have been rendered via a parameter passed when initially creating my project via the sails-generate project, but I haven't tracked it down yet
i was running into the same issue. Seems we both used the generator at a time that resulted in the code including the regex above. It is now changed with this commit and as soon as you change the code in your repo to the below it actually works again!
https://github.com/balderdashy/sails-generate/commit/76e2096d8173d474b6152a67ff4cfa08c38e6460
Related
I'm having an issue getting Login Kit to work. Similar to the question asked here I have the correct redirect domain listed in tiktok settings and the redirect_uri is basically just "domain/tiktok" but no matter what I do I get the same error message:
Below is my backend code - it's basically exactly the same as what is listed in the tiktok docs. Any help on this would be much appreciated!
const CLIENT_KEY = 'my_key'
const DOMAIN = 'dev.mydomain.com'
const csrfState = Math.random().toString(36).substring(2);
res.cookie('csrfState', csrfState, { maxAge: 60000 });
const redirect = encodeURIComponent(`https://${DOMAIN}/tiktok`)
let url = 'https://www.tiktok.com/auth/authorize/';
url += '?client_key=' + CLIENT_KEY;
url += '&scope=user.info.basic,video.list';
url += '&response_type=code';
url += '&redirect_uri=' + redirect;
url += '&state=' + csrfState;
res.redirect(url);
UPDATE 8/13/2022
I submitted the app for review and was approved so the status is now "Live in production" instead of "staging". The issue is still there - still showing error message no matter what domain / callback URL I use
UPDATE 8/16/2022
OK so I've made some progress on this.
First off - I was able to get the authentication/login screen to finally show up. I realized to do this you need to:
Make sure that the status of your app is "Live in production" and not "Staging". Even though when you create a new app you may see client_key and client_secret show up don't let that fool you - Login Kit WILL NOT WORK unless your app is submitted and approved
The redirect_uri you include in your server flow must match EXACTLY to whatever value you entered in "Registered domains" in the Settings page. So if you entered "dev.mydomain.com" in Settings then redirect_uri can only be "dev.mydomain.com" not "dev.mydomain.com/tiktok".
I think I might know what the issue is. My guess is that before - on the Settings page you had to enter the FULL redirect URL (not just the domain) and whatever redirect uri was included in the authorization query was checked against this value which was saved in TikTok's database (whatever was entered in the Settings page when path/protocol were allowed). At some point recently, the front-end business logic was changed such that you could only enter a domain (e.g., mydomain.com) on the Settings page without any protocols - however TikTok's backend logic was never updated so during the Login flow they are still checking against an EXACT match for whatever was saved in their DB as the redirect uri - this would explain why an app that was previously using the API with a redirect uri that DOES include protocols (e.g., for Later.com their redirect uri is https://app.later.com/users/auth/tiktok/callback) continues to work and why for any app attempting to save redirect WITH protocols are getting the error message screen. My gut feeling is telling me that the error is not on my part and this is actually a bug on TikTok's API - my guess is it can be addressed either by changing the front-end on the Settings page to allow for path/protocols (I think this is the ideal approach) or to change their backend so that any redirect uri is checked such that it must include 1 of the listed redirect domains.
I've been emailing with the TikTok team - their email is tiktokplatform#tiktok.com - and proposed the two solutions I mentioned above. I suggest if you're having the same issue you email them as well and maybe even link this StackOverflow question so that maybe it will get higher priority if enough people message them about it.
If you're looking for a shot-term hack I'd recommend creating a dedicated app on AWS or Heroku with a clean domain (e.g., https://mydomain-tiktok.herokuapp.com) and then redirect to either your dev or production environment by appending a prefix to the "state" query (e.g., "dev_[STATE_ID]"). I'll just reiterate I consider this a very "hacky" approach handling callbacks and would definitely not want to use something like this in production.
In my case, the integration worked after doing following steps:
In TikTok developers page:
Like #eugene-blinn said: make sure your app is in Live in production status (I couldn't find anything in the documentation about why Staging apps don't work);
Add the Login Kit product to your app and set the Redirect domain field with your host domain, for example: mywebsite.com.
In your code:
From my tests, I could add whanever url path I wanted, the only constraint was that the domain should match with step 2. So, yes, you can add https://mywebsite.com/whatever/path/you/want in redirect_url parameter.
That's it. It should work with these 3 steps.
Additionally, I got other issue related to use specific features in the scope property (like upload or read videos, etc), so here the solution as well:
Only add Video Kit product to the TikTok app and set video.upload or video.list in the scope authorize request won't work unless you also add the TikTok API product in your TikTok app as well. Btw, it neeeds to be approved too.
TikTok fixed the bug that resulted in URL mismatch with redirect domain from working. However, they fixed it only for paths (e.g., /auth/tiktok) but PORT additions still result in an error - so www.domain.com:8080/auth/tiktok won't work but www.domain.com/auth/tiktok WILL work
UPDATE 10/3/2022
Got the following response directly from TikTok engineering team:
At this point, we only support production integrations with TikTok for Developers and require that you have a URL without port number. However, we understand from your communication that this makes it harder for you to build, test, and iterate your integration with us. Unfortunately, at this time, we do not have a timeline for when this additional support for development servers will be added. We request that you only redirect to URLs without port numbers. Thank you for the feedback.
The frontend of the developer's dashboard still rejects protocol and path in validation. However, the backend skips the path validation.
To be able to update the "Redirect domain" simply:
Open dev tools in chrome and go to the "Network" tab.
Clic on "Save changes" button on the dashboard.
Right clic on the "publish" request that appeared and copy as cURL.
Modify the "redirect_domains" field in the request before pasting it in the terminal.
I believe the app still needs to be approved and in production to get it to work. I'm still waiting for approval and it has been a couple of weeks.
UPDATE 9/17/2022
Just like #mauricio-ribeiro, my app worked after it was approved to production. Setting up the redirect domain without path and scheme works just fine.
I had the same problem, my solution:
1.- In my TikTok App dashboard, the “redirect_uri” is: mydomain.com, without http/https and without path (/my-redirect-url). Also you can add subdomains using this rule
2.- In my code, I have to add http or https to the redirect_uri, and feel free to use path (/my-redirect-uri)
I hope this help you
So I have made a React app that uses Axios to fetch api's. During development, I would have an api call to 127.0.0.1. However, my ReactApp resided on localhost:3000. Therefore, it development, I can't just use:
axios.get('/api/'),
In dev I would need to use:
axios.get('127.0.0.1/api/'),
Anybody have any good ideas on how to resolve this conflict so I can see some data in dev? Kinda tough to design an UI without any data to populate it. Kinda like buying a shirt without trying it on first (which, I never try anything on, so this is a horrible analogy.)
Use it as in the first example. Because it is relative, it will resolve fine for different hosts:
axios.get('/api')
Will automatically resolve to:
// if called by https://example.com/index.js for example
"https://example.com/api"
// if called by localhost/index.js
"https(s)://localhost/api"
In your second example, if you prepend the host and port, you will get duplication!
For example, I just tried your first example on my localhost:3000 and the result is
GET http://localhost:3000/api 404 (Not Found)
Which makes sense because I don't have a /api. But did you notice it appended /api correctly after my host and port?
Now your second example:
GET http://localhost:3000/127.0.0.1/api 404 (Not Found)
It duplicates the host and port. In your case it would be 127.0.0.1:3000/127.0.0.1/api
Just use the first example and it will resolve fine for different hosts (and ports) because it's relative! Did you try it out?
I have a site that exists on a dev, staging and production server. On the dev and staging server the functionality is 100% fine, however on the production server the strangest thing happens - "undefined" gets added to the URL path.
Here is the short example of what is happening:
In the index.html I have an anchor tag to logout of a session with passport: Logout.
It goes to this route on my node server:
// passport oauth logout
routes.get('/auth/logout', (req, res) => {
req.session.destroy((e) => {
req.logout();
res.redirect(config.redirects.env.prod);
});
});
On dev and staging this destroys the session and redirects you to /. On production, when you click the button it takes you to this URL randomly https://somesite.com/auth/undefined.
Any ideas on how to debug this? It is making no sense to me and there's nothing I'm finding serverside or in the markup that would cause this, especially since it is functional on dev and staging. All servers are Ubuntu servers set up exactly the same way.
I was able to resolve this. Oddly enough, 400 lines down in a completely unrelated route used for file uploads, I had a line of code that referenced config.redirects.env.production instead of config.redirects.env.prod. I wasn't even looking at that route because it wasn't even part of functionality i was testing at the moment and I saw no errors spit out (again, since the route wasnt being referenced/used yet).
Fixing that typo resolved this bizarre issue of "undefined" being inserted into the URL. Still not sure how it managed to bubble up like that.
I use service worker with sw-toolbox library. My PWA caches everything except API queries (images, css, js, html). But what if some files will be changed someday. Or what if service-worker.js will be changed.
How application should know about changes in files?
My service-worker.js:
'use strict';
importScripts('./build/sw-toolbox.js');
self.toolbox.options.cache = {
name: 'ionic-cache'
};
// pre-cache our key assets
self.toolbox.precache(
[
'./build/main.js',
'./build/main.css',
'./build/polyfills.js',
'index.html',
'manifest.json'
]
);
// dynamically cache any other local assets
self.toolbox.router.any('/*', self.toolbox.cacheFirst);
// for any other requests go to the network, cache,
// and then only use that cached resource if your user goes offline
self.toolbox.router.default = self.toolbox.networkFirst;
I don't know what is the usual method to update cache in PWA. Maybe PWA should send AJAX request in background and check UI version?
AFAIK the sw_toolbox does not have a strategy for cache with network update. This is really what you want I think.
You want to modify the cache-network race strategy - > https://jakearchibald.com/2014/offline-cookbook/#cache-network-race
Instead of just letting the loser fade away, once the network responds you will want to update the client. This is a little more advanced that I have time or time to explain here.
I would post a message to the client to let it know there is an update. You may want to alert the user to the update or just force the update.
I don't consider this to be an edge case, but a very common, but advanced scenario. I hope to publish a more detailed solution soon.
There is nice solution written here where he states (in a nutshell) to either not use cache-first strategy or update a UX pattern of displaying a "Reload for the latest updates."
I dealt with services workers without using any library and the solution I ended up coming up with involved a bit of server side code and some client side. The strategy in a nutshell
Firstly the variables you will need and where:
On the server side have a "service worker version" variable (Put this in a database or config file if you are using something like php that will update immediately on the server side without requiring a redeploy. Let's call it serverSWVersion
On one of the javascript files you cache (I have a javascript file dedicated to this) have a global variable that will also be the "service worker version". Let's call it clientSWVersion
Now how to use the two:
Whenever a person lands on the page make an ajax call to your server to get the serverSWVersion value. Compare this with the clientSWVersion value.
If the values are different that means your web app version is not
the latest.
If this is the case then unregister the service worker and refresh the page so that the service worker will be re registered and the new files will be cached.
What to actually do when new file is available
Update the serviceSWVersion and clientSWVersion variables and upload to server where applicable.
When a person visits again then the service worker should be re registered and all the cached files will be retrieved.
I have provided a php server side based code that I used while I was implementing this strategy. It should show you the principles. Just drop the "Exercise" folder in a htdocs of a php server and it should work without you having to do anything else. I hope you find it useful... And remember you could just use a database instead of a config file to store the server side service worker variable if you are using some other server instead of php:
Zip file with code:
ServiceWorkerExercise.zip
When a service worker is altered, the browser will install it, but the new version will not be activated until the browser tab or PWA app window is closed and re-opened.
So, if you change the cache name, the new cache will not serve any files until the browser re-opens, nor will the old cache be deleted until that time.
You can detect service worker changes in your page javascript using registration.onupdatefound and ask the user to close and re-open the window - something like this:
// register the service worker
navigator.serviceWorker.register('sw.js').then(function(registration)
{
registration.onupdatefound = function()
{
console.log("ServiceWorker update found.");
alert("A new version is available - please close this browser tab or app window and re-open to update ... ");
}
}, function(err)
{
console.log('ServiceWorker registration failed: ', err);
});
change self.toolbox.router.any('/', self.toolbox.cacheFirst); to self.toolbox.router.any('/', self.toolbox.fastest);
I currently have a set-up based on the meanjs stack boilerplate where I can have users logged in this state of being 'logged-in' stays as I navigate the URLs of the site. This is due to holding the user object in a Service which becomes globally available.
However this only works if I navigate from my base root, i.e. from '/' and by navigation only within my app.
If I manually enter a URL such as '/page1' it loses the global user object, however if I go to my root homepage and navigate to '/page1' via the site. Then it's fine, it sees the global user object in the Service object.
So I guess this happens due to the full page refresh which loses the global value where is navigating via the site does not do a refresh so you keep all your variables.
Some things to note:
I have enabled HTML5Mode, using prefix of '!'.
I use UI-Router
I use a tag with '/'
I have a re-write rule on express that after loading all my routes, I have one last route that takes all '/*' to and sends back the root index.html file, as that is where the angularjs stuff is.
I'm just wondering what people generally do here? Do they revert the standard cookies and local storage solutions? I'm fairly new to angular so I am guessing there are libraries out there for this.
I just would like to know what the recommended way to deal with this or what the majority do, just so I am aligned in the right way and angular way I suppose.
Update:
If I manually navigate to another URL on my site via the address bar, I lose my user state, however if I manually go back to my root via the address bar, my user state is seen again, so it is not simply about loosing state on window refresh. So it seems it is related to code running on root URL.
I have an express re-write that manually entered URLs (due to HTML5 Location Mode) should return the index.html first as it contains the AngularJs files and then the UI-Route takes over and routes it properly.
So I would have expected that any code on the root would have executed anyway, so it should be similar to navigating via the site or typing in the address bar. I must be missing something about Angular that has this effect.
Update 2
Right so more investigation lead me to this:
<script type="text/javascript">
var user = {{ user | json | safe }};
</script>
Which is a server side code for index.html, I guess this is not run when refreshing the page to a new page via a manual URL.
Using the hash bang mode, it works, which is because with hash bang mode, even I type a URL in the browser, it does not cause a refresh, where as using HTML5 Mode, it does refresh. So right now the solution I can think of is using sessionStorage.
Unless there better alternatives?
Update 3:
It seems the best way to handle this when using HTML5Mode is that you just have to have a re-write on the express server and few other things.
I think you have it right, but you may want to look at all the routes that your app may need and just consider some basic structure (api, user, session, partials etc). It just seems like one of those issues where it's as complicated as you want to let it become.
As far as the best practice you can follow the angular-fullstack-generator or the meanio project.
What you are doing looks closest to the mean.io mostly because they also use the ui-router, although they seem to have kept the hashbang and it looks like of more of an SEO friendly with some independant SPA page(s) capability.
You can probably install it and find the code before I explained it here so -
npm install -g meanio
mean init name
cd [name] && npm install
The angular-fullstack looks like this which is a good example of a more typical routing:
// Server API Routes
app.route('/api/awesomeThings')
.get(api.awesomeThings);
app.route('/api/users')
.post(users.create)
.put(users.changePassword);
app.route('/api/users/me')
.get(users.me);
app.route('/api/users/:id')
.get(users.show);
app.route('/api/session')
.post(session.login)
.delete(session.logout);
// All undefined api routes should return a 404
app.route('/api/*')
.get(function(req, res) {
res.send(404);
});
// All other routes to use Angular routing in app/scripts/app.js
app.route('/partials/*')
.get(index.partials);
app.route('/*')
.get( middleware.setUserCookie, index.index);
The partials are then found with some regex for simplicity and delivered without rendering like:
var path = require('path');
exports.partials = function(req, res) {
var stripped = req.url.split('.')[0];
var requestedView = path.join('./', stripped);
res.render(requestedView, function(err, html) {
if(err) {
console.log("Error rendering partial '" + requestedView + "'\n", err);
res.status(404);
res.send(404);
} else {
res.send(html);
}
});
};
And the index is rendered:
exports.index = function(req, res) {
res.render('index');
};
In the end I did have quite a bit of trouble but managed to get it to work by doing few things that can be broken down in to steps, which apply to those who are using HTML5Mode.
1) After enabling HTML5Mode in Angular, set a re-write on your server so that it sends back your index.html that contains the Angular src js files. Note, this re-write should be at the end after your static files and normal server routes (e.g. after your REST API routes).
2) Make sure that angular routes are not the same as your server routes. So if you have a front-end state /user/account, then do not have a server route /user/account otherwise it will not get called, change your server-side route to something like /api/v1/server/route.
3) For all anchor tags in your front-end that are meant to trigger a direct call to the server without having to go through Angular state/route, make sure you add a 'target=_self'.