how to register a service worker from different su

2019-01-15 12:43发布

问题:

I have two subdomains: https://abc.xxxx.com and https://xyz.xxxx.com. So my questions:

1). is it possible to register a service worker for https://xyz.xxxx.com from https://abc.xxxx.com ? if yes then how?

2). if http://abc.xxxx.com (http insecure) then anyway to register a service worker for https://xyz.xxxx.com from http://abc.xxxx.com like in iframe or something....

This is a real situation, I am facing for my multiple subdomain. Any help appreciated. Thanks in advance.

回答1:

Here are some general answers that I think should address the various points you raise in your question:

  • Each registered service worker has an associated scope, which dictates the set of web pages that the service worker can control. The scope of a service worker is a URL, and that URL must have the same origin as the page that registers the service worker, and must be either a URL that corresponds to the same path level as the page or a path that's one or more levels down. The default scope corresponds to the same path level as location of the service worker script. Because of this restriction, it's not possible to call navigator.serviceWorker.register(...) from a page on one (sub-)domain and end up with a service worker that controls pages on another (sub-)domain.

  • There are restrictions in place to prevent you from throwing an https: <iframe> on an http: page and using that to register a service worker. See DOMException when registering service worker inside an https iframe

  • Though I don't know that it's directly related to your question, explicitly calling fetch() for an http: resource within your service worker code will result in a failure in current versions of Chrome, since mixed-content fetch()s are not allowed within a service worker. I don't know if things are 100% settled on that front, and this open bug is still relevant.

If you have pages that live on both abc.123.com and xyz.123.com and you want both sets of pages to be controlled by a service worker, then you need to have two separate service worker registrations. Each registration needs to be for a copy of your service worker JS file that's hosted on the respective domain corresponding to the top-level page, and all pages and service worker scripts need to be accessed via https:.

That being said, you can kick off a service worker registration for a different domain by including a cross-domain <iframe> on a page, but both the host page and the <iframe> need to be served via https:. The normal service worker scoping restrictions apply, so if, for example, you want to register a service worker for the other domain that will cover the entire https://other-domain.com/ scope, you need to make sure that the location of the service worker script being registered is at the top-level, e.g. https://other-domain.com/service-worker.js, not at https://other-domain.com/path/to/service-worker.js. This is the approach used by, for example, the AMP project via the <amp-install-serviceworker> element.



回答2:

Service Worker scripts must be hosted at the same origin (Protocol + Domain name + Port). Each sub-domain is considered a different origin, So, you will need to register a service worker for each one. Each of these workers will have its own cache and scope.



回答3:

Try use Ngnix proxy_pass. This work for me.



回答4:

My bad, I misunderstood a bit. Well, here's the code

if('serviceWorker' in navigator){
    if(window.location.pathname != '/'){
        //register with API
        if(!navigator.serviceWorker.controller) navigator.serviceWorker.register('/service-worker', { scope: '/' });
        //once registration is complete
        navigator.serviceWorker.ready.then(function(serviceWorkerRegistration){
            //get subscription
            serviceWorkerRegistration.pushManager.getSubscription().then(function(subscription){

                //enable the user to alter the subscription
                //jquery selector for enabling whatever you use to subscribe.removeAttr("disabled");
                //set it to allready subscribed if it is so
                if(subscription){
                    //code for showing the user that they're allready subscribed
                }
            });
        });
    }   
}else{  
    console.warn('Service workers aren\'t supported in this browser.');  
}

then here's the event -ish for your subscribe / unsubscribe

// subscribe or unsubscribe to the ServiceWorker
$(document.body).on('change', /*selector*/, function(){
    //new state is checked so we subscribe
    if($(this).prop('checked')){
        navigator.serviceWorker.ready.then(function(serviceWorkerRegistration){
            serviceWorkerRegistration.pushManager.subscribe()  
                .then(function(subscription){  
                    // The subscription was successful  
                    console.log('subscription successful'); //subscription.subscriptionId

                    //save in DB - this is important because 
                    $.post($('#basePath').val() + 'settings/ajax-SW-sub/', {id:subscription.subscriptionId}, function(data){
                        //console.log(data);
                    }, 'json');

                    }).catch(function(e) {  
                        if (Notification.permission === 'denied') {  
                            // The user denied the notification permission which  
                            // means we failed to subscribe and the user will need  
                            // to manually change the notification permission to  
                            // subscribe to push messages
                            console.warn('Permission for Notifications was denied');
                        } else {  
                            // A problem occurred with the subscription; common reasons  
                            // include network errors, and lacking gcm_sender_id and/or  
                            // gcm_user_visible_only in the manifest.  
                            console.error('Unable to subscribe to push.', e);
                        }  
                    });  
        });//*/
    //new state us unchecked so we unsubscribe
    }else{
        $('.js-enable-sub-test').parent().removeClass('checked');
        //get subscription
        navigator.serviceWorker.ready.then(function(reg) {
            reg.pushManager.getSubscription().then(function(subscription) {
                //unregister in db
                $.post($('#basePath').val() + 'settings/ajax-SW-unsub/', {id:subscription.subscriptionId}, function(data){
                    //console.log(data);
                }, 'json');

                //remove subscription from google servers
                subscription.unsubscribe().then(function(successful) {
                    // You've successfully unsubscribed
                    console.log('unsubscribe successful');
                }).catch(function(e) {
                    // Unsubscription failed
                    console.log('unsubscribe failed', e);
                })
            })        
        });//*/
    }
});

