Skip to content

Latest commit

 

History

History
491 lines (420 loc) · 14.3 KB

File metadata and controls

491 lines (420 loc) · 14.3 KB

Progressive Web App (PWA) Implementation Plan

Overview

This plan outlines the steps to transform LinuxReport into a Progressive Web App, enabling offline access and an app-like experience while maintaining the site's simplicity and performance.

Implementation Steps

  1. Phase 1: Foundation Setup (2-3 hours)

    • Create templates/service-worker.js with your existing cache system
    • Integrate with your current JavaScript compilation system in app.py
    • Add service worker registration to your existing core.js
    • Create static/manifest.json with your site's branding -(don't do now) Generate PWA icons in static/icons/ using your existing logo
    • Update templates/base.html with PWA meta tags
  2. Phase 2: Flask Integration (1-2 hours)

    • Add service worker route to routes.py
    • Add manifest route to routes.py
    • Update static file serving in Apache config (Updated the file httpd-vhosts-sample.conf)
    • Integrate with your existing diskcache system
    • Update service worker caching strategy
    • Implement cache versioning with your existing system
  3. Phase 3: Performance Optimization (1-2 hours)

    • Optimize PWA icons using your existing image processing
    • Implement proper caching strategies
    • Use your existing static file optimization
    • Implement proper preloading
    • Use your existing JavaScript optimization
    • Leverage your current caching system
  4. Phase 4: Security and Testing (1-2 hours)

    • Ensure HTTPS requirement
    • Update Apache configuration
    • Implement proper cache headers
    • Set appropriate cache durations
    • Test offline functionality
    • Verify cache behavior
    • Check cross-browser compatibility
    • Validate PWA requirements
  5. Phase 5: Integration with Existing Systems (1-2 hours)

    • Update your existing core.js compilation
    • Integrate with your current cache busting
    • Add PWA-specific functionality
    • Update your Apache configuration
    • Ensure proper serving of PWA assets
    • Integrate with your CDN setup
  6. Phase 6: Documentation and Deployment (1-2 hours)

    • Update README.md with PWA information
    • Document PWA features in agents.md
    • Add PWA configuration to config.yaml
    • Update deployment scripts
    • Configure production environment
    • Set up monitoring for PWA metrics

Implementation Details

  1. Service Worker Integration

    // Add this to your existing JavaScript file (e.g., linuxreport.js)
    if ('serviceWorker' in navigator) {
        window.addEventListener('load', () => {
            navigator.serviceWorker.register('/service-worker.js')
                .then(registration => {
                    console.log('ServiceWorker registration successful');
                })
                .catch(err => {
                    console.log('ServiceWorker registration failed: ', err);
                });
        });
    }
  2. Service Worker File (must be separate file: service-worker.js)

    // This must be in a separate file: service-worker.js
    //Service workers run in a different context than your main JavaScript
    //They need to be able to intercept network requests
    //They need to be able to work even when your main JavaScript isn't running
    //They need to be able to update independently of your main application
    
    const CACHE_VERSION = 'v1';
    const CACHE_NAME = `linuxreport-${CACHE_VERSION}`;
    const STATIC_ASSETS = [
        '/',
        '/static/linuxreport.css',
        '/static/linuxreport.js',
        '/static/manifest.json'
    ];
    
    self.addEventListener('install', (event) => {
        event.waitUntil(
            caches.open(CACHE_NAME)
                .then(cache => cache.addAll(STATIC_ASSETS))
        );
    });
    
    self.addEventListener('fetch', (event) => {
        event.respondWith(
            caches.match(event.request)
                .then(response => response || fetch(event.request))
        );
    });
  3. Flask Integration

    # app.py additions
    @app.route('/service-worker.js')
    def service_worker():
        response = make_response(
            send_from_directory('static/js', 'service-worker.js')
        )
        response.headers['Content-Type'] = 'application/javascript'
        response.headers['Service-Worker-Allowed'] = '/'
        return response
    
    @app.route('/static/manifest.json')
    def manifest():
        return send_from_directory('static', 'manifest.json')
  4. Apache Configuration

    # httpd-vhosts-sample.conf additions
    <VirtualHost *:80>
        # ... existing configuration ...
        
        # PWA specific headers
        Header set Service-Worker-Allowed "/"
        Header set Cache-Control "public, max-age=31536000"
        
        # ... rest of configuration ...
    </VirtualHost>
  5. JavaScript Integration

    // templates/core.js additions
    if ('serviceWorker' in navigator) {
        window.addEventListener('load', () => {
            navigator.serviceWorker.register('/static/js/service-worker.js')
                .then(registration => {
                    console.log('ServiceWorker registration successful');
                })
                .catch(err => {
                    console.log('ServiceWorker registration failed: ', err);
                });
        });
    }

