Skip to content

Conversation

joey-the-33rd
Copy link
Owner

@joey-the-33rd joey-the-33rd commented Oct 14, 2025

User description

This PR fixes two API endpoint issues identified in the application logs:

Changes Made

1. Added GET /api/database/jobs/{job_id} endpoint

  • Problem: GET requests to were returning 405 Method Not Allowed
  • Solution: Added method to class and corresponding FastAPI endpoint
  • Files modified: ,

2. Fixed analytics dashboard authentication

  • Problem: GET requests to were returning 403 Forbidden due to required authentication
  • Solution: Changed authentication dependency from to
  • Files modified:

Testing

  • Verified the new GET endpoint returns job data for valid IDs and 404 for invalid IDs
  • Verified the analytics dashboard now loads without authentication requirements
  • Application server runs successfully with both fixes

Related Issues

  • Fixes 405 Method Not Allowed error for individual job retrieval
  • Fixes 403 Forbidden error for analytics dashboard access

PR Type

Bug fix, Enhancement


Description

  • Add GET endpoint for retrieving individual jobs by ID (/api/database/jobs/{job_id})

  • Fix analytics dashboard authentication to allow optional user access

  • Implement DOM loading fix for database manager JavaScript initialization

  • Add auto-open job URL feature when viewing job details


Diagram Walkthrough

flowchart LR
  A["API Request"] --> B["GET /api/database/jobs/{job_id}"]
  B --> C["JobSearchStorage.get_job_by_id()"]
  C --> D["Return Job Data"]
  E["Analytics Dashboard"] --> F["Optional Authentication"]
  F --> G["Allow Public Access"]
  H["Database Manager JS"] --> I["DOMContentLoaded Wrapper"]
  I --> J["Initialize dbManager"]
Loading

File Walkthrough

Relevant files
Enhancement
job_search_storage.py
Add job retrieval method to storage class                               

job_search_storage.py

  • Add get_job_by_id() method to retrieve specific job by ID
  • Handle database connection, data serialization, and error logging
  • Convert PostgreSQL arrays and datetime fields to JSON-compatible
    formats
+42/-1   
Bug fix
stackscout_web.py
Add job endpoint and fix analytics authentication               

stackscout_web.py

  • Add GET /api/database/jobs/{job_id} endpoint for individual job
    retrieval
  • Change analytics dashboard authentication from required to optional
  • Return 404 status for non-existent jobs
+16/-1   
database_manager_fixed.js
Fix DOM loading and add job URL auto-open                               

static/js/database_manager_fixed.js

  • Wrap DatabaseManager initialization in DOMContentLoaded event listener
  • Add auto-open job URL in new tab when viewing details
  • Declare dbManager globally to maintain event handler functionality
+17/-5   
Configuration changes
database_manager_enhanced.html
Update template to use fixed JavaScript file                         

templates/database_manager_enhanced.html

  • Update script tag to load database_manager_fixed.js instead of
    database_manager.js
+1/-0     

…_optional_current_user for /api/analytics and /analytics routes to allow public access to analytics data without requiring user login
…ation in DOMContentLoaded event listener to ensure DOM is fully loaded before executing JS, preventing stats from not displaying
…ager_fixed.js instead of database_manager.js to apply the DOM loading fix for stats display
…bManager globally and revert analytics to require authentication for security
…ics dashboard authentication

- Add get_job_by_id method to JobSearchStorage class
- Add GET /api/database/jobs/{job_id} endpoint to retrieve individual jobs
- Change analytics dashboard authentication from required to optional
- Fix 405 Method Not Allowed error for job retrieval by ID
- Fix 403 Forbidden error for analytics dashboard access
Copy link

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Unvalidated external redirect

Description: Automatically opening a new tab to an external URL from untrusted backend data
(job.source_url) without validation can be abused for open redirect or tabnabbing;
validate/whitelist origin and use rel="noopener noreferrer".
database_manager_fixed.js [270-275]

Referred Code
// Open the job URL in a new tab
if (frontendJob.source_url && frontendJob.source_url !== 'N/A') {
    window.open(frontendJob.source_url, '_blank');
} else {
    this.showNotification('Job URL not available', 'warning');
}
Unsafe array parsing

Description: Splitting PostgreSQL array strings by comma to build lists may mishandle values containing
commas or quotes leading to data corruption and potential downstream injection risks; use
proper array parsing or drivers' array support.
job_search_storage.py [550-553]

Referred Code
if isinstance(job.get('tech_stack'), str):
    job['tech_stack'] = job['tech_stack'].strip('{}').split(',') if job['tech_stack'] else []
