Skip to content

Trip Offer Page Deployment Plan

Overview

This document outlines the implementation plan for deploying generated Trip Offer HTML pages directly to production web servers without a full build/deploy cycle.

Current State

  • HTML pages are generated via tripoffer/page/generate API endpoint
  • Generated HTML is downloaded to the user's browser for manual deployment
  • No automated mechanism to push pages to web servers

Proposed Solution

Implement a two-step publish workflow: 1. Preview & Download - Generate and download HTML for review (current behavior) 2. Deploy to Production - Write generated HTML to NFS staging directory for automatic distribution

Architecture

┌─────────────────────────────────────────────────────────────────────────┐
│                           TQPro Admin UI                                │
│                                                                         │
│   [Preview & Download]               [Deploy to Production]             │
│          │                                    │                         │
│          ▼                                    ▼                         │
│   Downloads .html to                  Calls deploy API                  │
│   user's browser                                                        │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│                           TQPro API Server                              │
│                                                                         │
│   POST /tripoffer/page/deploy                                           │
│   - Generates HTML (same as download)                                   │
│   - Writes to: /mnt/deploy-staging/{pageName}                           │
│   - Logs deployment to audit table                                      │
│   - Returns success/failure                                             │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│                    NFS Staging Directory                                │
│                    /mnt/deploy-staging/                                 │
│                                                                         │
│   ├── dubai-packages.html                                               │
│   ├── turkiye.html                                                      │
│   └── maldives-packages.html                                            │
└─────────────────────────────────────────────────────────────────────────┘
                            ┌───────────────────┴───────────────────┐
                            │      rsync/lsyncd daemon              │
                            │   (cron every 1-5 min or inotify)     │
                            └───────────────────┬───────────────────┘
                    ┌───────────────────────────┼───────────────────────────┐
                    ▼                           ▼                           ▼
           ┌────────────────┐          ┌────────────────┐          ┌────────────────┐
           │  Web Server 1  │          │  Web Server 2  │          │  Web Server N  │
           │  /var/www/html │          │  /var/www/html │          │  /var/www/html │
           └────────────────┘          └────────────────┘          └────────────────┘

Implementation Tasks

1. Configuration Updates

File: config/tlinqapi.properties

Add new configuration entries:

# Deployment staging directory (NFS mounted)
deploy.staging.dir=/mnt/deploy-staging

# Optional: subdirectory for trip offer pages
deploy.tripoffer.subdir=offers

# Enable/disable deployment feature
deploy.enabled=true

2. Database Changes

File: config/db-changes/trip-page-deploy-log.sql

Create deployment audit table:

CREATE TABLE nts.trip_page_deploy_log (
    deploy_id SERIAL PRIMARY KEY,
    page_id INTEGER REFERENCES nts.trip_page(page_id),
    page_name VARCHAR(200),
    deployed_by VARCHAR(100),
    deployed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    skeleton_id INTEGER,
    snippet_file_id INTEGER,
    snippet_count INTEGER,
    file_path VARCHAR(500),
    status VARCHAR(20)  -- 'success', 'failed', 'rolled_back'
);

CREATE INDEX idx_deploy_log_page_id ON nts.trip_page_deploy_log(page_id);
CREATE INDEX idx_deploy_log_deployed_at ON nts.trip_page_deploy_log(deployed_at);

3. Backend Implementation

3.1 TripOfferFacade Changes

File: tqapp/src/main/java/com/perun/tlinq/entity/trip/TripOfferFacade.java

Add new method:

/**
 * Deploy generated HTML page to staging directory
 * @param pageId Page to deploy
 * @param skeletonId Skeleton template to use
 * @param snippetFileId Snippet template to use
 * @param deployedBy Username performing deployment
 * @return Deployment result with file path and timestamp
 */
public Map<String, Object> deployHtmlPage(Integer pageId, Integer skeletonId,
        Integer snippetFileId, String deployedBy) throws TlinqClientException {
    // 1. Generate HTML (reuse existing generateHtmlPage logic)
    // 2. Get deployment directory from configuration
    // 3. Write file to staging directory
    // 4. Create backup of previous version if exists
    // 5. Log deployment to audit table
    // 6. Return result
}

3.2 TripOfferApi Changes

File: tqapi/src/main/java/com/perun/tlinq/api/TripOfferApi.java

Add new endpoint:

@POST
@Path("/page/deploy")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response deployPage(Map reqData) {
    // Validate admin permissions
    // Call facade.deployHtmlPage()
    // Return result
}

3.3 API Roles Configuration

File: config/api-roles.properties

Add permission entry:

tripoffer/page/deploy=admin

4. Frontend Implementation

4.1 HTML Changes

File: tqweb-adm/offerman.html

Update the generation section buttons:

<!-- Current -->
<button id="gen_generate">Generate & Download</button>
<button id="manage_skeletons">Manage Skeletons</button>

<!-- Proposed -->
<button id="gen_download">Preview & Download</button>
<button id="gen_deploy">Deploy to Production</button>
<button id="manage_skeletons">Manage Skeletons</button>

Add confirmation dialog:

<div class="small reveal" id="deploy_confirm_dlg" data-reveal>
    <h4>Deploy to Production</h4>
    <p>Are you sure you want to deploy <strong id="deploy_page_name"></strong> to production?</p>
    <p>This will make the page live on all web servers within 2 minutes.</p>
    <button class="button success" id="deploy_confirm">Deploy Now</button>
    <button class="button secondary" data-close>Cancel</button>