This implementation plan takes advantage of your existing:

  • JavaScript compilation system
  • Cache management
  • Static file serving
  • Apache configuration
  • Image processing
  • Documentation structure

The implementation follows your existing patterns and integrates seamlessly with your current architecture while adding PWA capabilities.

PWA Caching Integration

The application uses multiple caching layers that need to be integrated with the PWA service worker:

Existing Caching System

  1. In-Memory Caching (cacheout - via g_cm)

    • Location: shared.py
    • Purpose: Caches full pages and RSS templates
    • Integration: Use for dynamic content caching in service worker
  2. Disk-Based Caching (diskcache - via g_c)

    • Location: shared.py
    • Purpose: Persistent storage for weather data, chat comments, banned IPs
    • Integration: Use for offline data storage
  3. Flask-Assets Asset Management

    • Location: app.py
    • Purpose: Automatic asset bundling, minification, and cache busting
    • Integration: Use compiled assets for service worker

Service Worker Implementation

  1. Cache Strategies

    • Static Assets: Cache First with Flask-Assets versioning
    • Dynamic Content: Network First with fallback to cache
    • API Responses: Stale While Revalidate
    • Offline Content: Cache First with offline fallback
  2. Cache Management

    • Use separate caches for different content types
    • Implement cache cleanup based on size and age
    • Handle cache versioning using Flask-Assets system
  3. Offline Support

    • Cache essential static assets
    • Store dynamic content in IndexedDB
    • Provide offline fallback page
    • Sync data when back online
  4. Performance Optimization

    • Use Flask-Assets compiled assets
    • Implement proper cache headers
    • Optimize asset loading
    • Handle background sync

Implementation Steps

  1. Service Worker Setup

  2. Cache Configuration

    // Cache names
    const STATIC_CACHE = 'static-v1';
    const DYNAMIC_CACHE = 'dynamic-v1';
    const OFFLINE_CACHE = 'offline-v1';
  3. Cache Strategies

    // Static assets - Cache First
    async function cacheFirst(request) {
      const cached = await caches.match(request);
      return cached || fetch(request);
    }
    
    // Dynamic content - Network First
    async function networkFirst(request) {
      try {
        const response = await fetch(request);
        const cache = await caches.open(DYNAMIC_CACHE);
        cache.put(request, response.clone());
        return response;
      } catch (error) {
        return caches.match(request);
      }
    }
  4. Offline Support

    // Offline fallback
    async function offlineFallback() {
      const cache = await caches.open(OFFLINE_CACHE);
      return cache.match('/offline.html');
    }

Important Notes

  1. Cache Versioning

    • Use Flask-Assets versioning
    • Update cache names when content changes
    • Handle cache cleanup properly
  2. Performance Considerations

    • Cache only necessary assets
    • Implement proper cache headers
    • Use compression for cached content
    • Handle cache size limits
  3. Security

    • Validate cached content
    • Handle sensitive data properly
    • Implement proper CORS headers
    • Use HTTPS for all requests
  4. Testing

    • Test offline functionality
    • Verify cache strategies
    • Check performance impact
    • Validate security measures

Phase 2: Flask Integration

