No description
Find a file
2026-05-05 12:46:57 +01:00
Images Apply Live_v1 update: search/filter, auto-refresh, timeout handling 2026-05-05 10:56:56 +01:00
lib Fix hasPassword bug in admin UI, add forgot-password link style 2026-05-05 11:35:02 +01:00
scripts Add full auth system: SQLite sessions, email invites, admin console 2026-05-05 11:26:40 +01:00
.env.example INITIAL_ADMINS: Paul only, correct O2E username (email format) 2026-05-05 12:32:57 +01:00
.gitignore Add full auth system: SQLite sessions, email invites, admin console 2026-05-05 11:26:40 +01:00
accept-invite.html Add full auth system: SQLite sessions, email invites, admin console 2026-05-05 11:26:40 +01:00
accept-invite.js Add full auth system: SQLite sessions, email invites, admin console 2026-05-05 11:26:40 +01:00
admin.html Add full auth system: SQLite sessions, email invites, admin console 2026-05-05 11:26:40 +01:00
admin.js Add full auth system: SQLite sessions, email invites, admin console 2026-05-05 11:26:40 +01:00
auth.js Add full auth system: SQLite sessions, email invites, admin console 2026-05-05 11:26:40 +01:00
change-password.html Add full auth system: SQLite sessions, email invites, admin console 2026-05-05 11:26:40 +01:00
change-password.js Add full auth system: SQLite sessions, email invites, admin console 2026-05-05 11:26:40 +01:00
CLAUDE.md Add full auth system: SQLite sessions, email invites, admin console 2026-05-05 11:26:40 +01:00
dashboard.html Add full auth system: SQLite sessions, email invites, admin console 2026-05-05 11:26:40 +01:00
dashboard.js Add full auth system: SQLite sessions, email invites, admin console 2026-05-05 11:26:40 +01:00
deploy.sh deploy.sh: update for new auth structure (lib/, data/, better-sqlite3, pm2 reload) 2026-05-05 12:46:57 +01:00
editor.html Add full auth system: SQLite sessions, email invites, admin console 2026-05-05 11:26:40 +01:00
forgot-password.html Add full auth system: SQLite sessions, email invites, admin console 2026-05-05 11:26:40 +01:00
forgot-password.js Add full auth system: SQLite sessions, email invites, admin console 2026-05-05 11:26:40 +01:00
login.html Add full auth system: SQLite sessions, email invites, admin console 2026-05-05 11:26:40 +01:00
package-lock.json Add full auth system: SQLite sessions, email invites, admin console 2026-05-05 11:26:40 +01:00
package.json Add full auth system: SQLite sessions, email invites, admin console 2026-05-05 11:26:40 +01:00
README.md Update README and deploy.sh: reflect new auth system 2026-05-05 11:38:42 +01:00
reset-password.html Add full auth system: SQLite sessions, email invites, admin console 2026-05-05 11:26:40 +01:00
reset-password.js Add full auth system: SQLite sessions, email invites, admin console 2026-05-05 11:26:40 +01:00
server.js Add full auth system: SQLite sessions, email invites, admin console 2026-05-05 11:26:40 +01:00
styles.css Fix hasPassword bug in admin UI, add forgot-password link style 2026-05-05 11:35:02 +01:00

3M OMG Portal

A web portal for managing One2Edit translation jobs with 3M branding. Provides real email/password authentication, per-user job filtering, admin user management, and an embedded One2Edit editor.

Production URL: https://3m.automation.oliver.solutions


Quick start (local dev)

cp .env.example .env    # fill in all values (see below)
npm install
npm start               # http://localhost:3000

Environment variables