</div>

4.2 JavaScript Changes

File: tqweb-adm/js/modules/offerman.js

Add new functions:

/**
 * Open deploy confirmation dialog
 */
export function confirmDeploy() {
    if (!selectedPageId) {
        $.notify('Please select a page first', 'warning');
        return;
    }
    $('#deploy_page_name').text(selectedPageData.pageName);
    $('#deploy_confirm_dlg').foundation('open');
}

/**
 * Execute deployment
 */
export function executeDeploy() {
    const reqData = {
        session: "",
        pageId: selectedPageId,
        skeletonId: parseInt($('#gen_skeleton').val()),
        snippetFileId: parseInt($('#gen_snippet_file').val())
    };

    tlinq('tripoffer/page/deploy', reqData).then(
        (data) => {
            $.notify(`Deployed successfully to ${data.deployedTo}`, 'success');
            $('#deploy_confirm_dlg').foundation('close');
        },
        (err) => {
            $.notify(err.errorMessage || 'Deployment failed', 'error');
        }
    );
}

5. Infrastructure Setup

5.1 NFS Mount Configuration

On TQPro API server:

# /etc/fstab entry
nfs-server:/deploy-staging  /mnt/deploy-staging  nfs  defaults,rw  0  0

5.2 Rsync Distribution Options

Option A: Cron-based (Simple)

# /etc/cron.d/deploy-sync
*/2 * * * * root rsync -avz --delete /mnt/deploy-staging/ webserver1:/var/www/html/
*/2 * * * * root rsync -avz --delete /mnt/deploy-staging/ webserver2:/var/www/html/

Option B: Lsyncd (Recommended for real-time)

-- /etc/lsyncd.conf
sync {
    default.rsync,
    source = "/mnt/deploy-staging/",
    target = "webserver1:/var/www/html/",
    rsync = { archive = true, compress = true }
}
sync {
    default.rsync,
    source = "/mnt/deploy-staging/",
    target = "webserver2:/var/www/html/",
    rsync = { archive = true, compress = true }
}

Option C: Inotify Script

#!/bin/bash
# /opt/scripts/deploy-watcher.sh
inotifywait -m -e close_write /mnt/deploy-staging/ | while read dir event file; do
    rsync -avz "$dir$file" webserver1:/var/www/html/
    rsync -avz "$dir$file" webserver2:/var/www/html/
    logger "Deployed $file to web servers"
done

API Specification

Deploy Endpoint

Request:

POST /api/tripoffer/page/deploy
Content-Type: application/json

{
    "session": "",
    "pageId": 123,
    "skeletonId": 1,
    "snippetFileId": 1
}

Success Response:

{
    "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
    "apiData": {
        "success": true,
        "fileName": "turkiye.html",
        "deployedTo": "/mnt/deploy-staging/offers/turkiye.html",
        "deployedAt": "2024-12-23T14:30:00Z",
        "snippetCount": 5,
        "backupCreated": true
    }
}

Error Response:

{
    "apiStatus": { "errorCode": "GENERAL", "errorMessage": "Deployment directory not writable" },
    "apiData": null
}

Safety Features

Feature Description
Backup before overwrite Keep last 5 versions in /mnt/deploy-staging/.backup/{pageName}/
Deployment log All deployments logged to nts.trip_page_deploy_log table
Permission check Only admin role can access deploy endpoint
Confirmation dialog User must confirm before deployment
Validation Verify skeleton and snippet template selected before deploy

Future Enhancements

  1. Rollback UI - Button to restore previous version from backup
  2. Deployment history view - Table showing recent deployments with status
  3. Staging preview - Preview URL at staging.bookmyholiday.ae before production push
  4. Scheduled deployment - Deploy at a specific date/time
  5. Batch deployment - Deploy multiple pages at once
  6. Webhook notifications - Notify Slack/Teams on deployment

Workflow Summary

Step Actor Action
1 Content Creator Edits page placeholders & snippets in offerman.html
2 Content Creator Clicks Preview & Download
3 Content Creator Reviews HTML locally in browser
4 Content Creator Clicks Deploy to Production
5 System Shows confirmation dialog
6 Content Creator Confirms deployment
7 System Writes file to NFS staging directory
8 System Creates backup of previous version
9 System Logs deployment to audit table
10 Rsync/Lsyncd Syncs to all web servers (1-2 minutes)
11 System Page live on production

Files to Modify/Create

File Action Description
config/tlinqapi.properties Modify Add deployment configuration
config/db-changes/trip-page-deploy-log.sql Create Audit table migration
config/api-roles.properties Modify Add deploy endpoint permission
tqapp/.../TripOfferFacade.java Modify Add deployHtmlPage method
tqapi/.../TripOfferApi.java Modify Add /page/deploy endpoint
tqweb-adm/offerman.html Modify Add deploy button and confirmation dialog
tqweb-adm/js/modules/offerman.js Modify Add deploy functions

Dependencies

  • NFS server configured and mounted on TQPro API server
  • SSH key-based authentication from API server to web servers
  • Rsync or lsyncd installed on API server
  • Write permissions to staging directory for API process user