1. Update Flask Configuration

  1. Add PWA Configuration

    # In app.py
    PWA_CONFIG = {
        'name': 'LinuxReport',
        'short_name': 'LR',
        'description': 'Linux, AI, and Tech News Aggregator',
        'start_url': '/',
        'display': 'standalone',
        'background_color': '#ffffff',
        'theme_color': '#000000',
        'icons': [
            {
                'src': '/static/icons/icon-192x192.png',
                'sizes': '192x192',
                'type': 'image/png'
            },
            {
                'src': '/static/icons/icon-512x512.png',
                'sizes': '512x512',
                'type': 'image/png'
            }
        ]
    }
  2. Add PWA Routes

    # In routes.py
    @app.route('/manifest.json')
    def manifest():
        return jsonify(PWA_CONFIG)
    
    @app.route('/offline.html')
    def offline():
        return render_template('offline.html')
    
    @app.route('/service-worker.js')
    def service_worker():
        response = make_response(
            render_template('service-worker.js'),
            200,
            {'Content-Type': 'application/javascript'}
        )
        response.headers['Service-Worker-Allowed'] = '/'
        return response
  3. Update Base Template

    <!-- In templates/base.html -->
    <head>
        <!-- Add these lines -->
        <link rel="manifest" href="{{ url_for('manifest') }}">
        <meta name="theme-color" content="#000000">
        <meta name="apple-mobile-web-app-capable" content="yes">
        <meta name="apple-mobile-web-app-status-bar-style" content="black">
        <link rel="apple-touch-icon" href="{{ url_for('static', filename='icons/icon-192x192.png') }}">
    </head>

2. Create PWA Templates

  1. Create offline.html

    <!-- In templates/offline.html -->
    <!DOCTYPE html>
    <html>
    <head>
        <title>Offline - LinuxReport</title>
        <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
    </head>
    <body>
        <div class="offline-container">
            <h1>You're Offline</h1>
            <p>Please check your internet connection and try again.</p>
            <button onclick="window.location.reload()">Retry</button>
        </div>
    </body>
    </html>
  2. Create service-worker.js Template

    // In templates/service-worker.js
    const CACHE_NAME = 'linuxreport-v1';
    const OFFLINE_URL = '/offline.html';
    
    const STATIC_ASSETS = [
        '/',
        '/offline.html',
        '/static/css/style.css',
        '/static/js/linuxreport.js',
        '/static/icons/icon-192x192.png',
        '/static/icons/icon-512x512.png'
    ];
    
    self.addEventListener('install', (event) => {
        event.waitUntil(
            caches.open(CACHE_NAME)
                .then((cache) => cache.addAll(STATIC_ASSETS))
        );
    });
    
    self.addEventListener('fetch', (event) => {
        if (event.request.mode === 'navigate') {
            event.respondWith(
                fetch(event.request)
                    .catch(() => caches.match(OFFLINE_URL))
            );
        } else {
            event.respondWith(
                caches.match(event.request)
                    .then((response) => response || fetch(event.request))
            );
        }
    });

3. Add PWA Registration

  1. Update core.js
    // In templates/core.js
    // Add at the beginning of the file
    if ('serviceWorker' in navigator) {
        window.addEventListener('load', () => {
            navigator.serviceWorker.register('/service-worker.js')
                .then(registration => {
                    console.log('ServiceWorker registration successful');
                })
                .catch(err => {
                    console.log('ServiceWorker registration failed: ', err);
                });
        });
    }

4. Create PWA Icons

  1. Generate Icons

    • Create 192x192 and 512x512 PNG icons
    • Place in static/icons/ directory
    • Ensure icons follow PWA guidelines:
      • Simple, recognizable design
      • No transparency
      • Safe area for different devices
      • Proper padding
  2. Icon Requirements

    • Format: PNG
    • Sizes: 192x192 and 512x512
    • Background: Solid color
    • No transparency
    • Clear visibility at small sizes

5. Update Static File Handling

  1. Add Cache Headers

    # In app.py
    @app.after_request
    def add_cache_headers(response):
        if request.path.startswith('/static/'):
            response.headers['Cache-Control'] = 'public, max-age=31536000'
        return response
  2. Update Static File Hash Function

    # In app.py
    @lru_cache()
    def static_file_hash(filename):
        """Generate hash for static files, including PWA assets."""
        if filename == 'linuxreport.js':
            return get_combined_hash()
        try:
            with open(os.path.join(app.static_folder, filename), 'rb') as f:
                return hashlib.md5(f.read()).hexdigest()
        except:
            return str(time.time())

6. Testing Checklist

  1. Basic PWA Features

    • Manifest loads correctly
    • Service worker registers
    • Icons display properly
    • Offline page works
    • Add to home screen works
  2. Caching

    • Static assets cache properly
    • Dynamic content updates
    • Offline functionality works
    • Cache versioning works
  3. Performance

    • Page loads quickly
    • Assets load from cache
    • No unnecessary network requests
    • Smooth offline experience
  4. Browser Compatibility

    • Works in Chrome
    • Works in Firefox
    • Works in Safari
    • Works in Edge