Variable Description
SERVICE_USERNAME One2Edit service account email
SERVICE_PASSWORD One2Edit service account password
PORT HTTP port (default 3000)
APP_BASE_URL Public base URL used in email links
DATA_DIR Directory for SQLite DB (default ./data)
COOKIE_NAME Session cookie name (default portal_session)
COOKIE_SECURE Set false for local http dev, true for prod
SESSION_TTL_MS Session lifetime in ms (default 28800000 = 8h)
INITIAL_ADMINS JSON array of admins to seed on first boot (see below)
MAILGUN_API_KEY Mailgun API key
MAILGUN_DOMAIN Mailgun sending domain
MAILGUN_FROM From address for emails

INITIAL_ADMINS format

[
  {"email":"admin@example.com","one2editUsername":"firstname.lastname","password":"strongpassword"},
  {"email":"admin2@example.com","one2editUsername":"firstname2.lastname2","password":"strongpassword2"}
]

On first server start, if no admins exist, these accounts are created with must_change_password=1. Delete INITIAL_ADMINS from .env after the first login and password change.


Architecture

Browser  →  /api/auth/*        →  lib/routes/auth.js
Browser  →  /api/admin/*       →  lib/routes/admin.js
Browser  →  /api               →  lib/proxy.js  →  One2Edit API (requires auth)
Browser  →  static files       →  login.html, dashboard.html, editor.html, admin.html …

Auth: Email/password → HttpOnly cookie (portal_session) → SQLite session row
Roles: admin (full access) and user
Email: Mailgun HTTP API (no SDK) for invite and password-reset links


Pages

Page Description
login.html Sign-in form
dashboard.html Job list with progress, filters, PDF export
editor.html One2Edit embedded editor
admin.html Admin console: invite users, manage roles/status
change-password.html Forced password change on first login
forgot-password.html Self-service password reset request
reset-password.html Reset password via emailed link
accept-invite.html Accept invite and set password via emailed link

User flows

First-time admin setup:

  1. Set INITIAL_ADMINS in .env with correct One2Edit usernames, run npm start
  2. Log in → forced to /change-password.html → set new password → dashboard

Inviting a new user (admin only):

  1. Go to Admin console → "Invite User"
  2. Enter email, One2Edit username, role → "Send Invite"
  3. User receives email with magic link → sets password → auto-logged in

Forgot password:

  1. Click "Forgot password?" on login page → enter email → receive reset link
  2. Click link (valid 1h) → set new password → log in

API endpoints

Public

Method Path Description
POST /api/auth/login Login, sets session cookie
POST /api/auth/forgot-password Request password reset email
POST /api/auth/reset-password Consume reset token, set new password
POST /api/auth/accept-invite Consume invite token, set password, auto-login
GET /api/auth/invite-info Check invite token validity

Authenticated

Method Path Description
GET /api/auth/me Current user + session info
POST /api/auth/change-password Change password
POST /api/auth/logout Destroy session, remove O2E session

Admin only

Method Path Description
GET /api/admin/users List all users
POST /api/admin/users Create user + send invite email
PATCH /api/admin/users/:id Update role / active status
POST /api/admin/users/:id/resend-invite Resend invite email
POST /api/admin/users/:id/reset-password Send password reset email

Emergency admin CLI

If you're locked out:

node scripts/create-admin.js

Prompts for email, One2Edit username, and password. Creates a new admin or resets the password of an existing user directly in the database.


Deploy

# First time (on the server, as root):
git clone git@bitbucket.org:zlalani/3m-portal.git /opt/3m-portal
# Fill in /opt/3m-portal/.env
sudo bash /opt/3m-portal/deploy.sh

# After a git pull:
sudo bash /opt/3m-portal/deploy.sh --update

The deploy script installs build-essential (required by better-sqlite3), creates /opt/3m-portal/data/ with correct permissions, runs npm install --omit=dev, configures PM2, and sets up the Apache reverse proxy.


Database

SQLite at $DATA_DIR/portal.db. Tables: users, sessions, tokens, audit_log.

Check recent audit events:

sqlite3 /opt/3m-portal/data/portal.db \
  'SELECT action, actor_user_id, target_user_id, datetime(created_at/1000,"unixepoch") FROM audit_log ORDER BY id DESC LIMIT 20;'