- Move service account credentials to .env (loaded server-side only) - server.js: inject credentials in proxy, strip any client-provided creds, replace deprecated url.parse with new URL - auth.js / dashboard.js: remove all hardcoded passwords from client code - dashboard.js: remove broken category filter, fix redundant user.info call (use stored userId), add HTML escaping against XSS - login.html: remove unused password field - dashboard.html: remove broken category filter UI - Add .gitignore to exclude .env and node_modules - Add .env.example as configuration template Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|---|---|---|
| .env.example | ||
| .gitignore | ||
| api.js | ||
| auth.js | ||
| CLAUDE.md | ||
| dashboard.html | ||
| dashboard.js | ||
| editor.html | ||
| login.html | ||
| login.js | ||
| package.json | ||
| README.md | ||
| server.js | ||
| styles.css | ||
3M OMG Portal
A web-based portal for managing and editing One2Edit translation jobs with 3M branding.
Table of Contents
- Overview
- System Architecture
- Project Structure
- Setup and Installation
- How It Works
- API Documentation
- Features
- Troubleshooting
- Browser Compatibility
Overview
The 3M OMG Portal provides a streamlined interface for:
- Dual authentication system with Oliver Login and 3M Login
- External session management using One2Edit API session tokens
- Dynamic user resolution via
user.infoAPI to resolve usernames to user IDs - Viewing and managing active translation jobs (STARTED and RUNNING)
- Opening jobs in the One2Edit editor
- Exporting jobs as PDFs (blob download with save dialog)
- Tracking job progress with visual indicators
- Managing multiple translation projects
System Architecture
┌──────────────────────────────────────────────────────────────────────────┐
│ BROWSER (User Interface) │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ login.html │─────>│dashboard.html│─────>│ editor.html │ │
│ │ │ │ │ │ │ │
│ │ - Username │ │ - Job List │ │ - One2Edit │ │
│ │ - Password │ │ - Progress │ │ SDK │ │
│ │ - Oliver / │ │ - Filters │ │ - Job Editor │ │
│ │ 3M Login │ │ - PDF Export │ │ - Auto-close │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ │ auth.js │ dashboard.js │ (inline JS) │
│ │ │ │ │
└──────────┼─────────────────────┼─────────────────────┼──────────────────┘
│ │ │
│ │ │
▼ ▼ ▼
┌──────────────────────────────────────────────────────────────────────────┐
│ NODE.JS PROXY SERVER (server.js) │
│ http://localhost:3000 │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ Static File Server │ │
│ │ - Serves HTML, CSS, JS, Images │ │
│ │ - Routes '/' to login.html │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ API Proxy (/api endpoint) │ │
│ │ - Handles GET and POST requests │ │
│ │ - Forwards requests to One2Edit API │ │
│ │ - Adds CORS headers to responses │ │
│ │ - Logs requests/responses (passwords masked) │ │
│ │ - Converts 302/301 redirects to 401 errors │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────┬───────────────────────────────────────┘
│
│ HTTPS
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ ONE2EDIT API (External) │
│ https://oliver.one2edit.com/v3/Api.php │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ Available Commands: │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ * user.info → Get user ID from username │ │
│ │ * user.session.extern.add → Create external session (login) │ │
│ │ * user.session.extern.remove → Remove external session (logout) │ │
│ │ * job.list → Get all translation jobs │ │
│ │ * job.export.pdf → Export job as PDF │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────┐
│ SESSION MANAGEMENT │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ sessionStorage (Browser): │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ * isAuthenticated → 'true' if logged in │ │
│ │ * username → User's username │ │
│ │ * userId → User ID (resolved from user.info API) │ │
│ │ * externSessionId → Session ID from extern.add response │ │
│ │ * authConfig → API credentials (JSON) │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
│ Session Flow: │
│ 1. Login → user.info resolves username to userId │
│ 2. Login → user.session.extern.add returns externSessionId │
│ 3. Dashboard → user.info again to get userId (credential-based auth) │
│ 4. Dashboard → job.list with credential-based auth + userId │
│ 5. Editor → externSessionId used directly as SDK sessionId │
│ 6. Logout → credential-based auth removes extern session │
│ │
└──────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────┐
│ AUTHENTICATION METHODS │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ The portal uses TWO authentication methods for API calls: │
│ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ METHOD 1: Credential-Based (used by most API calls) │ │
│ │ ───────────────────────────────────────────────── │ │
│ │ authUsername: 'portal@oliver.agency' │ │
│ │ authPassword: 'Sp1d3r26!' │ │
│ │ │ │
│ │ Used by: user.info, user.session.extern.add, │ │
│ │ user.session.extern.remove, job.list │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ METHOD 2: Session-Based (used for PDF export and editor) │ │
│ │ ────────────────────────────────────────────────── │ │
│ │ sessionId: [externSessionId from login] │ │
│ │ │ │
│ │ Used by: job.export.pdf, One2Edit SDK │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────┐
│ AUTHENTICATION FLOW │
└──────────────────────────────────────────────────────────────────────────┘
User enters credentials
│
▼
┌──────────────────────────────────┐
│ User selects login type: │
│ - Oliver Login (black button) │
│ - 3M Login (red button) │
└────────┬─────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Step 1: API Call: user.info │
│ - Credential-based auth │
│ - Resolves username → userId │
│ - clientId: 6 │
└────────┬────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Step 2: user.session.extern.add │
│ - Credential-based auth │
│ - Uses resolved userId │
│ - clientId: 7 │
└────────┬────────────────────────────┘
│
▼
┌──────────────────────────────────┐
│ API returns externSessionId │
│ (e.g., f88637e4d22be36...) │
└────────┬─────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Store in sessionStorage: │
│ - isAuthenticated: 'true' │
│ - username (from form) │
│ - userId (from user.info) │
│ - externSessionId (from API) │
│ - authConfig (JSON) │
└────────┬────────────────────────────┘
│
▼
┌──────────────────────────────────┐
│ Redirect to dashboard.html │
└──────────────────────────────────┘
Project Structure
one2edit_Portal_v1/
├── server.js # Node.js proxy server (CORS handling, GET + POST)
├── login.html # Login page with dual authentication form
├── dashboard.html # Main dashboard showing jobs and progress
├── editor.html # One2Edit job editor page (SDK integration)
├── auth.js # Authentication logic (user.info + extern session)
├── dashboard.js # Dashboard functionality, job management, PDF export
├── login.js # Login form handler (legacy, uses api.js)
├── api.js # API helper functions (legacy One2EditAPI + SessionManager)
├── styles.css # All styling for the application
├── package.json # Node.js dependencies and scripts
├── Images/ # Image assets
│ ├── login_logo.png # 3M logo for login page and dashboard
│ └── 3M_Splash.jpg # Background splash image for login
├── Example/ # Legacy reference implementation (Bootstrap demo)
├── Example2/ # Alternative reference implementation
├── V1/ # Version 1 (earlier iteration)
├── V2/ # Version 2 (earlier iteration)
├── v3/ # Version 3 (earlier iteration)
├── v4/ # Version 4 (most recent alternative)
└── README.md # This file
File Descriptions
server.js (227 lines)
- Node.js HTTP server running on port 3000
- Serves static files (HTML, CSS, JS, images) with correct MIME types
- Proxies both GET and POST API requests to One2Edit API
- Routes
/tologin.html - Handles CORS preflight (OPTIONS) requests
- Logs all API requests and responses (with password masking)
- Converts 302/301 redirects to 401 errors
login.html (141 lines)
- Login form with username and password fields
- Two login buttons:
- Oliver Login (black button) - Uses Oliver authentication
- 3M Login (red button) - Uses 3M authentication
- Terms of Use & Privacy Policy modal
- Spinner feedback during authentication
- Links to auth.js for form handling
auth.js (163 lines)
- Two authentication configurations (both use portal@oliver.agency credentials):
- OLIVER_AUTH_CONFIG: portal@oliver.agency credentials, clientId: 7
- TMM_AUTH_CONFIG: portal@oliver.agency credentials, clientId: 7
- Login flow (two-step):
- Calls
user.infoto resolve username → userId (clientId: 6) - Calls
user.session.extern.addwith the resolved userId
- Calls
- Extracts
externSessionIdfrom API response - Stores session data in sessionStorage (isAuthenticated, username, userId, externSessionId, authConfig)
- Redirects to dashboard on success
dashboard.html (87 lines)
- Header bar with 3M logo, "Jobs" title, Refresh and Sign Out buttons
- Overall progress section with aggregated progress bar
- Category filter dropdown (All, Catalogue, Brochure, Flyer, Poster)
- Jobs grid container
- Loading, error, and empty state displays
dashboard.js (553 lines)
- Checks authentication on page load
- Calls
user.infoAPI with credential-based auth to resolve username → userId - Calls
job.listwith credential-based auth (authUsername/authPassword, NOT sessionId) - Filters for STARTED and RUNNING job statuses only
- Parses XML responses into job objects
- Creates job cards with:
- Document preview thumbnails (base64)
- Per-job progress bars (Done, Finished, Remaining)
- Edit and PDF buttons
- Lock indicators for opened jobs
- PDF export via
job.export.pdfusing GET (session-based auth, id = jobId)- Popup shown immediately on click for instant user feedback
- PDF button disabled with "Downloading..." text during request
- Fetches blob, triggers browser download, re-enables button
- Sign out via
user.session.extern.remove(credential-based auth with externSessionId parameter)
editor.html (126 lines)
- Loads One2Edit SDK from CDN (CSS + JS)
- Gets jobId from URL parameters
- Uses externSessionId directly as SDK sessionId (no separate user.auth call)
- Initializes One2Edit editor with
one2edit.create() - Removes toolbar buttons: search/replace, download PDF, rotate
- Hides main menu bar
- Returns to dashboard on editor close
- Debug mode enabled
login.js (62 lines)
- Legacy login form handler (uses
api.jsOne2EditAPI and SessionManager) - Checks if user already authenticated, redirects to dashboard
- Form submit handler with loading state
api.js (207 lines)
- Legacy API helper module with:
One2EditAPI.authenticate()- Login via extern sessionOne2EditAPI.getEditorSession()- Get editor sessionOne2EditAPI.getJobList()- Fetch jobsOne2EditAPI.exportJobPDF()- Export PDFSessionManager- Save/get/clear session data
- Not actively used by the current login flow (auth.js handles login directly)
Setup and Installation
Prerequisites
- Node.js (v12 or higher)
- Modern web browser (Chrome, Firefox, Safari, Edge)
- Internet connection (to access One2Edit API)
Installation Steps
-
Navigate to the project directory:
cd one2edit_Portal_v1 -
Install dependencies:
npm install -
Start the proxy server:
npm startOr directly:
node server.jsYou should see:
🚀 One2Edit Portal Server Running! 📱 Open your browser to: http://localhost:3000 ✅ API proxy is handling CORS at: http://localhost:3000/api Press Ctrl+C to stop the server -
Open your browser and navigate to:
http://localhost:3000IMPORTANT: Do NOT open the HTML files directly (file:// protocol). Always access through http://localhost:3000 to ensure the proxy server handles API calls.
Configuration
Authentication credentials are configured in auth.js with two separate configs:
const OLIVER_AUTH_CONFIG = {
apiBaseUrl: '/api',
authDomain: 'local',
authUsername: 'portal@oliver.agency',
authPassword: 'Sp1d3r26!',
clientId: '7',
userId: '9'
};
const TMM_AUTH_CONFIG = {
apiBaseUrl: '/api',
authDomain: 'local',
authUsername: 'portal@oliver.agency',
authPassword: 'Sp1d3r26!',
clientId: '7',
userId: '9'
};
- Oliver Login button uses OLIVER_AUTH_CONFIG
- 3M Login button uses TMM_AUTH_CONFIG
- Both configs currently use the same portal@oliver.agency credentials
- The
userId: '9'in configs is overridden at runtime by the value resolved fromuser.info
To change credentials, edit auth.js:1-18.
How It Works
Complete User Flow
┌─────────────────────────────────────────────────────────────────┐
│ 1. USER OPENS BROWSER │
│ http://localhost:3000 │
└────────────────────┬────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 2. LOGIN PAGE (login.html + auth.js) │
│ - User enters username and password │
│ - Clicks "Oliver Login" OR "3M Login" │
└────────────────────┬────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 3. USER RESOLUTION (auth.js) │
│ - Call: user.info (credential-based, clientId: 6) │
│ - Resolve entered username → numeric userId │
│ - Fail with error if user not found │
└────────────────────┬────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 4. SESSION CREATION (auth.js) │
│ - Call: user.session.extern.add (credential-based) │
│ - Pass resolved userId from step 3 │
│ - Extract externSessionId from XML response │
│ - Store session in sessionStorage │
│ - Redirect to dashboard.html │
└────────────────────┬────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 5. DASHBOARD (dashboard.html + dashboard.js) │
│ - Check authentication │
│ - Call: user.info (credential-based, resolve userId again) │
│ - Call: job.list (credential-based + userId) │
│ - Parse XML response │
│ - Display job cards with progress │
│ - Show overall progress bar │
└────────────────────┬────────────────────────────────────────────┘
│
├────────────────────────────────────┐
│ │
▼ ▼
┌──────────────────────────────────┐ ┌──────────────────────────────────┐
│ 6A. EDIT JOB (editor.html) │ │ 6B. EXPORT PDF (dashboard.js) │
│ - Get jobId from URL │ │ - Show popup immediately │
│ - Use externSessionId as │ │ - Disable PDF btn + show │
│ SDK sessionId directly │ │ "Downloading..." text │
│ - Load One2Edit editor │ │ - GET: job.export.pdf │
│ - Remove toolbar buttons │ │ - Session-based auth │
│ - On close → Return to │ │ - id = jobId (from job.list) │
│ dashboard │ │ - Fetch blob → download │
└───────────────────────────────────┘ │ - Re-enable PDF button │
└──────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 7. SIGN OUT (dashboard.js) │
│ - Call: user.session.extern.remove (credential-based auth) │
│ - Pass externSessionId as parameter │
│ - Clear sessionStorage │
│ - Redirect to login.html │
└─────────────────────────────────────────────────────────────────┘
Data Flow Details
1. Login Process
User Input (username/password)
│
▼
User Clicks "Oliver Login" or "3M Login"
│
▼
auth.js selects OLIVER_AUTH_CONFIG or TMM_AUTH_CONFIG
│
▼
Step 1: user.info (credential-based, clientId: 6)
│
▼
API Returns userId (e.g., 9)
│
▼
Step 2: user.session.extern.add (credential-based, clientId: 7)
│
▼
API Returns externSessionId (e.g., f88637e4d22be361...)
│
▼
sessionStorage Populated:
- isAuthenticated: 'true'
- username: [from form]
- userId: [from user.info]
- externSessionId: [from API]
- authConfig: [selected config as JSON]
│
▼
Dashboard Redirect
2. Dashboard Loading
Page Load → Check sessionStorage
↓
isAuthenticated = 'true'?
│
├─ No → Redirect to Login
│
└─ Yes → Get username + authConfig from sessionStorage
│
▼
user.info API (credential-based, clientId: 6)
│
▼
Extract userId from XML response
│
▼
job.list API (credential-based, clientId: 7)
┌─ authUsername: portal@oliver.agency
├─ authPassword: Sp1d3r26!
├─ userId: [from user.info]
├─ status[0]: STARTED
└─ status[1]: RUNNING
│
▼
Parse XML Response → Create Job Cards
│
▼
Update Overall Progress Bar
3. Job Editing
Click Edit → Navigate to editor.html?jobId=XXX
│
▼
Get externSessionId from sessionStorage
│
▼
one2edit.create({
sessionId: externSessionId,
clientId: authConfig.clientId,
jobId: jobId
})
│
▼
INITIALIZE event → Hide loading, hide menu
│
▼
EDITOR_INITIALIZE → Remove toolbar buttons
│
▼
User Makes Edits
│
▼
EDITOR_CLOSE → Return to Dashboard
4. PDF Export
Click PDF Button
│
├─► Disable PDF button → Show "Downloading..."
├─► Show "PDF download requested" popup immediately
│
▼
Find job in allJobs array → Get jobId
│
▼
Build GET params:
┌─ command: job.export.pdf
├─ authDomain: local
├─ sessionId: externSessionId
├─ clientId: 7
├─ id: jobId (from job.list)
└─ result: file
│
▼
GET /api?params → Receive blob response
│
▼
Create blob URL → Trigger download
(filename: title_jobId.pdf)
│
▼
Re-enable PDF button → Restore "PDF" text
API Documentation
All API calls are made to the One2Edit API v3:
https://oliver.one2edit.com/v3/Api.php
Proxied through:
http://localhost:3000/api
Authentication Methods
The portal uses two different authentication methods depending on the API call:
Method 1: Credential-Based Authentication (most API calls)
authDomain: 'local'
authUsername: 'portal@oliver.agency'
authPassword: 'Sp1d3r26!'
Used by: user.info, user.session.extern.add, user.session.extern.remove, job.list
Method 2: Session-Based Authentication (PDF export and SDK)
authDomain: 'local'
sessionId: '[externSessionId from login response]'
Used by: job.export.pdf, One2Edit SDK editor
1. user.info (Resolve Username to User ID)
Purpose: Resolve a username to a numeric userId
When Called:
- During login (auth.js) before creating extern session
- On dashboard load (dashboard.js) before loading jobs
Location: auth.js:42-50, dashboard.js:237-245
Parameters:
{
command: 'user.info',
authDomain: 'local',
authUsername: 'portal@oliver.agency',
authPassword: 'Sp1d3r26!',
clientId: '6',
username: '[entered username]',
domain: 'local'
}
Authentication: Credential-based (authUsername/authPassword)
Response XML:
<user>
<id>9</id>
<name>Paul Johns</name>
...
</user>
Usage:
- Extracts
<id>from the response - Used as
userIdparameter inuser.session.extern.addandjob.list
2. user.session.extern.add (Login)
Purpose: Create an external session for portal authentication
When Called: User clicks "Oliver Login" or "3M Login" button (after user.info succeeds)
Location: auth.js:83-91
Parameters:
{
command: 'user.session.extern.add',
authDomain: 'local',
authUsername: 'portal@oliver.agency',
authPassword: 'Sp1d3r26!',
clientId: '7',
username: '[user input]',
userId: '[resolved from user.info]'
}
Authentication: Credential-based (authUsername/authPassword)
Response XML:
<success>
<session>
<externSessionId>f88637e4d22be361821633a14125a2aa</externSessionId>
<user>
<id>9</id>
<name>Paul Johns</name>
<contact>pauljohns@oliver.agency</contact>
<role>admin</role>
</user>
</session>
</success>
Success Action:
- Extracts externSessionId from XML response
- Stores session data in sessionStorage
- Redirects to dashboard.html
Important: The externSessionId is returned by the API, not generated by the client.
3. job.list (Get Jobs)
Purpose: Retrieve active translation jobs for the user
When Called:
- Dashboard page loads (after user.info)
- User clicks "Refresh" button
Location: dashboard.js:278-294
Parameters:
{
command: 'job.list',
authDomain: 'local',
authUsername: 'portal@oliver.agency',
authPassword: 'Sp1d3r26!',
clientId: '7',
userId: '[resolved from user.info]',
includeDocumentInfos: '1',
includeDocumentPreviews: '1',
includeDocumentWorkflows: '1',
includeDocumentMetadata: '1',
'status[0]': 'STARTED',
'status[1]': 'RUNNING'
}
Authentication: Credential-based (authUsername/authPassword), NOT session-based
Response XML Structure:
<response>
<job>
<id>325</id>
<name>Job Name</name>
<status>STARTED|RUNNING</status>
<isOpened>false</isOpened>
<totalItems>789</totalItems>
<allItems>789</allItems>
<doneItems>200</doneItems>
<finishItems>100</finishItems>
<lastedit>2026-01-15 15:58:03</lastedit>
<lastuser>
<name>User Name</name>
</lastuser>
<document>
<id>200</id>
<version>Version Name</version>
<name>Document Name</name>
<pages>24</pages>
<preview>[base64 encoded JPEG]</preview>
</document>
<translationLanguage>
<name>Spanish</name>
</translationLanguage>
</job>
<!-- More job elements... -->
</response>
Parsed Job Object:
{
id: '325',
documentId: '200',
title: 'Version Name',
documentName: 'Version Name',
language: 'Spanish',
pages: '24',
status: 'STARTED',
isOpened: false,
totalItems: 789,
doneItems: 200,
finishItems: 100,
lastUserName: 'User Name',
lastEdit: '2026-01-15 15:58:03',
thumbnail: 'data:image/jpeg;base64,...'
}
4. job.export.pdf (Export PDF)
Purpose: Export a job as PDF
When Called: User clicks "PDF" button on a job card
Location: dashboard.js:38-53
Method: GET
Parameters:
{
command: 'job.export.pdf',
authDomain: 'local',
sessionId: '[externSessionId from login]',
clientId: '7',
id: '[jobId from job.list]',
result: 'file'
}
Authentication: Session-based (sessionId = externSessionId)
UX Behaviour:
- "PDF download requested" popup shown immediately on click
- PDF button disabled and text changes to "Downloading..." during request
- GET request sent to
/apiwith query parameters - PDF received as blob response
- Browser download triggered via temporary link element
- Filename format:
{title}_{jobId}.pdf - PDF button re-enabled and text restored to "PDF"
- On error: button re-enabled, alert shown
5. One2Edit SDK (Open Editor)
Purpose: Load and configure the One2Edit editor
When Called: User clicks "Edit" button on a job card
Location: editor.html:80-89
Configuration:
one2edit.create({
flashvars: {
server: 'https://oliver.one2edit.com/',
sessionId: externSessionId, // Uses externSessionId directly
clientId: parseInt(authConfig.clientId),
jobEditor: {
jobId: parseInt(jobId)
}
}
});
Important: The editor uses the externSessionId directly as the SDK sessionId. No separate user.auth call is needed.
Events:
INITIALIZE: Hides loading message, hides menu bar viaone2edit.editor.showMenu(false)EDITOR_INITIALIZE: Removes toolbar buttons (search/replace, download PDF, rotate)EDITOR_CLOSE: Returns to dashboard viawindow.location.hrefEDITOR_ERROR: Displays error message
Customizations:
- Removes toolbar buttons:
toolbar_tool_search_replace,toolbar_download_pdf,toolbar_rotate - Hides main menu bar
- Debug mode enabled (
one2edit.debug = true)
6. user.session.extern.remove (Logout)
Purpose: Remove external session on sign out
When Called: User clicks "Sign Out" button
Location: dashboard.js:170-177
Parameters:
{
command: 'user.session.extern.remove',
authDomain: 'local',
authUsername: 'portal@oliver.agency',
authPassword: 'Sp1d3r26!',
clientId: '7',
externSessionId: '[externSessionId from sessionStorage]'
}
Authentication: Credential-based (authUsername/authPassword)
Note: The parameter name is externSessionId here (not sessionId), unlike job.export.pdf which uses sessionId.
Action:
- Attempts to remove session via API
- Clears sessionStorage (regardless of API response)
- Redirects to login.html
- Sign out always succeeds locally even if API fails
Session Management Details
Understanding externSessionId
IMPORTANT: This is a critical concept for understanding how authentication works in the portal.
┌──────────────────────────────────────────────────────────────────┐
│ 1. API Response Field: externSessionId │
│ (Returned by user.session.extern.add) │
└────────────────────┬─────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ 2. sessionStorage Key: externSessionId │
│ (How we store it in the browser) │
└────────────────────┬─────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ 3. Used differently depending on the API call: │
│ │
│ job.export.pdf → sessionId: [externSessionId] │
│ One2Edit SDK → sessionId: [externSessionId] │
│ extern.remove → externSessionId: [externSessionId] │
│ job.list → NOT USED (credential-based auth) │
│ user.info → NOT USED (credential-based auth) │
└──────────────────────────────────────────────────────────────────┘
Key Points:
- Most API calls use credential-based auth (authUsername/authPassword) and don't need the externSessionId
- The PDF export (
job.export.pdf) and SDK editor use the externSessionId assessionId - The logout call passes it as
externSessionId(its original name)
Features
Dashboard Features
Overall Progress Bar
- Aggregates progress from all jobs
- Shows Done, Finished, and Remaining items
- Displays percentage completion and element counts
Job Cards
- Document preview thumbnail (base64 from API)
- Document version/name
- Document ID
- Translation language
- Page count
- Last user who edited
- Last edit timestamp
- Per-job progress bar with legend
- Edit and PDF buttons
Lock Status
- Jobs opened by other users show lock icon
- Edit and PDF buttons disabled for locked jobs
- Visual indication with disabled styling
Category Filter
- Filter jobs by category (All, Catalogue, Brochure, Flyer, Poster)
Refresh Button
- Reload jobs without page refresh
- Maintains session
Sign Out Button
- End session securely
- Clear all session data
- Return to login
Progress Bar Colors
- Light Green (#81c784): Done items (translated but not approved)
- Dark Green (#2e7d32): Finished items (approved/completed)
- Yellow (#ffd54f): Remaining items (not yet translated)
PDF Export
- Instant feedback: "PDF download requested" popup shown immediately on button click
- Button state management: PDF button disabled and shows "Downloading..." during request
- GET-based download: Fetches PDF as blob via
job.export.pdfwith session-based auth - Job-based export: Uses
id: jobIdfromjob.list(not document ID) - Browser save dialog: Triggers download with filename
{title}_{jobId}.pdf - Auto re-enable: PDF button restored after download completes or on error
Security Features
- Credential-based API authentication using portal service account
- Dual authentication system (Oliver and 3M login options)
- External session IDs generated by API (format: 32-character hex string)
- Dynamic user resolution via user.info at login and dashboard load
- Automatic session cleanup on sign out
- Session validation on page load
- Passwords masked in server logs
Troubleshooting
Common Issues
1. CORS Errors
Symptom:
Access to fetch at 'file:///api...' has been blocked by CORS policy
Cause: Opening HTML files directly (file:// protocol)
Solution:
- Start the server:
node server.js - Access via:
http://localhost:3000 - Never open HTML files directly from file system
2. Login Fails
Symptoms:
- Error message on login page
- "User not found" error
- Redirect or authentication failed error
Debugging Steps:
- Check server.js is running:
lsof -ti:3000 - Check server.js console logs for API response
- Verify credentials in auth.js (lines 2-18)
- Check that user.info call succeeds (returns a valid user)
- Ensure API returns 200 status (not 302 redirect)
- Check network tab in browser DevTools
Common Causes:
- Server not running
- Incorrect credentials
- Username not found in One2Edit system
- API returns redirect (converted to 401 by proxy)
3. Jobs Not Loading
Symptoms:
- Empty dashboard
- Error message: "Failed to load jobs"
- Loading message never disappears
Debugging Steps:
- Open browser console (F12)
- Check for JavaScript errors
- Check user.info API call succeeds (returns a valid userId)
- Check server.js logs for job.list response
- Verify session data in sessionStorage (DevTools → Application → Session Storage)
Common Causes:
- Session expired
- user.info call failed (incorrect username or credentials)
- userId not found for the given username
- API error
- No jobs available for user
4. Editor Not Opening
Symptoms:
- "Loading editor..." message never disappears
- Error loading editor message
- Blank page
Debugging Steps:
- Check browser console for One2Edit SDK errors
- Verify externSessionId exists in sessionStorage
- Verify jobId is passed in URL
- Check that the One2Edit SDK scripts loaded from CDN
- Check server.js logs for any proxy errors
Common Causes:
- Session expired (externSessionId invalid)
- Invalid jobId
- One2Edit SDK failed to load from CDN
- Network connectivity issues
5. PDF Export Fails
Symptoms:
- PDF button shows "Downloading..." but download never starts
- Error in console: "Failed to export PDF"
- Alert message after clicking PDF
Debugging Steps:
- Open browser console and check for errors
- Verify the job ID is correct (check allJobs array in console)
- Check server.js logs for the GET request/response
- Ensure externSessionId is still valid
- Check network tab for the GET request to
/api?command=job.export.pdf...
Common Causes:
- externSessionId expired
- Invalid job ID
- API error (check server terminal for response body)
- Network timeout for large documents
6. Server Won't Start
Symptoms:
Error: listen EADDRINUSE :::3000
Cause: Port 3000 already in use
Solution:
# Find process using port 3000
lsof -ti:3000
# Kill the process
kill -9 $(lsof -ti:3000)
# Or change port in server.js
const PORT = 3001; // Use different port
Debug Mode
To enable detailed logging:
-
Server Logs: Already enabled in server.js
- All API requests logged (passwords masked)
- Response status, length, and body (first 500 chars) logged
-
Browser Console:
- Open DevTools (F12)
- Check Console tab for errors
- Check Network tab for API calls
-
One2Edit SDK Debug:
- Already enabled in editor.html
one2edit.debug = true;
Browser Compatibility
Tested and Working:
- Chrome 90+
- Firefox 88+
- Safari 14+
- Edge 90+
Requirements:
- JavaScript enabled
- Modern ES6 support (async/await, URLSearchParams, fetch)
- sessionStorage enabled
- Blob API support (for PDF downloads)
API Version
This portal uses One2Edit API v3:
https://oliver.one2edit.com/v3/Api.php
Note: v4 API was tested but caused redirect issues (302 responses). v3 is stable and working correctly with this portal.
Support and Maintenance
Logs Location
- Server Logs: Console output where
node server.jsis running - Browser Logs: Browser DevTools Console (F12)
- API Responses: Logged in server console (first 500 chars)
Session Data
View session data in browser:
- Open DevTools (F12)
- Go to Application tab
- Select Session Storage → http://localhost:3000
- View stored keys:
- isAuthenticated
- username
- userId
- externSessionId
- authConfig
Stopping the Server
Press Ctrl+C in the terminal where server.js is running
Summary
The 3M OMG Portal is a three-tier web application:
- Frontend (HTML/CSS/JS) - User interface with dual login system
- Proxy Server (Node.js) - CORS handling and API forwarding (GET + POST)
- Backend (One2Edit API v3) - Data and authentication
Key Points:
- Always access via
http://localhost:3000 - Server must be running for API calls to work
- Dual login system: Oliver Login (black) or 3M Login (red)
- Two-step login: user.info (resolve userId) → user.session.extern.add (create session)
- Credential-based auth for most API calls (portal@oliver.agency)
- Session-based auth for PDF export and SDK editor (externSessionId as sessionId)
- Session data stored in browser's sessionStorage
- Job filtering: Only STARTED and RUNNING jobs displayed
- PDF export:
job.export.pdfvia GET, instant popup feedback, button disabled during download - Editor: Uses externSessionId directly (no separate user.auth call)
- CORS issues handled by proxy server
Workflow: Select Login Type → Enter Credentials → View Active Jobs → Edit/Export → Sign Out
Authentication Summary:
┌────────────────────────────────────────────────────────────────┐
│ API Call │ Auth Method │
├──────────────────────────────────┼──────────────────────────────┤
│ user.info │ Credential (clientId: 6) │
│ user.session.extern.add │ Credential (clientId: 7) │
│ user.session.extern.remove │ Credential (clientId: 7) │
│ job.list │ Credential (clientId: 7) │
│ job.export.pdf │ Session (externSessionId) │
│ One2Edit SDK │ Session (externSessionId) │
└────────────────────────────────────────────────────────────────┘