Compare commits
4 commits
main
...
feature/co
| Author | SHA1 | Date | |
|---|---|---|---|
| f311c92dbb | |||
| 9ba94f9f4b | |||
| 2c21269a16 | |||
| 604a8667cf |
12 changed files with 440 additions and 110 deletions
16
README.md
16
README.md
|
|
@ -11,7 +11,7 @@ DTI is a Rails application with a React-based outfit editor, backed by MySQL dat
|
|||
### Core Components
|
||||
|
||||
- **Rails backend** (Ruby 3.4, Rails 8.0): Serves web pages, API endpoints, and manages data
|
||||
- **MySQL databases**: Primary database (`openneo_impress`) + legacy auth database (`openneo_id`)
|
||||
- **MySQL database**: Single database (`openneo_impress`) containing all application and authentication data
|
||||
- **React outfit editor**: Embedded in `app/javascript/wardrobe-2020/`, provides the main customization UI
|
||||
- **Modeling system**: Crowdsources pet/item appearance data by fetching from Neopets APIs when users load their pets
|
||||
|
||||
|
|
@ -98,7 +98,7 @@ app/
|
|||
```
|
||||
config/
|
||||
├── routes.rb # All Rails routes
|
||||
├── database.yml # Multi-database setup (main + openneo_id)
|
||||
├── database.yml # Database configuration
|
||||
└── environments/
|
||||
└── *.rb # Env-specific config (incl. impress_2020_origin)
|
||||
```
|
||||
|
|
@ -117,7 +117,7 @@ config/
|
|||
|
||||
- **Backend**: Ruby on Rails (Ruby 3.4, Rails 8.0)
|
||||
- **Frontend**: Mix of Rails views (Turbo/HAML) and React (for outfit editor)
|
||||
- **Database**: MySQL (two databases: `openneo_impress`, `openneo_id`)
|
||||
- **Database**: MySQL (`openneo_impress`)
|
||||
- **Styling**: CSS, Sass (moving toward modern Rails conventions)
|
||||
- **External Integrations**:
|
||||
- **Neopets.com**: Legacy Flash/AMF protocol for pet appearance data (modeling)
|
||||
|
|
@ -129,11 +129,15 @@ config/
|
|||
|
||||
## Development Notes
|
||||
|
||||
### OpenNeo ID Database
|
||||
### Authentication Architecture
|
||||
|
||||
The `openneo_id` database is a legacy from when authentication was a separate service ("OpenNeo ID") meant to unify auth across multiple OpenNeo projects. DTI was the only project that succeeded, so the apps were merged—but the database split remains for now.
|
||||
Authentication data lives in the `auth_users` table (managed by the `AuthUser` model). This was historically in a separate `openneo_id` database (a legacy from when "OpenNeo ID" was envisioned as a service to unify auth across multiple OpenNeo projects). As of November 2025, the databases have been consolidated into a single database for simplicity.
|
||||
|
||||
**Implication**: Rails is configured for multi-database mode. User auth models live in `auth_user.rb` and connect to `openneo_id`.
|
||||
User accounts are split across two related tables:
|
||||
- `auth_users` - Authentication data (passwords, email, OAuth connections) via Devise
|
||||
- `users` - Application data (points, closet settings, etc.)
|
||||
|
||||
These are linked via `User.remote_id` → `AuthUser.id`. See `db/openneo_id_migrate/README.md` for the historical context.
|
||||
|
||||
### Rails/React Hybrid
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
class AuthRecord < ApplicationRecord
|
||||
self.abstract_class = true
|
||||
|
||||
connects_to database: {reading: :openneo_id, writing: :openneo_id}
|
||||
end
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
class AuthUser < AuthRecord
|
||||
self.table_name = 'users'
|
||||
class AuthUser < ApplicationRecord
|
||||
# TODO: Consider merging with User model to eliminate the remote_id relationship
|
||||
# and simplify the authentication architecture. This would involve combining the
|
||||
# auth_users and users tables into a single table.
|
||||
|
||||
devise :database_authenticatable, :encryptable, :registerable, :validatable,
|
||||
:rememberable, :trackable, :recoverable, :omniauthable,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@ class User < ApplicationRecord
|
|||
|
||||
PreviewTopContributorsCount = 3
|
||||
|
||||
# TODO: This relationship could be simplified by merging the auth_users and users
|
||||
# tables. Currently User.remote_id points to AuthUser.id, but if the tables were
|
||||
# merged, we could eliminate remote_id and auth_server_id entirely.
|
||||
belongs_to :auth_user, foreign_key: :remote_id, inverse_of: :user
|
||||
delegate :disconnect_neopass, :uses_neopass?, to: :auth_user
|
||||
|
||||
|
|
|
|||
|
|
@ -1,57 +1,28 @@
|
|||
development:
|
||||
primary:
|
||||
adapter: mysql2
|
||||
host: <%= ENV.fetch("DB_HOST", "localhost") %>
|
||||
database: openneo_impress
|
||||
username: root
|
||||
pool: 5
|
||||
encoding: utf8mb4
|
||||
collation: utf8mb4_unicode_520_ci
|
||||
variables:
|
||||
sql_mode: TRADITIONAL
|
||||
|
||||
openneo_id:
|
||||
adapter: mysql2
|
||||
host: <%= ENV.fetch("DB_HOST", "localhost") %>
|
||||
database: openneo_id
|
||||
username: root
|
||||
pool: 2
|
||||
variables:
|
||||
sql_mode: TRADITIONAL
|
||||
migrations_paths: db/openneo_id_migrate
|
||||
adapter: mysql2
|
||||
host: <%= ENV.fetch("DB_HOST", "localhost") %>
|
||||
database: openneo_impress
|
||||
username: root
|
||||
pool: 5
|
||||
encoding: utf8mb4
|
||||
collation: utf8mb4_unicode_520_ci
|
||||
variables:
|
||||
sql_mode: TRADITIONAL
|
||||
|
||||
test:
|
||||
primary:
|
||||
adapter: mysql2
|
||||
host: <%= ENV.fetch("DB_HOST", "localhost") %>
|
||||
database: openneo_impress_test
|
||||
username: root
|
||||
pool: 5
|
||||
encoding: utf8mb4
|
||||
collation: utf8mb4_unicode_520_ci
|
||||
variables:
|
||||
sql_mode: TRADITIONAL
|
||||
|
||||
openneo_id:
|
||||
adapter: mysql2
|
||||
host: <%= ENV.fetch("DB_HOST", "localhost") %>
|
||||
database: openneo_id_test
|
||||
username: root
|
||||
pool: 2
|
||||
variables:
|
||||
sql_mode: TRADITIONAL
|
||||
migrations_paths: db/openneo_id_migrate
|
||||
adapter: mysql2
|
||||
host: <%= ENV.fetch("DB_HOST", "localhost") %>
|
||||
database: openneo_impress_test
|
||||
username: root
|
||||
pool: 5
|
||||
encoding: utf8mb4
|
||||
collation: utf8mb4_unicode_520_ci
|
||||
variables:
|
||||
sql_mode: TRADITIONAL
|
||||
|
||||
production:
|
||||
primary:
|
||||
url: <%= ENV['DATABASE_URL_PRIMARY'] %>
|
||||
encoding: utf8mb4
|
||||
collation: utf8mb4_unicode_520_ci
|
||||
variables:
|
||||
sql_mode: TRADITIONAL
|
||||
|
||||
openneo_id:
|
||||
url: <%= ENV['DATABASE_URL_OPENNEO_ID'] %>
|
||||
variables:
|
||||
sql_mode: TRADITIONAL
|
||||
migrations_paths: db/openneo_id_migrate
|
||||
url: <%= ENV['DATABASE_URL_PRIMARY'] %>
|
||||
encoding: utf8mb4
|
||||
collation: utf8mb4_unicode_520_ci
|
||||
variables:
|
||||
sql_mode: TRADITIONAL
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
class IncreaseUsernameLength < ActiveRecord::Migration[7.1]
|
||||
def change
|
||||
# NOTE: This is paired with a migration to the `openneo_id` database, too!
|
||||
# NOTE: This was originally paired with a migration to the legacy `openneo_id`
|
||||
# database (see db/openneo_id_migrate/20240401124406_increase_username_length.rb).
|
||||
# As of November 2025, the databases have been consolidated.
|
||||
reversible do |direction|
|
||||
direction.up {
|
||||
change_column :users, :name, :string, limit: 30, null: false
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
class CopyAuthUsersTableToMainDatabase < ActiveRecord::Migration[8.0]
|
||||
def up
|
||||
# Create auth_users table in openneo_impress with same structure as openneo_id.users
|
||||
# This preserves all IDs, data, and constraints from the source table.
|
||||
create_table "auth_users", id: { type: :integer, unsigned: true }, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
|
||||
t.string "name", limit: 30, null: false
|
||||
t.string "encrypted_password", limit: 64
|
||||
t.string "email", limit: 50
|
||||
t.string "password_salt", limit: 32
|
||||
t.string "reset_password_token"
|
||||
t.integer "sign_in_count", default: 0
|
||||
t.datetime "current_sign_in_at", precision: nil
|
||||
t.datetime "last_sign_in_at", precision: nil
|
||||
t.string "current_sign_in_ip"
|
||||
t.string "last_sign_in_ip"
|
||||
t.integer "failed_attempts", default: 0
|
||||
t.string "unlock_token"
|
||||
t.datetime "locked_at", precision: nil
|
||||
t.datetime "created_at", precision: nil
|
||||
t.datetime "updated_at", precision: nil
|
||||
t.datetime "reset_password_sent_at", precision: nil
|
||||
t.datetime "remember_created_at"
|
||||
t.string "provider"
|
||||
t.string "uid"
|
||||
t.string "neopass_email"
|
||||
end
|
||||
|
||||
# Add indexes (matching openneo_id.users schema)
|
||||
add_index "auth_users", ["email"], name: "index_auth_users_on_email", unique: true
|
||||
add_index "auth_users", ["provider", "uid"], name: "index_auth_users_on_provider_and_uid", unique: true
|
||||
add_index "auth_users", ["reset_password_token"], name: "index_auth_users_on_reset_password_token", unique: true
|
||||
add_index "auth_users", ["unlock_token"], name: "index_auth_users_on_unlock_token", unique: true
|
||||
|
||||
# Copy all data from openneo_id.users to openneo_impress.auth_users
|
||||
# This preserves all IDs so that User.remote_id continues to reference the correct AuthUser
|
||||
execute <<-SQL
|
||||
INSERT INTO openneo_impress.auth_users
|
||||
SELECT * FROM openneo_id.users
|
||||
SQL
|
||||
end
|
||||
|
||||
def down
|
||||
drop_table "auth_users"
|
||||
end
|
||||
end
|
||||
35
db/openneo_id_migrate/README.md
Normal file
35
db/openneo_id_migrate/README.md
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Legacy openneo_id Database Migrations
|
||||
|
||||
These migrations are kept for historical reference only. They were applied to the separate `openneo_id` database before it was consolidated into the main `openneo_impress` database in November 2025.
|
||||
|
||||
## What happened?
|
||||
|
||||
Originally, Dress to Impress used two separate MySQL databases:
|
||||
- `openneo_impress` - Main application data (items, outfits, closets, etc.)
|
||||
- `openneo_id` - Authentication data (user accounts, passwords, OAuth)
|
||||
|
||||
This split was a legacy from when "OpenNeo ID" was envisioned as a separate authentication service that would unify login across multiple OpenNeo projects. Since DTI was the only successful project, we consolidated the databases.
|
||||
|
||||
## Migration details
|
||||
|
||||
On **November 2, 2025**, the `openneo_id.users` table was copied to `openneo_impress.auth_users`, preserving all data and IDs. The `openneo_id` database was then removed from production.
|
||||
|
||||
See the main migrations directory for:
|
||||
- `20251102064247_copy_auth_users_table_to_main_database.rb` - The migration that copied the data
|
||||
|
||||
## Can these migrations be run?
|
||||
|
||||
**No.** These migrations reference the `openneo_id` database which no longer exists. They are preserved purely as documentation of how the authentication schema evolved over time.
|
||||
|
||||
## Migration history
|
||||
|
||||
1. `20230807005748_add_remember_created_at_to_users.rb` - Added Devise rememberable feature
|
||||
2. `20240313200849_add_omniauth_fields_to_users.rb` - Added NeoPass OAuth support
|
||||
3. `20240315020053_allow_null_email_and_password_for_users.rb` - Made email/password optional for OAuth users
|
||||
4. `20240401124406_increase_username_length.rb` - Increased username limit from 20 to 30 chars
|
||||
5. `20240407135246_add_neo_pass_email_to_users.rb` - Added neopass_email field
|
||||
6. `20240408120359_add_unique_index_for_omniauth_to_users.rb` - Added unique constraint for provider+uid
|
||||
|
||||
---
|
||||
|
||||
For current authentication schema, see `db/schema.rb` and look for the `auth_users` table.
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
# This file is auto-generated from the current state of the database. Instead
|
||||
# of editing this file, please use the migrations feature of Active Record to
|
||||
# incrementally modify your database, and then regenerate this schema definition.
|
||||
#
|
||||
# This file is the source Rails uses to define your schema when running `bin/rails
|
||||
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
|
||||
# be faster and is potentially less error prone than running all of your
|
||||
# migrations from scratch. Old migrations may fail to apply correctly if those
|
||||
# migrations use external dependencies or application code.
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[8.0].define(version: 2024_04_08_120359) do
|
||||
create_table "users", id: { type: :integer, unsigned: true }, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
|
||||
t.string "name", limit: 30, null: false
|
||||
t.string "encrypted_password", limit: 64
|
||||
t.string "email", limit: 50
|
||||
t.string "password_salt", limit: 32
|
||||
t.string "reset_password_token"
|
||||
t.integer "sign_in_count", default: 0
|
||||
t.datetime "current_sign_in_at", precision: nil
|
||||
t.datetime "last_sign_in_at", precision: nil
|
||||
t.string "current_sign_in_ip"
|
||||
t.string "last_sign_in_ip"
|
||||
t.integer "failed_attempts", default: 0
|
||||
t.string "unlock_token"
|
||||
t.datetime "locked_at", precision: nil
|
||||
t.datetime "created_at", precision: nil
|
||||
t.datetime "updated_at", precision: nil
|
||||
t.datetime "reset_password_sent_at", precision: nil
|
||||
t.datetime "remember_created_at"
|
||||
t.string "provider"
|
||||
t.string "uid"
|
||||
t.string "neopass_email"
|
||||
t.index ["email"], name: "index_users_on_email", unique: true
|
||||
t.index ["provider", "uid"], name: "index_users_on_provider_and_uid", unique: true
|
||||
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
|
||||
t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true
|
||||
end
|
||||
end
|
||||
29
db/schema.rb
29
db/schema.rb
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[8.0].define(version: 2025_02_16_041650) do
|
||||
ActiveRecord::Schema[8.0].define(version: 2025_11_02_064247) do
|
||||
create_table "alt_styles", charset: "utf8mb4", collation: "utf8mb4_unicode_520_ci", force: :cascade do |t|
|
||||
t.integer "species_id", null: false
|
||||
t.integer "color_id", null: false
|
||||
|
|
@ -32,6 +32,33 @@ ActiveRecord::Schema[8.0].define(version: 2025_02_16_041650) do
|
|||
t.string "secret", limit: 64, null: false
|
||||
end
|
||||
|
||||
create_table "auth_users", id: { type: :integer, unsigned: true }, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
|
||||
t.string "name", limit: 30, null: false
|
||||
t.string "encrypted_password", limit: 64
|
||||
t.string "email", limit: 50
|
||||
t.string "password_salt", limit: 32
|
||||
t.string "reset_password_token"
|
||||
t.integer "sign_in_count", default: 0
|
||||
t.datetime "current_sign_in_at", precision: nil
|
||||
t.datetime "last_sign_in_at", precision: nil
|
||||
t.string "current_sign_in_ip"
|
||||
t.string "last_sign_in_ip"
|
||||
t.integer "failed_attempts", default: 0
|
||||
t.string "unlock_token"
|
||||
t.datetime "locked_at", precision: nil
|
||||
t.datetime "created_at", precision: nil
|
||||
t.datetime "updated_at", precision: nil
|
||||
t.datetime "reset_password_sent_at", precision: nil
|
||||
t.datetime "remember_created_at"
|
||||
t.string "provider"
|
||||
t.string "uid"
|
||||
t.string "neopass_email"
|
||||
t.index ["email"], name: "index_auth_users_on_email", unique: true
|
||||
t.index ["provider", "uid"], name: "index_auth_users_on_provider_and_uid", unique: true
|
||||
t.index ["reset_password_token"], name: "index_auth_users_on_reset_password_token", unique: true
|
||||
t.index ["unlock_token"], name: "index_auth_users_on_unlock_token", unique: true
|
||||
end
|
||||
|
||||
create_table "campaigns", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_520_ci", force: :cascade do |t|
|
||||
t.integer "progress", default: 0, null: false
|
||||
t.integer "goal", null: false
|
||||
|
|
|
|||
|
|
@ -419,19 +419,18 @@
|
|||
community.mysql.mysql_db:
|
||||
name:
|
||||
- openneo_impress
|
||||
- openneo_id
|
||||
|
||||
- name: Create MySQL user openneo_impress
|
||||
community.mysql.mysql_user:
|
||||
name: openneo_impress
|
||||
password: "{{ mysql_user_password }}"
|
||||
priv: "openneo_impress.*:ALL,openneo_id.*:ALL"
|
||||
priv: "openneo_impress.*:ALL"
|
||||
|
||||
- name: Create MySQL user impress2020
|
||||
community.mysql.mysql_user:
|
||||
name: impress2020
|
||||
password: "{{ mysql_user_password_2020 }}"
|
||||
priv: "openneo_impress.*:ALL,openneo_id.*:ALL"
|
||||
priv: "openneo_impress.*:ALL"
|
||||
|
||||
- name: Create the Neopets Media Archive data directory
|
||||
file:
|
||||
|
|
|
|||
287
docs/database-consolidation-deployment.md
Normal file
287
docs/database-consolidation-deployment.md
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
# Database Consolidation Deployment Guide
|
||||
|
||||
This document outlines the plan and checklist for consolidating the `openneo_id` database into the main `openneo_impress` database.
|
||||
|
||||
## Current Status: BLOCKED
|
||||
|
||||
**This migration cannot be deployed until Impress 2020 is retired.**
|
||||
|
||||
## The Problem
|
||||
|
||||
While the main DTI Rails app is ready to move to a single-database architecture, **Impress 2020 still directly accesses both databases**:
|
||||
|
||||
- `openneo_impress` - For reading item, pet, and outfit data
|
||||
- `openneo_id` - For user authentication via GraphQL
|
||||
|
||||
If we consolidate the databases now, Impress 2020's authentication will break immediately, causing login failures for users accessing DTI through the Impress 2020 GraphQL API.
|
||||
|
||||
## Path Forward
|
||||
|
||||
There are two options to unblock this migration:
|
||||
|
||||
### Option A: Retire Impress 2020 First (Recommended)
|
||||
|
||||
1. Complete the migration of remaining Impress 2020 dependencies back to the main Rails app
|
||||
- See `docs/impress-2020-dependencies.md` for current status
|
||||
- Primary remaining dependencies: GraphQL API for outfit data, image generation service
|
||||
2. Spin down the Impress 2020 service entirely
|
||||
3. Execute the database consolidation (steps below)
|
||||
|
||||
### Option B: Coordinated Update (Complex)
|
||||
|
||||
1. Update Impress 2020 to point to `openneo_impress.auth_users` instead of `openneo_id.users`
|
||||
2. Deploy both applications simultaneously during a maintenance window
|
||||
3. Execute the database consolidation
|
||||
|
||||
**Recommendation:** Option A is simpler and aligns with our long-term goal of fully consolidating back into the Rails monolith.
|
||||
|
||||
---
|
||||
|
||||
## Deployment Checklist (When Ready)
|
||||
|
||||
⚠️ **DO NOT EXECUTE UNTIL IMPRESS 2020 IS RETIRED**
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- [ ] Impress 2020 service is spun down and no longer accessing databases
|
||||
- [ ] All Impress 2020 dependencies have been migrated to main Rails app
|
||||
- [ ] Database backups are current and tested
|
||||
- [ ] Maintenance window scheduled (estimate: 30-60 minutes)
|
||||
|
||||
### Phase 1: Deploy Write Lock
|
||||
|
||||
**Branch:** `feature/consolidate-auth-database` @ commit `604a8667`
|
||||
|
||||
**Purpose:** Prevent writes to AuthUser table while keeping login/logout functional.
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. Deploy Phase 1 to production
|
||||
2. Verify:
|
||||
- [ ] Existing users can log in
|
||||
- [ ] Existing users can log out
|
||||
- [ ] Registration shows maintenance message
|
||||
- [ ] Settings updates show maintenance message
|
||||
- [ ] NeoPass connection shows maintenance message
|
||||
|
||||
**Expected Downtime:** None (read-only mode for account changes only)
|
||||
|
||||
### Phase 2: Copy Data
|
||||
|
||||
**Purpose:** Copy auth data from `openneo_id` to `openneo_impress` while table is stable.
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. **Backup openneo_id database:**
|
||||
```bash
|
||||
mysqldump -h [host] -u [user] -p openneo_id > openneo_id_backup_$(date +%Y%m%d_%H%M%S).sql
|
||||
```
|
||||
|
||||
2. **Verify backup:**
|
||||
```bash
|
||||
# Check file size is reasonable
|
||||
ls -lh openneo_id_backup_*.sql
|
||||
|
||||
# Spot-check contents
|
||||
head -n 50 openneo_id_backup_*.sql
|
||||
```
|
||||
|
||||
3. **Run the migration:**
|
||||
```bash
|
||||
cd /var/www/impress
|
||||
bundle exec rails db:migrate
|
||||
```
|
||||
|
||||
4. **Verify data copy:**
|
||||
```sql
|
||||
-- Connect to MySQL
|
||||
mysql -h [host] -u [user] -p
|
||||
|
||||
-- Check row counts match
|
||||
SELECT COUNT(*) AS openneo_id_count FROM openneo_id.users;
|
||||
SELECT COUNT(*) AS auth_users_count FROM openneo_impress.auth_users;
|
||||
|
||||
-- Spot-check a few records
|
||||
SELECT id, name, email FROM openneo_id.users LIMIT 5;
|
||||
SELECT id, name, email FROM openneo_impress.auth_users WHERE id IN (1, 2, 3, 4, 5);
|
||||
|
||||
-- Verify indexes were created
|
||||
SHOW INDEX FROM openneo_impress.auth_users;
|
||||
```
|
||||
|
||||
5. **Verify results:**
|
||||
- [ ] Row counts match exactly
|
||||
- [ ] Sample records match (IDs, names, emails)
|
||||
- [ ] All 4 indexes created (email, provider+uid, reset_password_token, unlock_token)
|
||||
|
||||
**Expected Downtime:** None (still in write-lock mode)
|
||||
|
||||
### Phase 3: Switch to New Table
|
||||
|
||||
**Branch:** `feature/consolidate-auth-database` @ commit `2c21269a`
|
||||
|
||||
**Purpose:** Point AuthUser at consolidated table, restore full functionality.
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. Deploy Phase 2 to production
|
||||
2. **Immediately test critical paths:**
|
||||
- [ ] Login with existing account
|
||||
- [ ] Logout
|
||||
- [ ] Register new account
|
||||
- [ ] Update account settings (email, password)
|
||||
- [ ] Connect NeoPass (if available)
|
||||
- [ ] Disconnect NeoPass (if available)
|
||||
|
||||
3. **Monitor error logs:**
|
||||
```bash
|
||||
tail -f /var/www/impress/log/production.log | grep -i error
|
||||
```
|
||||
|
||||
4. **Verify database queries are using auth_users:**
|
||||
```bash
|
||||
# Check recent queries in logs
|
||||
grep "auth_users" /var/www/impress/log/production.log | tail -n 20
|
||||
|
||||
# Should see SELECT/INSERT/UPDATE on auth_users, NOT openneo_id.users
|
||||
```
|
||||
|
||||
**Expected Downtime:** Brief (< 1 minute for deployment)
|
||||
|
||||
**Rollback Plan:** If critical issues found, revert to Phase 1 commit and restore openneo_id from backup.
|
||||
|
||||
### Phase 4: Documentation Update
|
||||
|
||||
**Branch:** `feature/consolidate-auth-database` @ commit `9ba94f9f`
|
||||
|
||||
**Purpose:** Update documentation to reflect single-database architecture.
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. Deploy Phase 3 to production
|
||||
2. Verify no errors
|
||||
|
||||
**Expected Downtime:** None
|
||||
|
||||
### Phase 5: Database Teardown
|
||||
|
||||
**Purpose:** Remove the now-unused `openneo_id` database.
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. **Wait 7 days** to ensure no issues found in production
|
||||
|
||||
2. **Final backup:**
|
||||
```bash
|
||||
mysqldump -h [host] -u [user] -p openneo_id > openneo_id_final_backup_$(date +%Y%m%d_%H%M%S).sql
|
||||
```
|
||||
|
||||
3. **Store backup offsite:**
|
||||
- Upload to secure backup storage
|
||||
- Keep for at least 90 days
|
||||
|
||||
4. **Drop the database:**
|
||||
```sql
|
||||
DROP DATABASE openneo_id;
|
||||
```
|
||||
|
||||
5. **Remove environment variable:**
|
||||
- Delete `DATABASE_URL_OPENNEO_ID` from production environment config
|
||||
- Restart app to ensure it doesn't try to connect
|
||||
|
||||
6. **Update MySQL users:**
|
||||
```sql
|
||||
-- Remove openneo_id privileges from users
|
||||
-- (Already done in deploy/setup.yml for new deployments)
|
||||
```
|
||||
|
||||
**Expected Downtime:** None
|
||||
|
||||
---
|
||||
|
||||
## Rollback Procedures
|
||||
|
||||
### If Issues Found After Phase 3
|
||||
|
||||
1. **Immediate rollback:**
|
||||
```bash
|
||||
# Revert to Phase 1 commit
|
||||
git checkout 604a8667
|
||||
bundle exec rails db:migrate:down VERSION=20251102064247
|
||||
# Deploy
|
||||
```
|
||||
|
||||
2. **Restore openneo_id (if needed):**
|
||||
```bash
|
||||
mysql -h [host] -u [user] -p openneo_id < openneo_id_backup_[timestamp].sql
|
||||
```
|
||||
|
||||
3. **Investigate issues before reattempting**
|
||||
|
||||
### If Data Corruption Detected
|
||||
|
||||
1. **Immediately restore from backup:**
|
||||
```bash
|
||||
# Drop corrupted auth_users table
|
||||
mysql -h [host] -u [user] -p -e "DROP TABLE openneo_impress.auth_users;"
|
||||
|
||||
# Restore openneo_id if needed
|
||||
mysql -h [host] -u [user] -p openneo_id < openneo_id_backup_[timestamp].sql
|
||||
```
|
||||
|
||||
2. **Revert to pre-migration code**
|
||||
3. **Review migration SQL before reattempting**
|
||||
|
||||
---
|
||||
|
||||
## Key Risks & Mitigations
|
||||
|
||||
| Risk | Impact | Mitigation | Status |
|
||||
|------|--------|------------|--------|
|
||||
| Impress 2020 auth breaks | HIGH - Users can't log in via I2020 | Block deployment until I2020 retired | ⚠️ BLOCKING |
|
||||
| Data copy fails mid-migration | HIGH - Incomplete auth data | Wrapped in transaction, can rollback | ✅ Mitigated |
|
||||
| Production traffic during copy | MEDIUM - Stale data | Write lock prevents changes | ✅ Mitigated |
|
||||
| Schema mismatch between DBs | MEDIUM - Migration fails | Migration matches exact schema | ✅ Mitigated |
|
||||
| Indexes not created | MEDIUM - Slow queries | Verification step checks indexes | ✅ Mitigated |
|
||||
| Login tracking data loss | LOW - Missing login stats | Acceptable trade-off | ✅ Accepted |
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- [ ] All existing users can log in
|
||||
- [ ] New user registration works
|
||||
- [ ] Settings updates work
|
||||
- [ ] NeoPass connection/disconnection works
|
||||
- [ ] No errors in production logs
|
||||
- [ ] Query performance unchanged
|
||||
- [ ] Database row counts match
|
||||
- [ ] All auth_users indexes present
|
||||
|
||||
---
|
||||
|
||||
## Timeline Estimate
|
||||
|
||||
**Total time:** 30-60 minutes (after Impress 2020 retired)
|
||||
|
||||
- Phase 1 deployment: 5 min
|
||||
- Phase 2 data copy: 5-10 min (depending on user count)
|
||||
- Phase 3 deployment + testing: 15-30 min
|
||||
- Phase 4 deployment: 5 min
|
||||
- Phase 5 teardown: 7+ days later, 10 min
|
||||
|
||||
---
|
||||
|
||||
## Questions Before Proceeding
|
||||
|
||||
1. **Is Impress 2020 fully retired?** If not, STOP.
|
||||
2. Do we have recent database backups? (< 24 hours old)
|
||||
3. Do we have a maintenance window scheduled?
|
||||
4. Have we announced the maintenance to users?
|
||||
5. Do we have rollback access ready?
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** November 2025
|
||||
**Status:** Blocked on Impress 2020 retirement
|
||||
**Branch:** `feature/consolidate-auth-database`
|
||||
Loading…
Reference in a new issue