after that you need to register an account on the google developer console and register a project for something like *.xxxx.com . Then you need to get a proper manifest json with gcm_sender_id and gcm_user_visible_only

You need to create a key for both server and browser applications, there's more info on that on this page.

https://developers.google.com/web/updates/2015/03/push-notificatons-on-the-open-web?hl=en

The one for browser applications goes in your manifest json.

Then to send out push notifications you'll be using something like this:

  function addSWmessage($args){

    $output = false;

    if(!isset($args['expiration']) || $args['expiration'] == ''){
        $args['expiration'] = date('Y-m-d H:i:s', strtotime('+7 days', time()));
    }

    $sql = sprintf("INSERT INTO `serviceworker_messages` SET title = '%s', body = '%s', imageurl = '%s', linkurl = '%s', hash = '%s', expiration = '%s'",
                    parent::escape_string($args['title']),
                    parent::escape_string($args['text']),
                    parent::escape_string($args['imageurl']),
                    parent::escape_string($args['linkurl']),
                    parent::escape_string(md5(uniqid('******************', true))),
                    parent::escape_string($args['expiration']));

    if($id = parent::insert($sql)){
        $output = $id;
    }

    return $output;

}
function pushSWmessage($args){

    //$args['messageid'] $args['userids'][]

    foreach($args['userids'] as $val){

        $sql = sprintf("SELECT messages_mobile, messages FROM `users_serviceworker_hash` WHERE users_id = '%s'",
                        parent::escape_string($val));

        if($row = parent::queryOne($sql)){
            $m1 = json_decode($row['messages'], true);
            $m1[] = $args['messageid'];
            $m2 = json_decode($row['messages_mobile'], true);
            $m2[] = $args['messageid'];

            $sql = sprintf("UPDATE `users_serviceworker_hash` SET messages = '%s', messages_mobile = '%s' WHERE users_id = '%s'",
                            parent::escape_string(json_encode($m1)),
                            parent::escape_string(json_encode($m2)),
                            parent::escape_string($val['users_id']));

            parent::insert($sql);
        }
    }

    $sql = sprintf("SELECT subscriptionID, users_id FROM `users_serviceworker_subscriptions`");

    if($rows = parent::query($sql)){

        foreach($rows as $val){
            if(in_array($val['users_id'], $args['userids'])){
                $registrationIds[] = $val['subscriptionID'];
            }
        }
        if(isset($registrationIds) && !empty($registrationIds)){
            // prep the bundle
            $msg = array
            (
                'message'       => '!',
                'title'         => '!',
                'subtitle'      => '!',
                'tickerText'    => '!',
                'vibrate'       => 1,
                'sound'         => 1,
                'largeIcon'     => '!',
                'smallIcon'     => '!'
            );

            $headers = array
            (
                'Authorization: key='.SW_API_ACCESS_KEY,
                'Content-Type: application/json'
            );

            $fields = array
            (
                'registration_ids'  => $registrationIds,
                'data'              => $msg
            );

            $ch = curl_init();
            curl_setopt($ch,CURLOPT_URL, 'https://android.googleapis.com/gcm/send');
            curl_setopt($ch,CURLOPT_POST, true);
            curl_setopt($ch,CURLOPT_HTTPHEADER, $headers);
            curl_setopt($ch,CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch,CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch,CURLOPT_POSTFIELDS, json_encode($fields));
            curl_exec($ch);
            curl_close($ch);
        }
    }

}

And no, I don't know what issue you've been having but this works for me with multiple sub domains. :)