Update from a place of mild understanding:
I have read much about Service Workers and advanced the project I'm working on far past where it was when I wrote this.
The main issue I was exploring here (getting firebase-messaging-sw.js served from the root of the domain) was solved by the ServiceWorkerView below.
Fun fact: turns out that the reason to have firebase-messaging-sw.js served from the root of the domain is that the scope of a Service Worker's control is defined by the URL. So, while:
http://whatever.com/firebase-messaging-sw.js has control over everything,
http://whatever.com/static/firebase-messaging-sw.js only has control over everything under /static/ (in this case, only the static resources served by Django). Even though the JS file is static, it needs to control pages outside that path.
A good starting point for learning about Service Workers
Original Question:
How do I get the FCM service worker (firebase-messaging-sw.js) to work in Django?
I'm using django with fcm-django.
How do I serve firebase-messaging-sw.js at the root?
Django serves static assets by default under the path “/static/“. Changing the STATIC_URL to “/“ makes firebase-messaging-sw.js available but nothing else is..
I tried a suggestion to put the following in my urls.py:
url(r'^(?!/static/.*)(?P<path>.*\..*)$', RedirectView.as_view(url='/static/%(path)s'))
While clearly a glorious use of regex magic, it turns out that the service worker can't be behind a redirect. (I guess this also has to do with the fun fact)
I wrote a view pointed to by an explicit URL in the urlconf. that works.
class ServiceWorkerView(View):
def get(self, request, *args, **kwargs):
return render(request, 'fcmtest/firebase-messaging-sw.js')
urls.py:
path('firebase-messaging-sw.js', views.ServiceWorkerView.as_view(), name='service_worker')
Now the url http://localhost:8000/firebase-messaging-sw.js serves up the javascript; however, Firebase complains that the content-type is text/plain
So I changed the view to:
class ServiceWorkerView(View):
def get(self, request, *args, **kwargs):
return render(request, 'fcmtest/firebase-messaging-sw.js', content_type="application/x-javascript")
And the error I get is:
Uncaught (in promise) TypeError: Failed to register a ServiceWorker: A
bad HTTP response code (404) was received when fetching the script.
My firebase-messaging-sw.js (note none of the console.log statements print):
importScripts("https://www.gstatic.com/firebasejs/5.2.0/firebase-app.js");
importScripts("https://www.gstatic.com/firebasejs/5.2.0/firebase-messaging.js");
importScripts("https://www.gstatic.com/firebasejs/5.2.0/init.js");
// Initialize Firebase
var config = {
apiKey: "AIzaSyBOb5gh1Lry84116M1uvAS_xnKtcWUoNlA",
authDomain: "pyfcm-5f794.firebaseapp.com",
databaseURL: "https://pyfcm-5f794.firebaseio.com",
projectId: "pyfcm-5f794",
storageBucket: "pyfcm-5f794.appspot.com",
messagingSenderId: "813943398064"
};
console.log('in service worker - before initializeApp')
firebase.initializeApp(config);
console.log('in service worker - after initializeApp')
const messaging = firebase.messaging();
console.log('in service worker - after firebase.messaging')
// if the app is in the background (user not on page)
messaging.setBackgroundMessageHandler(function(payload) {
console.log('in service worker - in setBackgroundMessageHandler')
const title = "Hello World";
const options = {
body: payload.data.status
}
return self.registration.showNotification(title, options);
});
and finally, my device registration page (I'm not sure how to run the javascript and then submit. form.submit() didn't work but that might be because of other things not working..?
(fcm/fcmtest/templates/fcmtest/device_registration.html):
<head>
{% load static %}
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>FCM Device Registration</title>
<!-- update the version number as needed -->
<script src="https://www.gstatic.com/firebasejs/5.2.0/firebase.js"></script>
<script>
// Initialize Firebase
var config = {
apiKey: "AIzaSyBOb5gh1Lry84116M1uvAS_xnKtcWUoNlA",
authDomain: "pyfcm-5f794.firebaseapp.com",
databaseURL: "https://pyfcm-5f794.firebaseio.com",
projectId: "pyfcm-5f794",
storageBucket: "pyfcm-5f794.appspot.com",
messagingSenderId: "813943398064"
};
firebase.initializeApp(config);
</script>
<script defer src="https://www.gstatic.com/firebasejs/5.2.0/firebase-app.js"></script>
<!-- include only the Firebase features as you need -->
<script defer src="https://www.gstatic.com/firebasejs/5.2.0/firebase-auth.js"></script>
<!--<script defer src="https://www.gstatic.com/firebasejs/5.2.0/firebase-database.js"></script>-->
<script defer src="https://www.gstatic.com/firebasejs/5.2.0/firebase-messaging.js"></script>
<!--<script defer src="https://www.gstatic.com/firebasejs/5.2.0/firebase-storage.js"></script>-->
<!-- initialize the SDK after all desired features are loaded -->
<!--<script defer src="https://www.gstatic.com/firebasejs/5.2.0/init.js"></script>-->
</head>
<body>
<script>
function deviceRegistration() {
console.log('in deviceRegistration()')
firebase.initializeApp(config);
const messaging = firebase.messaging();
// public key generated in firebase console
messaging.usePublicVapidKey("BMEoHrnzLq5WNeyahbSxJNTyS-44bXug2wetxAWVMLMSUIQE0dexhP4pJhcSA-ZlQlneHURmYQcnq9ofym_sStY");
// console.log('{% static "firebase-messaging-sw.js" %}')
console.log("/firebase-messaging-sw.js")
// changed location passed to navigator.serviceWorker.register since I am no longer serving firebase-messaging.js from localhost/static/ (see that url hack)
// navigator.serviceWorker.register('{% static "firebase-messaging-sw.js" %}')
navigator.serviceWorker.register("/firebase-messaging-sw.js")
.then((registration) => {
console.log('before messaging.useServiceWorker')
messaging.useServiceWorker(registration);
console.log('after messaging.useServiceWorker')
// Request permission and get token.....
messaging.requestPermission()
.then(function(){
console.log("Have Permission");
})
.then(function(token) {
form = document.getElementById("registration_form");
registration_token = messaging.getToken();
form.registration_id = registration_token;
var isAndroid = navigator.userAgent.toLowerCase().indexOf("android") >= -1;
if (isAndroid) {
form.type = "android";
} else {
var isiOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
if (isiOS) {
form.type = "ios";
} else {
form.type = "web";
}
}
//form.submit();
})
.catch(function(err){
console.log("Permission denied");
})
});
// request permission to send notifications
messaging.requestPermission()
.then(function(){
console.log("Have Permission");
})
.then(function(token) {
form = document.getElementById("registration_form");
registration_token = messaging.getToken();
form.registration_id = registration_token;
var isAndroid = navigator.userAgent.toLowerCase().indexOf("android") >= -1;
if (isAndroid) {
form.type = "android";
} else {
var isiOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
if (isiOS) {
form.type = "ios";
} else {
form.type = "web";
}
}
//form.submit();
})
.catch(function(err){
console.log("Permission denied");
})
// if user is on the page then show message directly instead of a notification
messaging.onMessage(function(payload) {
console.log('payload: ', payload);
});
}
</script>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form id="registration_form" method="post">{% csrf_token %}
<input id="name" type="text" />
<input id="type" type="hidden" />
<input id="user" type="hidden" value="{{ user.id }}" />
<input id="registration_id" type="hidden" />
<input id="btn_register" type="button" value="Register" onclick="deviceRegistration();" />
</form>
</body>
I tried adding a manifest.json as per instructions on
https://firebase.google.com/docs/cloud-messaging/js/client
It is currently served from my static files and is linked in the registration and send pages
The errors in my console:
Uncaught (in promise) TypeError: Failed to register a ServiceWorker: A
bad HTTP response code (404) was received when fetching the script.
A bad HTTP response code (404) was received when fetching the script.
Failed to load resource: net::ERR_INVALID_RESPONSE
firebase-messaging-sw.js:3 Uncaught
(anonymous) # firebase-messaging-sw.js:3
index.esm.js:1945 Uncaught (in promise)
Object
browserErrorMessage:
"Failed to register a ServiceWorker: ServiceWorker script evaluation failed"
code:
"messaging/failed-serviceworker-registration"
message:
"Messaging: We are unable to register the default service worker. Failed to register a ServiceWorker: ServiceWorker script evaluation failed (messaging/failed-serviceworker-registration)."
stack:
"FirebaseError: Messaging: We are unable to register the default service worker. Failed to register a ServiceWorker: ServiceWorker script evaluation failed (messaging/failed-serviceworker-registration).↵ at https://www.gstatic.com/firebasejs/5.2.0/firebase-messaging.js:1:34104"
The issue of not being able to serve firebase-messaging-sw.js can be solved by creating a ServiceWorkerView to GET the file directly and hardcoding the URLconf.
fcm/fcmtest/views.py:
class ServiceWorkerView(View):
def get(self, request, *args, **kwargs):
return render(request, 'fcmtest/firebase-messaging-sw.js', content_type="application/x-javascript")
fcm/fcmtest/urls.py
app_name = 'fcmtest'
urlpatterns = [
...
path('firebase-messaging-sw.js', views.ServiceWorkerView.as_view(), name='service_worker')
]
The reason to serve firebase-messaging-sw.js from the root of the domain is that the scope of a Service Worker's control is defined by the URL path. Everything under the path the Service Worker is available at is under its control.
The 404 might be due to a javascript error in device_registration.html. config is referenced but has not been assigned to.
function deviceRegistration() {
firebase.initializeApp(config);
Also, it is not necessary (maybe even problematic) to do this with Firebase Cloud Messaging:
navigator.serviceWorker.register("/firebase-messaging-sw.js")
.then((registration) => {
messaging.useServiceWorker(registration);
})
Instead, use firebase.initializeApp(config);
It might be a good idea to serve manifest.json at the root.
The issue with form.submit() is that the code to alter the elements in the form from the promise is incomplete. After debugging that it will work great.
I like what Inversus has done because it will work in dev/test/prod. That being said, in production I have elected to let nginx serve the firebase-messaging-sw.js file . . .
# Firebase Cloud Messaging (FCM) requires this file to be served from
# your root directory
location =/firebase-messaging-sw.js {
root /home/ubuntu/yourpath/yourproject/FCM/;
}
Related
On a Flutter Web Application, I initialize Firebase in the index.html like below.
<body>
<!-- Check type of device and block all mobile phone devices! Tablets should work -->
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
<!-- Firebase App (the core Firebase SDK) is always required and must be listed first -->
<script defer src="https://www.gstatic.com/firebasejs/8.2.0/firebase-app.js"></script>
<!-- If you enabled Analytics in your project, add the Firebase SDK for Analytics -->
<script defer src="https://www.gstatic.com/firebasejs/8.2.0/firebase-analytics.js"></script>
<!-- Add Firebase products that you want to use -->
<script defer src="https://www.gstatic.com/firebasejs/8.2.0/firebase-auth.js"></script>
<script defer src="https://www.gstatic.com/firebasejs/8.2.0/firebase-firestore.js"></script>
<script defer src="https://www.gstatic.com/firebasejs/8.2.0/firebase-storage.js"></script>
<script defer src="https://www.gstatic.com/firebasejs/8.2.0/firebase-functions.js"></script>
<script src="init-firebase.js" defer></script>
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('flutter_service_worker.js?v=3005614081');
});
}
</script>
<script src="main.dart.js" type="application/javascript"></script>
</body>
Further, I have an init-firebase.js for the Firebase config as per below.
// Initialize Firebase
var firebaseConfig = {
apiKey: "...",
authDomain: "...",
databaseURL: "...",
projected: "...",
storageBucket: "...",
messagingSenderId: "...",
appId: "...",
measurementId: "..."
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
This works all fine and within Dart I can access Firebase Auth, etc.
I wonder however how I can access Firebase Auth when I open a new HTML file within the application like example.html?
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
#override
void initState() {
_showDocument();
super.initState();
}
_showDocument() async {
await ui.platformViewRegistry.registerViewFactory(
'ExamplePage',
(int viewId) => IFrameElement()
..src = 'https://example.com'
..style.border = 'none');
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: HtmlElementView(viewType: 'ExamplePage'),
));
}
}
In the example.html like below I would like to access Firebase data like Auth, Storage, etc for users who have authenticated in the Flutter Web App but I can't. Calling my init-firebase.js would init Firebase again.
<html>
<head>
<link rel="shortcut icon" type="image/png" href="favicon.png" />
<title>Example</title>
<script defer src="https://www.gstatic.com/firebasejs/8.2.0/firebase-app.js"></script>
<script defer src="https://www.gstatic.com/firebasejs/8.2.0/firebase-auth.js"></script>
<script defer src="https://www.gstatic.com/firebasejs/8.2.0/firebase-firestore.js"></script>
<script defer src="https://www.gstatic.com/firebasejs/8.2.0/firebase-storage.js"></script>
<script defer src="https://www.gstatic.com/firebasejs/8.2.0/firebase-functions.js"></script>
<!-- <script src="init-firebase.js" defer></script> -->
</head>
<body>
<div id="my-view" style="height: 95%; width: 100%;" oncontextmenu="return false;"></div>
<script>
// Access Firebase data like AUTH, Storage, Firestore here.....
</script>
</body>
</html>
The Flutter Web App is still on the same tab and index.html and I am only opening another HTML window within that. Any way to pass Firebase down to HTML?
There is no way to pass a FirebaseApp instance (or services taken from that such as the database or auth) between pages. Each web page that loads in a browser is its own instance, and will need to load the Firebase services it uses.
I am using glue to spin up the hapi server so I gave the json object with connection and registration details.
I have 10 routes and i need to use authentication strategy for all the 10 routes, So followed the below steps
1) I have registered the xyz custom auth plugin
2) Defined the strategy server.auth.strategy('xyz', 'xyz', { });
3) At every route level I am enabling auth strategy
auth: {
strategies: ['xyz'],
}
How can I give below line to glue configuration object itself.
server.auth.strategy('xyz', 'xyz', { });
Glue.compose(ServerConfig, { relativeTo: baseDir }, (err, server) => {
internals.server = server;
})
One more question here is, in this line server.auth.strategy('xyz', 'xyz', { from json file}); I am reading the JSON data from a config file. When I change the data in this JSON file I dont want to restart the server manually to load the modified data. Is there any plugin or custom code to achieve this?
I figured out a general workaround for when you want to do setup that Glue does not directly support (AFAIK) and you also don't want to keep adding to index.js.
Create a plugins folder where your manifest.js is located.
Create a file plugins/auth.js (in this case). Here you will have a register callback with access to the server object and you can make setup calls that go beyond what Glue does declaratively.
Add a plugin item to manifest.js pointing to your plugin file.
in manifest.js:
register: {
plugins: [
{
plugin: './plugins/auth',
},
]
}
in plugins/auth.js:
module.exports = {
name: 'auth',
async register (server) {
await server.register([
require('#hapi/cookie'),
]);
server.auth.strategy('session', 'cookie', {
cookie: {
name: 'sid-example',
password: '!wsYhFA*C2U6nz=Bu^%A#^F#SF3&kSR6',
isSecure: false
},
redirectTo: '/login',
validateFunc: async (request, session) => {
const account = await users.find(
(user) => (user.id === session.id)
);
if (!account) {
return { valid: false };
}
return { valid: true, credentials: account };
}
});
server.auth.default('session');
},
};
(auth setup code is from the Hapi docs at enter link description here)
This is the way I have found where I can call things like server.auth.strategy() sort-of from manifest.js.
Note: Auth is not a great example for this technique as there is a special folder for auth strategies in lib/auth/strategies.
I have no idea why this is happening. We finally launched our app, everything works on the staging server, and then I deploy to an identical setup - and now this is happening - and I can't figure out why.
You can see it happening for yourself: go to http://www.crunchyserial.com/
Here is the error in the console:
Uncaught DOMException: Blocked a frame with origin "http://www.crunchyserial.com" from accessing a cross-origin frame.
at http://www.crunchyserial.com/packages/oauth/end_of_popup_response.js:18:39
at http://www.crunchyserial.com/packages/oauth/end_of_popup_response.js:37:3
If you refresh the blank oauth window, it closes and then you get this error:
and on the server log you get this:
[00.00.00.00]{"line":"431","file":"oauth.js","message":"Error in OAuth Server: Failed to complete OAuth handshake with Google. failed [400] { \"error\" : \"invalid_grant\", \"error_description\" : \"Code was already redeemed.\" }","time":{"$date":1497382695634},"level":"warn"}
[00.00.00.00]Exception while invoking method 'login' Error: Failed to complete OAuth handshake with Google. failed [400] { "error" : "invalid_grant", "error_description" : "Code was already redeemed." }
[00.00.00.00] at getTokens (packages/google-oauth/google_server.js:107:7)
at Object.getServiceData [as handleOauthRequest] (packages/google-oauth/google_server.js:81:35)
at OAuth._requestHandlers.(anonymous function) (packages/oauth2.js:27:31)
at middleware (packages/oauth.js:203:5)
at packages/oauth.js:176:5
{"line":"431","file":"oauth.js","message":"Error in OAuth Server: Failed to complete OAuth handshake with Google. failed [400] { \"error\" : \"invalid_grant\", \"error_description\" : \"Code was already redeemed.\" }","time":{"$date":1497382701056},"level":"warn"}
Exception while invoking method 'login' Error: Failed to complete OAuth handshake with Google. failed [400] { "error" : "invalid_grant", "error_description" : "Code was already redeemed." }
at getTokens (packages/google-oauth/google_server.js:107:7)
at Object.getServiceData [as handleOauthRequest] (packages/google-oauth/google_server.js:81:35)
at OAuth._requestHandlers.(anonymous function) (packages/oauth2.js:27:31)
at middleware (packages/oauth.js:203:5)
[00.00.00.00] at packages/oauth.js:176:5
Here is the settings.json I use in my ./deploy directory for the production server:
{
"public": {
"analyticsSettings": {
"Google Analytics" : {"trackingId": "//redacted//"}
}
},
"private": {
"oAuth": {
"google": {
"clientId": "//redacted//",
"secret": "//redacted//",
"loginStyle": "popup"
},
"facebook": {
"appId": "//redacted//",
"secret": "//redacted//",
"loginStyle": "popup"
},
"twitter": {
"consumerKey": "//redacted//",
"secret": "//redacted//"
"loginStyle": "popup"
}
}
}
}
Oddly enough, there is no difference between "redirect" and "popup" loginStyles... I don't notice ANY behavior difference in the app.
This is my mup.js:
module.exports = {
servers: {
one: {
host: '//redacted//',
username: 'ubuntu',
pem: "//redacted//"
// password:
// or leave blank for authenticate from ssh-agent
}
},
meteor: {
name: 'CrunchySerial',
path: '../../CrunchySerial',
servers: {
one: {}
},
buildOptions: {
serverOnly: true,
},
env: {
ROOT_URL: 'http://www.crunchyserial.com',
MONGO_URL: 'mongodb://localhost/meteor'
},
dockerImage: 'abernix/meteord:base',
deployCheckWaitTime: 400
},
mongo: {
oplog: true,
port: //redacted//,
servers: {
one: {},
},
},
};
I've tried the ROOT_URL with and without a / I've updated to all the latest versions... And I still have no idea why this is happening.
Solution: oauth, ROOT_URL, redirect_uri and www.domain.com vs domain.com
Your oauth script will only work from one domain - and when you have a ROOT_URL, it uses this in the uri that your oauth sends in it's request. Also, you have to register which redirect_uri(s) are valid with some services (like google). So oauth will only work from ONE subdomain/domain. You have to pick www or no-www - not both. I chose to keep www and setup both a DNS redirect, and a programmatic redirect (just to be sure).
DNS Redirect from # to www.[your-domain].com:
We're using NameCheap, so the URL Redirect Record was easy to setup. Here is NameCheap's documentation.
Programmatic redirect in Meteor:
This is how I force a redirect in Meteor check for www and enforce it if it's not there.
File Location: [project]/imports/startup/client/force_www_redirect.js
import { Meteor } from 'meteor/meteor'
Meteor.startup(function () {
if (location.host.indexOf('www.crunchyserial.com') !== 0) {
location = 'http://www.crunchyserial.com'
}
})
and I just make sure this is the first code my client startup sees by importing it first.
File Location: [project]/imports/startup/client/index.js
// force www.crunchyserial.com
import './force_www_redirect'
// Configure Login Buttons UI
import './login_button_configuration'
// Configure atForm
import './useraccounts_configuration'
// Run the Routes
import './routes'
which is called from [project]/client/main.js:
import '../imports/startup/client'
I am trying to pre-cache some of my static app shell files using service worker. I can't use 'sw-appcache' as I am not using any build system. However, I tried using 'sw-toolbox' but I am not being able to use it for pre-caching.
Here is what I am doing in my service worker JS:
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open('gdvs-static').then(function(cache) {
var precache_urls = [
'/',
'/?webapp=true',
'/css/gv.min.css',
'/js/gv.min.js'
];
return cache.addAll(precache_urls);
});
);
});
I've also tried this:
importScripts('/path/to/sw-toolbox.js');
self.addEventListener('install', function(event) {
var precache_urls = [
'/',
'/?webapp=true',
'/css/gv.min.css',
'/js/gv.min.js'
];
toolbox.precache(precache_urls);
});
Here is the URL of my app: https://guidedverses-webapp-staging.herokuapp.com/
Here is the URL of my service worker file: https://guidedverses-webapp-staging.herokuapp.com/gdvs-sw.js
What am I missing?
Silly me. I forgot to add the fetch handler into my service worker. I thought it works like appchache and automatically returns the cached data when matches with the cache URL. I underestimated the power of Service Worker. Following code did the trick for me.
this.addEventListener('fetch', function(event) {
console.log(event.request.url);
event.respondWith(
caches.match(event.request).then(function(response) {
return response || fetch(event.request);
})
);
});
I have a mixed Ember/Rails app with a Rails route in the API namespace to take any single Event and convert it to an .ics file for import into a user's calendar (a la this question). Ember is running with the command ember server --proxy http://localhost:3000, which is connecting it to the Rails server process.
The below snippets illustrate my setup:
Rails routes.rb snippet:
namespace :api do
# snip
resources :events do
# snip
get 'export_event_ical', on: :member
end
end
Rails events_controller.rb snippet:
def export_event_ical
#event = Event.find(params[:id])
#calendar = Icalendar::Calendar.new
ical_event = Icalendar::Event.new
ical_event.dtstart = #event.start.strftime("%Y%m%dT%H%M%S")
ical_event.dtend = #event.start.strftime("%Y%m%dT%H%M%S")
ical_event.summary = #event.body.truncate(50)
ical_event.description = #event.body
# event.location = #event.location
#calendar.add_event ical_event
#calendar.publish
headers['Content-Type'] = "text/calendar; charset=UTF-8"
render :text => #calendar.to_ical
end
So, for example, in my Ember/Handlebars index template, if I have an event parameter that references a single Event, I can use Export to iCal to reach the API route that Rails is providing (i.e., skipping Ember on port 4200 and talking to Rails at 3000).
So far so good. But how do I make this into a dynamic Ember-controlled link that is routed through Ember to Rails?
I've tried a few things that don't work:
Adding a route to the Ember events resource (router.js):
this.resource('events', function() {
this.route('show', {path: ':event_id'});
this.route('export_to_ical', {
path: '/api/events/:event_id/export_event_ical'
});
});
Adding some goofball jQuery to the events.js route as a button action and using <button {{action 'exportToICal' event.id}}>Export to iCal</button> in my template:
import Ember from 'ember';
export default Ember.Route.extend({
actions: {
exportToICal: function(eventID) {
$.get('/api/events/' + eventID + '/export_event_ical',
function(){
alert('Got here.');
});
}
}
});
Reading some docs:
http://guides.emberjs.com/v1.10.0/components/sending-actions-from-components-to-your-application
EmberJS - How to dynamically generate link with linkTo?
How are you supposed to do this in Ember?
In my app I use the environment to declare server endpoints, sort of like in rails, at the bottom:
/* jshint node: true */
'use strict';
var extend = require('util')._extend;
module.exports = function(environment, appConfig) {
var ENV = extend(appConfig, {
EmberENV: {
FEATURES: {
// Here you can enable experimental features on an ember canary build
// e.g. 'with-controller': true
}
},
APP: {
// Here you can pass flags/options to your application instance
// when it is created
},
});
if (environment === 'development') {
ENV.serverHost = 'http://localhost:3000';
}
return ENV;
};
Then you can grab the value like this
var config = this.container.lookup('config:environment');
var url = config.serverHost + "/...";