if isinstance(job.get('keywords'), str):
    job['keywords'] = job['keywords'].strip('{}').split(',') if job['keywords'] else []
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
No custom compliance provided

Follow the guide to enable custom compliance check.

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

Copy link

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Implement a database connection pool

Replace the current practice of creating a new database connection per API
request with a database connection pool. The pool should be initialized at
application startup to improve efficiency and scalability.

Examples:

stackscout_web.py [261-263]
        storage = JobSearchStorage(DB_CONFIG)
        job = storage.get_job_by_id(job_id)
        storage.close()
job_search_storage.py [14-20]
    def __init__(self, db_config):
        """Initialize database connection"""
        self.db_config = db_config
        self.connection = None
        try:
            self.connect()
        except Exception as e:

Solution Walkthrough:

Before:

# In stackscout_web.py
@app.get("/api/database/jobs/{job_id}")
async def get_job_by_id(job_id: int):
    try:
        # A new JobSearchStorage instance is created for each request.
        storage = JobSearchStorage(DB_CONFIG)
        job = storage.get_job_by_id(job_id)
        storage.close()
        ...
    ...

# In job_search_storage.py
class JobSearchStorage:
    def __init__(self, db_config):
        # The constructor establishes a new database connection.
        self.connection = None
        self.connect()

    def connect(self):
        self.connection = psycopg2.connect(**self.db_config)

After:

# In stackscout_web.py (or a central app file)
# Initialize a connection pool once at application startup.
db_pool = psycopg2.pool.SimpleConnectionPool(1, 20, **DB_CONFIG)
storage = JobSearchStorage(db_pool) # Create a single storage instance.

@app.get("/api/database/jobs/{job_id}")
async def get_job_by_id(job_id: int):
    # Use the shared storage instance, which gets connections from the pool.
    job = storage.get_job_by_id(job_id)
    ...

# In job_search_storage.py
class JobSearchStorage:
    def __init__(self, db_pool):
        self.db_pool = db_pool

    def get_job_by_id(self, job_id):
        conn = self.db_pool.getconn() # Get connection from pool.
        try:
            with conn.cursor() as cursor:
                ...
        finally:
            self.db_pool.putconn(conn) # Return connection to pool.
Suggestion importance[1-10]: 9

__

Why: This suggestion correctly identifies a critical performance and scalability issue where a new database connection is created for every API request, and the PR adds a new endpoint that perpetuates this inefficient pattern.

High
Possible issue
Fix incorrect API response handling

Fix incorrect API response handling in viewJobDetails. The code should access
data.job from the response, not the entire response object, before passing it to
this.mapJobBackendToFrontend.

static/js/database_manager_fixed.js [263-275]

     async viewJobDetails(jobId) {
         try {
             const response = await fetch(`/api/database/jobs/${jobId}`);
-            const job = await response.json();
+            const data = await response.json();
 
-            const frontendJob = this.mapJobBackendToFrontend(job);
+            if (!response.ok) {
+                throw new Error(data.error || 'Failed to fetch job details');
+            }
+
+            const frontendJob = this.mapJobBackendToFrontend(data.job);
 
             // Open the job URL in a new tab
             if (frontendJob.source_url && frontendJob.source_url !== 'N/A') {
                 window.open(frontendJob.source_url, '_blank');
             } else {
                 this.showNotification('Job URL not available', 'warning');
             }
 ...

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a critical bug where the frontend JavaScript mishandles the API response structure, which would cause the "view job details" feature to fail. The fix is accurate and necessary.

High
Fix incorrect parsing of empty arrays

Fix the parsing of empty PostgreSQL array strings for tech_stack and keywords.
The current logic incorrectly produces [''] instead of an empty list [].

job_search_storage.py [550-553]

     if isinstance(job.get('tech_stack'), str):
-        job['tech_stack'] = job['tech_stack'].strip('{}').split(',') if job['tech_stack'] else []
+        stripped_stack = job['tech_stack'].strip('{}')
+        job['tech_stack'] = stripped_stack.split(',') if stripped_stack else []
     if isinstance(job.get('keywords'), str):
-        job['keywords'] = job['keywords'].strip('{}').split(',') if job['keywords'] else []
+        stripped_keywords = job['keywords'].strip('{}')
+        job['keywords'] = stripped_keywords.split(',') if stripped_keywords else []
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies a bug in the new get_job_by_id method where parsing an empty PostgreSQL array string results in [''] instead of [], and provides a correct fix.

Medium
  • More

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant