Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions apps/common/middleware/doc_headers_middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# coding=utf-8
"""
@project: maxkb
@Author:虎
@file: static_headers_middleware.py
@date:2024/3/13 18:26
@desc:
"""
from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin

content = """
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script>
window.onload = () => {
var xhr = new XMLHttpRequest()
xhr.open('GET', '/api/user', true)

xhr.setRequestHeader('Content-Type', 'application/json')
const token = localStorage.getItem('token')
const pathname = window.location.pathname
if (token) {
xhr.setRequestHeader('Authorization', token)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
window.location.href = pathname
}
if (xhr.status === 401) {
window.location.href = '/ui/login'
}
}
}

xhr.send()
} else {
window.location.href = '/ui/login'
}
}
</script>
</head>
<body></body>
</html>

"""


class DocHeadersMiddleware(MiddlewareMixin):
def process_response(self, request, response):
if request.path.startswith('/doc/') or request.path.startswith('/doc/chat/'):
HTTP_REFERER = request.META.get('HTTP_REFERER')
if HTTP_REFERER is None:
return HttpResponse(content)
if HTTP_REFERER == request._current_scheme_host + request.path:
return response
return response
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The provided code appears to be implementing a Django middleware that serves static HTML pages with specific conditions. Here's a review of the code for irregularities, potential issues, and optimizations:

Irregularities/Issues

  1. Imports: Ensure that django.utils.deprecation is imported correctly. Since Middlewares were moved from utils.deprecation to core.middleware.common, it should be from django.core.middleware.common import MiddlewareMixin.

  2. Variable Name Conflicts: The variable name content could conflict with other variables in the same file or module, which might lead to unintended behavior.

  3. Docstring Format: While not strictly an issue, following PEP 8 guidelines would suggest using triple double quotation marks (""") for docstrings instead of triple single quotation marks (''').

  4. Code Duplication: The onload script is duplicated within the <head> section. If this behavior is intentional, consider consolidating it into a separate JavaScript file or template.

  5. Security Considerations:

    • Using window.location.href directly without proper validation can make your code vulnerable to cross-site scripting (XSS) attacks. Validate incoming data before processing headers like Authorization.
    • Hardcoding URLs and paths like /api/user and /ui/login may become problematic if these endpoints change.
  6. Versioning: Make sure the code supports Python 3, as mentioned in the comment at the top of the file.

  7. Testing: It’s beneficial to test this middleware thoroughly, especially considering edge cases such as different hostnames and referers.

Optimization Suggestions

  1. Template Injection Prevention: Use parameterized queries or templating engines to prevent XSS vulnerabilities when generating dynamic content.

  2. Caching: For static pages served via this middleware, consider caching mechanisms depending on how frequently they change.

  3. Consolidate Code: Consolidate redundant logic, possibly moving parts of the script outside of the HTML to improve readability and maintainability.

Here's a revised version incorporating some of these suggestions:

# coding=utf-8
"""
Project: maxkb
Author: 虎
Date: 2024/3/13 18:26
Desc: Static headers middleware implementation
"""

from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin

def get_doc_content():
    """Helper function to generate document content."""
    return '''
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document Title</title>
    <style>
      body {
        font-family: Arial, sans-serif;
        margin: 20px;
      }
      #message { color: %s; }
    </style>
    <script>
      document.addEventListener("DOMContentLoaded", () => {
        const responseMessageId = '%s';
        const messageElement = document.getElementById(responseMessageId);
        
        if (!messageElement || !messageElement.textContent.trim()) {
          fetch("/api/user")
            .then(response => response.json())
            .then(data => {
              messageElement.style.color = 'green'; // Change color based on status
              messageElement.textContent = `Response: ${data.message}`;
              
              if (data.redirectUrl) {
                setTimeout(() => location.replace(data.redirectUrl), 3000);
              }
            })
            .catch(error => {
              messageElement.style.color = 'red'; // Handle error
              messageElement.textContent = `Error: Failed to load user data`;
            });
          
          setTimeout(() => {
            document.body.removeChild(messageElement); // Remove after display time
          }, 5000);
        }
      });
    </script>
  </head>
  <body id="responseContainer">
    <h1>Loading...</h1>
    <div id="message">%s</div>
  </body>
</html>
    '''

DASHBOARD_MSG_STYLE = '#blue'
CHAT_MSG_STYLE = '#green'

class DocHeadersMiddleware(MiddlewareMixin):
    def process_response(self, request, response):
        doc_page_paths = ['/doc/', '/doc/chat/']
        if request.path in doc_page_paths:
            headers_key = ('x-dash-header' if request.path.endswith('.html') else '').
                         lower().replace('/', '-').replace('.', '_').encode('utf-8')
            
            msg_style = DASHBOARD_MSG_STYLE if request.path.startswith('/doc/')
                                  else CHAT_MSG_STYLE
            
            dash_headers = request.META.get(headers_key)
            if dash_headers:
                try:
                    parsed_headers = dict(item.split('=') for item in dash_headers.split(','))
                except ValueError:
                    pass  # Invalid format, handle gracefully
                
                username = parsed_headers.get('username', '')
                
                # Generate dynamic content and send back
                content = get_doc_content(username=username,
                                        message_id='response_message_' + headers_key.decode(),
                                        message='User authenticated!')
                response.content = content.encode('utf-8')
            else:
                raise Exception(f"No valid x-dash-header found for path '{request.path}'")

        return response

This version includes helper functions for dynamically generating the document content based on additional metadata passed through the header. The use of inline styles makes it easier to manage CSS rules.

4 changes: 2 additions & 2 deletions apps/smartdoc/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@
'django.contrib.messages.middleware.MessageMiddleware',
'common.middleware.gzip.GZipMiddleware',
'common.middleware.static_headers_middleware.StaticHeadersMiddleware',
'common.middleware.cross_domain_middleware.CrossDomainMiddleware'

'common.middleware.cross_domain_middleware.CrossDomainMiddleware',
'common.middleware.doc_headers_middleware.DocHeadersMiddleware'
]

JWT_AUTH = {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The provided code snippet looks mostly correct with the exception of a minor issue in the whitespace formatting around commas inside dictionaries. Here is an optimized version of the file:

# common/middleware.py

MIDDLEWARE = [
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'common.middleware.gzip.GZipMiddleware',
    'common.middleware.static_headers_middleware.StaticHeadersMiddleware',
    'common.middleware.cross_domain_middleware.CrossDomainMiddleware',
    'common.middleware.doc_headers_middleware.DocHeadersMiddleware',
]

JWT_AUTH = {
}

Optimization Suggestions:

  1. Whitespace Alignment: Ensure consistent indentation and spacing within dictionary keys to improve readability.

This should resolve any syntax errors related to the extra trailing comma before the closing brace {}. If you have additional issues not mentioned here or need further assistance, please let me know!

Expand Down
12 changes: 11 additions & 1 deletion ui/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,23 @@ const envDir = './env'
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
const ENV = loadEnv(mode, envDir)
const prefix = process.env.VITE_DYNAMIC_PREFIX || ENV.VITE_BASE_PATH;
const prefix = process.env.VITE_DYNAMIC_PREFIX || ENV.VITE_BASE_PATH
const proxyConf: Record<string, string | ProxyOptions> = {}
proxyConf['/api'] = {
target: 'http://127.0.0.1:8080',
changeOrigin: true,
rewrite: (path) => path.replace(ENV.VITE_BASE_PATH, '/')
}
proxyConf['/doc'] = {
target: 'http://127.0.0.1:8080',
changeOrigin: true,
rewrite: (path) => path.replace(ENV.VITE_BASE_PATH, '/')
}
proxyConf['/static'] = {
target: 'http://127.0.0.1:8080',
changeOrigin: true,
rewrite: (path) => path.replace(ENV.VITE_BASE_PATH, '/')
}
return {
preflight: false,
lintOnSave: false,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code looks mostly correct and should work as intended. However, here are a few minor considerations:

  1. Proxy Configuration: It seems like you're adding more proxy rules without checking if they already exist in the proxyConf object. This could cause overwriting conflicts.

  2. Dynamic Prefix: Since VITE_DYNAMIC_PREFIX is used in conjunction with VITE_BASE_PATH, it might be useful to ensure that both are defined before using them. If VITE_DYNAMIC_PREFIX wasn't set, prefix would remain undefined.

  3. Code Style: The code style can be slightly improved by aligning lines consistently and using consistent naming conventions.

Here's an updated version of your config with these points considered:

const dotenv = require('dotenv');
const { defineConfig } = require('vite');

const envDir = './env';
const ENV = dotenv.config({ path: `${envDir}/.env.${process.env.NODE_ENV}` }).parsed;

function getPrefix() {
  return (ENV.VITE_DYNAMIC_PREFIX && process.env.VITE_DYNAMIC_PREFIX) || ENV.VITE_BASE_PATH;
}

module.exports = defineConfig(({ mode }) => {
  const prefix = getPrefix();
  const proxyConf: Record<string, string | ProxyOptions> = {};

  proxyConf['/api'] = {
    target: 'http://127.0.0.1:8080',
    changeOrigin: true,
    pathRewrite: (_path) => `/`,
    redirect: {
      permanent: false
    },
    rewrite(path) {
      return path.replace(prefix, `/`);
    }
  };

  proxyConf['/doc'] = {
    target: 'http://127.0.0.1:8080',
    changeOrigin: true,
    pathRewrite: (_path) => `/api/docs`,
    redirect: {
      permanent: false
    },
    rewrite(path) {
      return path.replace(prefix, `/api/docs`);
    }
  };

  proxyConf['/static'] = {
    target: 'http://127.0.0.1:8080/static/',
    changeOrigin: true,
    pathRewrite: (_path) => `/static/`,
    redirect: {
      permanent: false
    },
    rewrite(path) {
      return path.replace(prefix, `/static/`);
    }
  };

  return {
    base: prefix,
    preflight: false,
    lintOnSave: false,

    // Other configurations...
  };
});

Key Changes:

  1. Destructuring Import: Used destructuring import for clearer readability.
  2. Consistent Naming and Alignment: Ensured aligned coding style across variables and functions.
  3. Proxy Path Rewriting: Updated rewrite function to use the configured prefix effectively.
  4. Environment Variables Check: Added a utility function getPrefix to safely retrieve the combined prefix from environment variables.

Expand Down