5947 lines
135 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Build Power. Not Rent It. Own your digital infrastructure.">
<meta name="author" content="Bunker Operations">
<link rel="canonical" href="https://bnkserve.org/v2/migration/">
<link rel="prev" href="../troubleshooting/performance-optimization/">
<link rel="next" href="feature-parity/">
<link rel="icon" href="../../assets/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.1">
<title>Migration Guide: V1 to V2 Overview - Changemaker Lite</title>
<link rel="stylesheet" href="../../assets/stylesheets/main.484c7ddc.min.css">
<link rel="stylesheet" href="../../assets/stylesheets/palette.ab4e12ef.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300,300i,400,400i,700,700i%7CJetBrains+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"Inter";--md-code-font:"JetBrains Mono"}</style>
<link rel="stylesheet" href="../../stylesheets/extra.css">
<link rel="stylesheet" href="../../stylesheets/home.css">
<link rel="stylesheet" href="../../assets/css/video-player.css">
<script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
<meta property="og:type" content="website" />
<meta property="og:title" content="Migration Guide: V1 to V2 Overview - Changemaker Lite" />
<meta property="og:description" content="Build Power. Not Rent It. Own your digital infrastructure." />
<meta property="og:image" content="https://bnkserve.org/assets/images/social/v2/migration/index.png" />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:url" content="https://bnkserve.org/v2/migration/" />
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:title" content="Migration Guide: V1 to V2 Overview - Changemaker Lite" />
<meta property="twitter:description" content="Build Power. Not Rent It. Own your digital infrastructure." />
<meta property="twitter:image" content="https://bnkserve.org/assets/images/social/v2/migration/index.png" />
</head>
<body dir="ltr" data-md-color-scheme="slate" data-md-color-primary="deep-purple" data-md-color-accent="amber">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
<a href="#migration-guide-v1-to-v2-overview" class="md-skip">
Skip to content
</a>
</div>
<div data-md-component="announce">
</div>
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href="../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
<img src="../../assets/logo.png" alt="logo">
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
Changemaker Lite
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
Migration Guide: V1 to V2 Overview
</span>
</div>
</div>
</div>
<form class="md-header__option" data-md-component="palette">
<input class="md-option" data-md-color-media="" data-md-color-scheme="slate" data-md-color-primary="deep-purple" data-md-color-accent="amber" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_0">
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_1" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m17.75 4.09-2.53 1.94.91 3.06-2.63-1.81-2.63 1.81.91-3.06-2.53-1.94L12.44 4l1.06-3 1.06 3zm3.5 6.91-1.64 1.25.59 1.98-1.7-1.17-1.7 1.17.59-1.98L15.75 11l2.06-.05L18.5 9l.69 1.95zm-2.28 4.95c.83-.08 1.72 1.1 1.19 1.85-.32.45-.66.87-1.08 1.27C15.17 23 8.84 23 4.94 19.07c-3.91-3.9-3.91-10.24 0-14.14.4-.4.82-.76 1.27-1.08.75-.53 1.93.36 1.85 1.19-.27 2.86.69 5.83 2.89 8.02a9.96 9.96 0 0 0 8.02 2.89m-1.64 2.02a12.08 12.08 0 0 1-7.8-3.47c-2.17-2.19-3.33-5-3.49-7.82-2.81 3.14-2.7 7.96.31 10.98 3.02 3.01 7.84 3.12 10.98.31"/></svg>
</label>
<input class="md-option" data-md-color-media="" data-md-color-scheme="default" data-md-color-primary="deep-purple" data-md-color-accent="amber" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_1">
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_0" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 7a5 5 0 0 1 5 5 5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5m0 2a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3m0-7 2.39 3.42C13.65 5.15 12.84 5 12 5s-1.65.15-2.39.42zM3.34 7l4.16-.35A7.2 7.2 0 0 0 5.94 8.5c-.44.74-.69 1.5-.83 2.29zm.02 10 1.76-3.77a7.131 7.131 0 0 0 2.38 4.14zM20.65 7l-1.77 3.79a7.02 7.02 0 0 0-2.38-4.15zm-.01 10-4.14.36c.59-.51 1.12-1.14 1.54-1.86.42-.73.69-1.5.83-2.29zM12 22l-2.41-3.44c.74.27 1.55.44 2.41.44.82 0 1.63-.17 2.37-.44z"/></svg>
</label>
</form>
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
<label class="md-header__button md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
<label class="md-search__icon md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
</label>
<nav class="md-search__options" aria-label="Search">
<a href="javascript:void(0)" class="md-search__icon md-icon" title="Share" aria-label="Share" data-clipboard data-clipboard-text="" data-md-component="search-share" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91s2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08"/></svg>
</a>
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</button>
</nav>
<div class="md-search__suggest" data-md-component="search-suggest"></div>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
<div class="md-search-result" data-md-component="search-result">
<div class="md-search-result__meta">
Initializing search
</div>
<ol class="md-search-result__list" role="presentation"></ol>
</div>
</div>
</div>
</div>
</div>
<div class="md-header__source">
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
</div>
<div class="md-source__repository">
changemaker.lite
</div>
</a>
</div>
</nav>
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
<div class="md-grid">
<ul class="md-tabs__list">
<li class="md-tabs__item">
<a href="../.." class="md-tabs__link">
Home
</a>
</li>
<li class="md-tabs__item md-tabs__item--active">
<a href="../" class="md-tabs__link">
V2 Documentation
</a>
</li>
<li class="md-tabs__item">
<a href="../../phil/" class="md-tabs__link">
Philosophy
</a>
</li>
<li class="md-tabs__item">
<a href="../../v1/" class="md-tabs__link">
V1 Documentation (Legacy)
</a>
</li>
<li class="md-tabs__item">
<a href="../../blog/" class="md-tabs__link">
Blog
</a>
</li>
</ul>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary md-nav--lifted" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href="../.." title="Changemaker Lite" class="md-nav__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
<img src="../../assets/logo.png" alt="logo">
</a>
Changemaker Lite
</label>
<div class="md-nav__source">
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
</div>
<div class="md-source__repository">
changemaker.lite
</div>
</a>
</div>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../.." class="md-nav__link">
<span class="md-ellipsis">
Home
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2" checked>
<div class="md-nav__link md-nav__container">
<a href="../" class="md-nav__link ">
<span class="md-ellipsis">
V2 Documentation
</span>
</a>
<label class="md-nav__link " for="__nav_2" id="__nav_2_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_2_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_2">
<span class="md-nav__icon md-icon"></span>
V2 Documentation
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_2" >
<div class="md-nav__link md-nav__container">
<a href="../getting-started/" class="md-nav__link ">
<span class="md-ellipsis">
Getting Started
</span>
</a>
<label class="md-nav__link " for="__nav_2_2" id="__nav_2_2_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_2">
<span class="md-nav__icon md-icon"></span>
Getting Started
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../getting-started/quick-start/" class="md-nav__link">
<span class="md-ellipsis">
Quick Start
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_3" >
<div class="md-nav__link md-nav__container">
<a href="../architecture/" class="md-nav__link ">
<span class="md-ellipsis">
Architecture
</span>
</a>
<label class="md-nav__link " for="__nav_2_3" id="__nav_2_3_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_3_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_3">
<span class="md-nav__icon md-icon"></span>
Architecture
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../architecture/dual-api/" class="md-nav__link">
<span class="md-ellipsis">
Dual API System
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../architecture/authentication/" class="md-nav__link">
<span class="md-ellipsis">
Authentication & Security
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_4" >
<div class="md-nav__link md-nav__container">
<a href="../backend/" class="md-nav__link ">
<span class="md-ellipsis">
Backend
</span>
</a>
<label class="md-nav__link " for="__nav_2_4" id="__nav_2_4_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_4_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_4">
<span class="md-nav__icon md-icon"></span>
Backend
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../backend/modules/" class="md-nav__link">
<span class="md-ellipsis">
Modules
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../backend/services/" class="md-nav__link">
<span class="md-ellipsis">
Services
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../backend/middleware/" class="md-nav__link">
<span class="md-ellipsis">
Middleware
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../backend/utilities/" class="md-nav__link">
<span class="md-ellipsis">
Utilities
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_5" >
<div class="md-nav__link md-nav__container">
<a href="../frontend/" class="md-nav__link ">
<span class="md-ellipsis">
Frontend
</span>
</a>
<label class="md-nav__link " for="__nav_2_5" id="__nav_2_5_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_5_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_5">
<span class="md-nav__icon md-icon"></span>
Frontend
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../frontend/components/" class="md-nav__link">
<span class="md-ellipsis">
Components
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../frontend/layouts/" class="md-nav__link">
<span class="md-ellipsis">
Layouts
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../frontend/pages/" class="md-nav__link">
<span class="md-ellipsis">
Pages
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_6" >
<div class="md-nav__link md-nav__container">
<a href="../database/" class="md-nav__link ">
<span class="md-ellipsis">
Database
</span>
</a>
<label class="md-nav__link " for="__nav_2_6" id="__nav_2_6_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_6_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_6">
<span class="md-nav__icon md-icon"></span>
Database
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../database/schema/" class="md-nav__link">
<span class="md-ellipsis">
Schema Overview
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../database/migrations/" class="md-nav__link">
<span class="md-ellipsis">
Migrations
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../database/seeding/" class="md-nav__link">
<span class="md-ellipsis">
Seeding
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../database/indexes/" class="md-nav__link">
<span class="md-ellipsis">
Indexes
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../database/models/" class="md-nav__link">
<span class="md-ellipsis">
Models
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_7" >
<div class="md-nav__link md-nav__container">
<a href="../features/" class="md-nav__link ">
<span class="md-ellipsis">
Features
</span>
</a>
<label class="md-nav__link " for="__nav_2_7" id="__nav_2_7_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_7_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_7">
<span class="md-nav__icon md-icon"></span>
Features
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../features/influence/" class="md-nav__link">
<span class="md-ellipsis">
Influence
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../features/map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../features/landing-pages/" class="md-nav__link">
<span class="md-ellipsis">
Landing Pages
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../features/email-templates/" class="md-nav__link">
<span class="md-ellipsis">
Email Templates
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../features/media/" class="md-nav__link">
<span class="md-ellipsis">
Media
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../features/newsletter/" class="md-nav__link">
<span class="md-ellipsis">
Newsletter
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../features/observability/" class="md-nav__link">
<span class="md-ellipsis">
Observability
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../features/tunnel/" class="md-nav__link">
<span class="md-ellipsis">
Tunnel
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_8" >
<div class="md-nav__link md-nav__container">
<a href="../deployment/" class="md-nav__link ">
<span class="md-ellipsis">
Deployment
</span>
</a>
<label class="md-nav__link " for="__nav_2_8" id="__nav_2_8_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_8_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_8">
<span class="md-nav__icon md-icon"></span>
Deployment
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../deployment/docker-compose/" class="md-nav__link">
<span class="md-ellipsis">
Docker Compose
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../deployment/environment-variables/" class="md-nav__link">
<span class="md-ellipsis">
Environment Variables
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../deployment/nginx/" class="md-nav__link">
<span class="md-ellipsis">
Nginx Configuration
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../deployment/ssl-tls/" class="md-nav__link">
<span class="md-ellipsis">
SSL/TLS
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../deployment/tunneling/" class="md-nav__link">
<span class="md-ellipsis">
Tunneling
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../deployment/monitoring-stack/" class="md-nav__link">
<span class="md-ellipsis">
Monitoring Stack
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../deployment/healthchecks/" class="md-nav__link">
<span class="md-ellipsis">
Health Checks
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../deployment/scaling/" class="md-nav__link">
<span class="md-ellipsis">
Scaling
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../deployment/backup-restore/" class="md-nav__link">
<span class="md-ellipsis">
Backup & Restore
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_9" >
<div class="md-nav__link md-nav__container">
<a href="../development/" class="md-nav__link ">
<span class="md-ellipsis">
Development
</span>
</a>
<label class="md-nav__link " for="__nav_2_9" id="__nav_2_9_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_9_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_9">
<span class="md-nav__icon md-icon"></span>
Development
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../development/local-setup/" class="md-nav__link">
<span class="md-ellipsis">
Local Setup
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../development/docker-workflow/" class="md-nav__link">
<span class="md-ellipsis">
Docker Workflow
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../development/git-workflow/" class="md-nav__link">
<span class="md-ellipsis">
Git Workflow
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../development/npm-commands/" class="md-nav__link">
<span class="md-ellipsis">
NPM Commands
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../development/migrations/" class="md-nav__link">
<span class="md-ellipsis">
Migrations
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../development/typescript/" class="md-nav__link">
<span class="md-ellipsis">
TypeScript
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../development/testing/" class="md-nav__link">
<span class="md-ellipsis">
Testing
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../development/debugging/" class="md-nav__link">
<span class="md-ellipsis">
Debugging
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../development/code-style/" class="md-nav__link">
<span class="md-ellipsis">
Code Style
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_10" >
<div class="md-nav__link md-nav__container">
<a href="../api-reference/" class="md-nav__link ">
<span class="md-ellipsis">
API Reference
</span>
</a>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_10_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_10">
<span class="md-nav__icon md-icon"></span>
API Reference
</label>
<ul class="md-nav__list" data-md-scrollfix>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_11" >
<div class="md-nav__link md-nav__container">
<a href="../user-guides/" class="md-nav__link ">
<span class="md-ellipsis">
User Guides
</span>
</a>
<label class="md-nav__link " for="__nav_2_11" id="__nav_2_11_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_11_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_11">
<span class="md-nav__icon md-icon"></span>
User Guides
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../user-guides/admin-guide/" class="md-nav__link">
<span class="md-ellipsis">
Admin Guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../user-guides/campaign-manager-guide/" class="md-nav__link">
<span class="md-ellipsis">
Campaign Manager Guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../user-guides/map-organizer-guide/" class="md-nav__link">
<span class="md-ellipsis">
Map Organizer Guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../user-guides/content-editor-guide/" class="md-nav__link">
<span class="md-ellipsis">
Content Editor Guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../user-guides/volunteer-guide/" class="md-nav__link">
<span class="md-ellipsis">
Volunteer Guide
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_12" >
<div class="md-nav__link md-nav__container">
<a href="../troubleshooting/" class="md-nav__link ">
<span class="md-ellipsis">
Troubleshooting
</span>
</a>
<label class="md-nav__link " for="__nav_2_12" id="__nav_2_12_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_12_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_12">
<span class="md-nav__icon md-icon"></span>
Troubleshooting
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../troubleshooting/faq/" class="md-nav__link">
<span class="md-ellipsis">
FAQ
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../troubleshooting/common-errors/" class="md-nav__link">
<span class="md-ellipsis">
Common Errors
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../troubleshooting/auth-issues/" class="md-nav__link">
<span class="md-ellipsis">
Auth Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../troubleshooting/database-issues/" class="md-nav__link">
<span class="md-ellipsis">
Database Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../troubleshooting/docker-issues/" class="md-nav__link">
<span class="md-ellipsis">
Docker Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../troubleshooting/email-issues/" class="md-nav__link">
<span class="md-ellipsis">
Email Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../troubleshooting/geocoding-issues/" class="md-nav__link">
<span class="md-ellipsis">
Geocoding Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../troubleshooting/monitoring-issues/" class="md-nav__link">
<span class="md-ellipsis">
Monitoring Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../troubleshooting/performance-optimization/" class="md-nav__link">
<span class="md-ellipsis">
Performance Optimization
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_13" checked>
<div class="md-nav__link md-nav__container">
<a href="./" class="md-nav__link md-nav__link--active">
<span class="md-ellipsis">
Migration
</span>
</a>
<label class="md-nav__link md-nav__link--active" for="__nav_2_13" id="__nav_2_13_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_13_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_2_13">
<span class="md-nav__icon md-icon"></span>
Migration
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="feature-parity/" class="md-nav__link">
<span class="md-ellipsis">
Feature Parity
</span>
</a>
</li>
<li class="md-nav__item">
<a href="breaking-changes/" class="md-nav__link">
<span class="md-ellipsis">
Breaking Changes
</span>
</a>
</li>
<li class="md-nav__item">
<a href="api-changes/" class="md-nav__link">
<span class="md-ellipsis">
API Changes
</span>
</a>
</li>
<li class="md-nav__item">
<a href="data-migration/" class="md-nav__link">
<span class="md-ellipsis">
Data Migration
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_14" >
<div class="md-nav__link md-nav__container">
<a href="../contributing/" class="md-nav__link ">
<span class="md-ellipsis">
Contributing
</span>
</a>
<label class="md-nav__link " for="__nav_2_14" id="__nav_2_14_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_14_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_14">
<span class="md-nav__icon md-icon"></span>
Contributing
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../contributing/development-setup/" class="md-nav__link">
<span class="md-ellipsis">
Development Setup
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../contributing/code-of-conduct/" class="md-nav__link">
<span class="md-ellipsis">
Code of Conduct
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../contributing/pull-requests/" class="md-nav__link">
<span class="md-ellipsis">
Pull Requests
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../contributing/roadmap/" class="md-nav__link">
<span class="md-ellipsis">
Roadmap
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../phil/" class="md-nav__link">
<span class="md-ellipsis">
Philosophy
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../v1/" class="md-nav__link">
<span class="md-ellipsis">
V1 Documentation (Legacy)
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../blog/" class="md-nav__link">
<span class="md-ellipsis">
Blog
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--secondary" aria-label="On this page">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
On this page
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#overview" class="md-nav__link">
<span class="md-ellipsis">
Overview
</span>
</a>
<nav class="md-nav" aria-label="Overview">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#v1-vs-v2-at-a-glance" class="md-nav__link">
<span class="md-ellipsis">
V1 vs V2 at a Glance
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#why-migrate-to-v2" class="md-nav__link">
<span class="md-ellipsis">
Why Migrate to V2?
</span>
</a>
<nav class="md-nav" aria-label="Why Migrate to V2?">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#technical-benefits" class="md-nav__link">
<span class="md-ellipsis">
Technical Benefits
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#feature-enhancements" class="md-nav__link">
<span class="md-ellipsis">
Feature Enhancements
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#migration-timeline" class="md-nav__link">
<span class="md-ellipsis">
Migration Timeline
</span>
</a>
<nav class="md-nav" aria-label="Migration Timeline">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#planned-phases-from-v2_planmd" class="md-nav__link">
<span class="md-ellipsis">
Planned Phases (from V2_PLAN.md)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#actual-development-timeline" class="md-nav__link">
<span class="md-ellipsis">
Actual Development Timeline
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#migration-duration-estimate" class="md-nav__link">
<span class="md-ellipsis">
Migration Duration Estimate
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#risk-assessment" class="md-nav__link">
<span class="md-ellipsis">
Risk Assessment
</span>
</a>
<nav class="md-nav" aria-label="Risk Assessment">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#high-risk-areas" class="md-nav__link">
<span class="md-ellipsis">
High Risk Areas
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#medium-risk-areas" class="md-nav__link">
<span class="md-ellipsis">
Medium Risk Areas
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#low-risk-areas" class="md-nav__link">
<span class="md-ellipsis">
Low Risk Areas
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#rollback-plan" class="md-nav__link">
<span class="md-ellipsis">
Rollback Plan
</span>
</a>
<nav class="md-nav" aria-label="Rollback Plan">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#if-migration-fails" class="md-nav__link">
<span class="md-ellipsis">
If Migration Fails
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#rollback-window" class="md-nav__link">
<span class="md-ellipsis">
Rollback Window
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#support-resources" class="md-nav__link">
<span class="md-ellipsis">
Support Resources
</span>
</a>
<nav class="md-nav" aria-label="Support Resources">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#documentation" class="md-nav__link">
<span class="md-ellipsis">
Documentation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#community-support" class="md-nav__link">
<span class="md-ellipsis">
Community &amp; Support
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#professional-services" class="md-nav__link">
<span class="md-ellipsis">
Professional Services
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#prerequisites" class="md-nav__link">
<span class="md-ellipsis">
Prerequisites
</span>
</a>
<nav class="md-nav" aria-label="Prerequisites">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#v1-environment" class="md-nav__link">
<span class="md-ellipsis">
V1 Environment
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#v2-environment" class="md-nav__link">
<span class="md-ellipsis">
V2 Environment
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#migration-planning" class="md-nav__link">
<span class="md-ellipsis">
Migration Planning
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#migration-steps-overview" class="md-nav__link">
<span class="md-ellipsis">
Migration Steps Overview
</span>
</a>
<nav class="md-nav" aria-label="Migration Steps Overview">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#phase-1-preparation-no-downtime" class="md-nav__link">
<span class="md-ellipsis">
Phase 1: Preparation (No Downtime)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-2-data-transformation-no-downtime" class="md-nav__link">
<span class="md-ellipsis">
Phase 2: Data Transformation (No Downtime)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-3-testing-no-downtime" class="md-nav__link">
<span class="md-ellipsis">
Phase 3: Testing (No Downtime)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-4-switchover-15-minutes-downtime" class="md-nav__link">
<span class="md-ellipsis">
Phase 4: Switchover (15 Minutes Downtime)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-5-verification-post-migration" class="md-nav__link">
<span class="md-ellipsis">
Phase 5: Verification (Post-Migration)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#post-migration-checklist" class="md-nav__link">
<span class="md-ellipsis">
Post-Migration Checklist
</span>
</a>
<nav class="md-nav" aria-label="Post-Migration Checklist">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#immediate-day-1" class="md-nav__link">
<span class="md-ellipsis">
Immediate (Day 1)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#first-week" class="md-nav__link">
<span class="md-ellipsis">
First Week
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#first-month" class="md-nav__link">
<span class="md-ellipsis">
First Month
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#common-migration-scenarios" class="md-nav__link">
<span class="md-ellipsis">
Common Migration Scenarios
</span>
</a>
<nav class="md-nav" aria-label="Common Migration Scenarios">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#scenario-1-small-organization-1000-locations" class="md-nav__link">
<span class="md-ellipsis">
Scenario 1: Small Organization (&lt; 1000 locations)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#scenario-2-medium-organization-1000-10000-locations" class="md-nav__link">
<span class="md-ellipsis">
Scenario 2: Medium Organization (1000-10000 locations)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#scenario-3-large-organization-10000-locations" class="md-nav__link">
<span class="md-ellipsis">
Scenario 3: Large Organization (10000+ locations)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#scenario-4-active-campaign-during-migration" class="md-nav__link">
<span class="md-ellipsis">
Scenario 4: Active Campaign During Migration
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#migration-validation-checklist" class="md-nav__link">
<span class="md-ellipsis">
Migration Validation Checklist
</span>
</a>
<nav class="md-nav" aria-label="Migration Validation Checklist">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#data-integrity" class="md-nav__link">
<span class="md-ellipsis">
Data Integrity
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#functional-testing" class="md-nav__link">
<span class="md-ellipsis">
Functional Testing
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#performance-testing" class="md-nav__link">
<span class="md-ellipsis">
Performance Testing
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#security-testing" class="md-nav__link">
<span class="md-ellipsis">
Security Testing
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#troubleshooting-migration-issues" class="md-nav__link">
<span class="md-ellipsis">
Troubleshooting Migration Issues
</span>
</a>
<nav class="md-nav" aria-label="Troubleshooting Migration Issues">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#issue-user-login-fails-after-migration" class="md-nav__link">
<span class="md-ellipsis">
Issue: User Login Fails After Migration
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#issue-missing-data-after-import" class="md-nav__link">
<span class="md-ellipsis">
Issue: Missing Data After Import
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#issue-geocoding-data-lost" class="md-nav__link">
<span class="md-ellipsis">
Issue: Geocoding Data Lost
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#issue-campaign-emails-not-sending" class="md-nav__link">
<span class="md-ellipsis">
Issue: Campaign Emails Not Sending
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#issue-high-memory-usage-after-migration" class="md-nav__link">
<span class="md-ellipsis">
Issue: High Memory Usage After Migration
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#related-documentation" class="md-nav__link">
<span class="md-ellipsis">
Related Documentation
</span>
</a>
<nav class="md-nav" aria-label="Related Documentation">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#migration-guides" class="md-nav__link">
<span class="md-ellipsis">
Migration Guides
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#v2-setup-guides" class="md-nav__link">
<span class="md-ellipsis">
V2 Setup Guides
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#v2-architecture" class="md-nav__link">
<span class="md-ellipsis">
V2 Architecture
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#post-migration" class="md-nav__link">
<span class="md-ellipsis">
Post-Migration
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#next-steps" class="md-nav__link">
<span class="md-ellipsis">
Next Steps
</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<nav class="md-path" aria-label="Navigation" >
<ol class="md-path__list">
<li class="md-path__item">
<a href="../.." class="md-path__link">
<span class="md-ellipsis">
Home
</span>
</a>
</li>
<li class="md-path__item">
<a href="../" class="md-path__link">
<span class="md-ellipsis">
V2 Documentation
</span>
</a>
</li>
<li class="md-path__item">
<a href="./" class="md-path__link">
<span class="md-ellipsis">
Migration
</span>
</a>
</li>
</ol>
</nav>
<article class="md-content__inner md-typeset">
<a href="https://gitea.bnkops.com/admin/changemaker.lite/src/branch/main/mkdocs/docs/v2/migration/index.md" title="Edit this page" class="md-content__button md-icon" rel="edit">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10 20H6V4h7v5h5v3.1l2-2V8l-6-6H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h4zm10.2-7c.1 0 .3.1.4.2l1.3 1.3c.2.2.2.6 0 .8l-1 1-2.1-2.1 1-1c.1-.1.2-.2.4-.2m0 3.9L14.1 23H12v-2.1l6.1-6.1z"/></svg>
</a>
<a href="https://gitea.bnkops.com/admin/changemaker.lite/src/branch/main/mkdocs/docs/v2/migration/index.md" title="View source of this page" class="md-content__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M17 18c.56 0 1 .44 1 1s-.44 1-1 1-1-.44-1-1 .44-1 1-1m0-3c-2.73 0-5.06 1.66-6 4 .94 2.34 3.27 4 6 4s5.06-1.66 6-4c-.94-2.34-3.27-4-6-4m0 6.5a2.5 2.5 0 0 1-2.5-2.5 2.5 2.5 0 0 1 2.5-2.5 2.5 2.5 0 0 1 2.5 2.5 2.5 2.5 0 0 1-2.5 2.5M9.27 20H6V4h7v5h5v4.07c.7.08 1.36.25 2 .49V8l-6-6H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h4.5a8.2 8.2 0 0 1-1.23-2"/></svg>
</a>
<h1 id="migration-guide-v1-to-v2-overview">Migration Guide: V1 to V2 Overview<a class="headerlink" href="#migration-guide-v1-to-v2-overview" title="Permanent link">&para;</a></h1>
<p>This comprehensive guide covers the complete migration process from Changemaker Lite V1 to V2, including architectural changes, data migration, and rollback procedures.</p>
<h2 id="overview">Overview<a class="headerlink" href="#overview" title="Permanent link">&para;</a></h2>
<p>Changemaker Lite V2 is a <strong>complete rebuild</strong> of the platform, not an incremental upgrade. The migration represents a fundamental shift in architecture, technology stack, and approach to campaign management.</p>
<h3 id="v1-vs-v2-at-a-glance">V1 vs V2 at a Glance<a class="headerlink" href="#v1-vs-v2-at-a-glance" title="Permanent link">&para;</a></h3>
<table>
<thead>
<tr>
<th>Aspect</th>
<th>V1</th>
<th>V2</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Architecture</strong></td>
<td>Two separate Express apps</td>
<td>Single unified Express + Fastify API</td>
</tr>
<tr>
<td><strong>Data Layer</strong></td>
<td>NocoDB REST API</td>
<td>Prisma ORM + PostgreSQL 16</td>
</tr>
<tr>
<td><strong>Frontend</strong></td>
<td>Embedded EJS templates</td>
<td>React SPA (Vite + Ant Design)</td>
</tr>
<tr>
<td><strong>Authentication</strong></td>
<td>Session cookies + bcrypt</td>
<td>JWT tokens (access + refresh)</td>
</tr>
<tr>
<td><strong>API Style</strong></td>
<td>REST via NocoDB</td>
<td>REST with Zod validation</td>
</tr>
<tr>
<td><strong>State Management</strong></td>
<td>Server-side sessions</td>
<td>Zustand client state + JWT</td>
</tr>
<tr>
<td><strong>Job Queue</strong></td>
<td>Bull (Redis)</td>
<td>BullMQ (Redis)</td>
</tr>
<tr>
<td><strong>Database</strong></td>
<td>NocoDB tables</td>
<td>Prisma migrations</td>
</tr>
<tr>
<td><strong>Email</strong></td>
<td>Nodemailer + Bull</td>
<td>BullMQ + Listmonk integration</td>
</tr>
<tr>
<td><strong>Ports</strong></td>
<td>3333 (influence), 3000 (map)</td>
<td>4000 (API), 3000 (admin)</td>
</tr>
</tbody>
</table>
<h2 id="why-migrate-to-v2">Why Migrate to V2?<a class="headerlink" href="#why-migrate-to-v2" title="Permanent link">&para;</a></h2>
<h3 id="technical-benefits">Technical Benefits<a class="headerlink" href="#technical-benefits" title="Permanent link">&para;</a></h3>
<ol>
<li><strong>Unified Codebase</strong>: Single API codebase instead of two separate applications</li>
<li><strong>Type Safety</strong>: Full TypeScript coverage with Prisma type generation</li>
<li><strong>Modern Stack</strong>: Latest React, Vite build tooling, Ant Design components</li>
<li><strong>Better Performance</strong>: Direct database access via Prisma vs REST API abstraction</li>
<li><strong>Improved Security</strong>: JWT refresh token rotation, RBAC, comprehensive audit trail</li>
<li><strong>Scalability</strong>: Separation of concerns (dual API architecture for media)</li>
<li><strong>Developer Experience</strong>: Hot reload, better tooling, comprehensive documentation</li>
</ol>
<h3 id="feature-enhancements">Feature Enhancements<a class="headerlink" href="#feature-enhancements" title="Permanent link">&para;</a></h3>
<ol>
<li><strong>New Features</strong>:</li>
<li>Landing page builder with GrapesJS</li>
<li>Email template system with versioning</li>
<li>Media library with video uploads and reactions</li>
<li>Volunteer canvassing system with GPS tracking</li>
<li>Data quality dashboard for geocoding</li>
<li>Comprehensive monitoring (Prometheus + Grafana)</li>
<li>NAR 2025 electoral data import</li>
<li>
<p>Pangolin tunnel integration</p>
</li>
<li>
<p><strong>Enhanced Existing Features</strong>:</p>
</li>
<li>Response wall with upvoting and moderation</li>
<li>Multi-provider geocoding (6 providers)</li>
<li>Advanced shift management with cut assignments</li>
<li>Printable walk sheets with QR codes</li>
<li>
<p>Listmonk newsletter sync</p>
</li>
<li>
<p><strong>Improved Admin Experience</strong>:</p>
</li>
<li>Modern React UI with consistent design</li>
<li>Real-time updates with optimistic UI</li>
<li>Advanced filtering and search</li>
<li>Bulk operations</li>
<li>Responsive mobile support</li>
</ol>
<h2 id="migration-timeline">Migration Timeline<a class="headerlink" href="#migration-timeline" title="Permanent link">&para;</a></h2>
<h3 id="planned-phases-from-v2_planmd">Planned Phases (from V2_PLAN.md)<a class="headerlink" href="#planned-phases-from-v2_planmd" title="Permanent link">&para;</a></h3>
<ul>
<li><strong>Phase 1-14</strong>: ✅ <strong>COMPLETE</strong> (Foundation through Monitoring)</li>
<li><strong>Phase 15</strong>: 🚧 Testing + Polish (current)</li>
</ul>
<h3 id="actual-development-timeline">Actual Development Timeline<a class="headerlink" href="#actual-development-timeline" title="Permanent link">&para;</a></h3>
<ul>
<li><strong>2025-01</strong>: V2 rebuild initiated (clean-room approach)</li>
<li><strong>2025-02</strong>: Security audit completed, NAR import, media upload</li>
<li><strong>2026-02</strong>: Phase 14 complete, ready for production migration</li>
</ul>
<h3 id="migration-duration-estimate">Migration Duration Estimate<a class="headerlink" href="#migration-duration-estimate" title="Permanent link">&para;</a></h3>
<table>
<thead>
<tr>
<th>Migration Step</th>
<th>Duration</th>
<th>Downtime Required</th>
</tr>
</thead>
<tbody>
<tr>
<td>V1 data export</td>
<td>1-2 hours</td>
<td>No</td>
</tr>
<tr>
<td>Data transformation</td>
<td>2-4 hours</td>
<td>No</td>
</tr>
<tr>
<td>V2 database setup</td>
<td>30 minutes</td>
<td>No</td>
</tr>
<tr>
<td>V2 data import</td>
<td>1-3 hours</td>
<td>No</td>
</tr>
<tr>
<td>Testing &amp; validation</td>
<td>2-4 hours</td>
<td>No</td>
</tr>
<tr>
<td>DNS/service switchover</td>
<td>15 minutes</td>
<td><strong>Yes</strong></td>
</tr>
<tr>
<td>Post-migration verification</td>
<td>1 hour</td>
<td>No</td>
</tr>
<tr>
<td><strong>Total</strong></td>
<td><strong>8-15 hours</strong></td>
<td><strong>15 minutes</strong></td>
</tr>
</tbody>
</table>
<div class="admonition tip">
<p class="admonition-title">Minimize Downtime</p>
<p>Perform all data export, transformation, and testing on a separate V2 staging environment. Only switch production traffic after full validation.</p>
</div>
<h2 id="risk-assessment">Risk Assessment<a class="headerlink" href="#risk-assessment" title="Permanent link">&para;</a></h2>
<h3 id="high-risk-areas">High Risk Areas<a class="headerlink" href="#high-risk-areas" title="Permanent link">&para;</a></h3>
<ol>
<li><strong>Data Loss</strong></li>
<li><strong>Risk</strong>: Campaign data, locations, or user accounts lost during migration</li>
<li><strong>Mitigation</strong>: Full V1 backup before migration, validation checksums, rollback plan</li>
<li>
<p><strong>Impact</strong>: High (business-critical data)</p>
</li>
<li>
<p><strong>Authentication Disruption</strong></p>
</li>
<li><strong>Risk</strong>: Users unable to login after migration (password hash incompatibility)</li>
<li><strong>Mitigation</strong>: Test password migration with sample users, password reset flow ready</li>
<li>
<p><strong>Impact</strong>: High (blocks all access)</p>
</li>
<li>
<p><strong>Email Delivery Failure</strong></p>
</li>
<li><strong>Risk</strong>: Campaign emails stop sending after migration</li>
<li><strong>Mitigation</strong>: Test SMTP configuration, BullMQ queue verification, MailHog testing</li>
<li><strong>Impact</strong>: High (core feature)</li>
</ol>
<h3 id="medium-risk-areas">Medium Risk Areas<a class="headerlink" href="#medium-risk-areas" title="Permanent link">&para;</a></h3>
<ol>
<li><strong>Representative Data</strong></li>
<li><strong>Risk</strong>: Cached representative data doesn't migrate correctly</li>
<li><strong>Mitigation</strong>: Cache can be rebuilt from Represent API, non-critical</li>
<li>
<p><strong>Impact</strong>: Medium (cacheable data)</p>
</li>
<li>
<p><strong>Location Geocoding</strong></p>
</li>
<li><strong>Risk</strong>: Geocoded coordinates lost or corrupted</li>
<li><strong>Mitigation</strong>: V2 multi-provider geocoding can re-geocode, bulk geocode endpoint</li>
<li>
<p><strong>Impact</strong>: Medium (can be re-geocoded)</p>
</li>
<li>
<p><strong>Shift Signups</strong></p>
</li>
<li><strong>Risk</strong>: Volunteer shift assignments lost</li>
<li><strong>Mitigation</strong>: Export signups separately, manual verification, confirmation emails</li>
<li><strong>Impact</strong>: Medium (time-sensitive data)</li>
</ol>
<h3 id="low-risk-areas">Low Risk Areas<a class="headerlink" href="#low-risk-areas" title="Permanent link">&para;</a></h3>
<ol>
<li><strong>Response Wall Data</strong></li>
<li><strong>Risk</strong>: Public responses or upvotes lost</li>
<li><strong>Mitigation</strong>: CSV export, manual re-entry if needed</li>
<li>
<p><strong>Impact</strong>: Low (public-facing only)</p>
</li>
<li>
<p><strong>Custom Settings</strong></p>
</li>
<li><strong>Risk</strong>: V1 settings don't map to V2 schema</li>
<li><strong>Mitigation</strong>: Manual reconfiguration in V2 SettingsPage</li>
<li><strong>Impact</strong>: Low (quick to reconfigure)</li>
</ol>
<h2 id="rollback-plan">Rollback Plan<a class="headerlink" href="#rollback-plan" title="Permanent link">&para;</a></h2>
<h3 id="if-migration-fails">If Migration Fails<a class="headerlink" href="#if-migration-fails" title="Permanent link">&para;</a></h3>
<ol>
<li>
<p><strong>Immediate Actions</strong> (within 15 minutes):
<div class="language-bash highlight"><pre><span></span><code><span id="__span-0-1"><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="c1"># Stop V2 services</span>
</span><span id="__span-0-2"><a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a>docker<span class="w"> </span>compose<span class="w"> </span>down
</span><span id="__span-0-3"><a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a>
</span><span id="__span-0-4"><a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="c1"># Restore V1 services</span>
</span><span id="__span-0-5"><a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a>docker<span class="w"> </span>compose<span class="w"> </span>-f<span class="w"> </span>docker-compose.v1.yml<span class="w"> </span>up<span class="w"> </span>-d
</span><span id="__span-0-6"><a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a>
</span><span id="__span-0-7"><a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="c1"># Restore DNS (point back to V1)</span>
</span><span id="__span-0-8"><a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a><span class="c1"># Update tunnel/proxy configuration</span>
</span></code></pre></div></p>
</li>
<li>
<p><strong>Data Restoration</strong> (if V2 data was modified):
<div class="language-bash highlight"><pre><span></span><code><span id="__span-1-1"><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a><span class="c1"># Restore V1 database from backup</span>
</span><span id="__span-1-2"><a id="__codelineno-1-2" name="__codelineno-1-2" href="#__codelineno-1-2"></a>docker<span class="w"> </span>compose<span class="w"> </span>-f<span class="w"> </span>docker-compose.v1.yml<span class="w"> </span><span class="nb">exec</span><span class="w"> </span>-T<span class="w"> </span>v1-postgres<span class="w"> </span><span class="se">\</span>
</span><span id="__span-1-3"><a id="__codelineno-1-3" name="__codelineno-1-3" href="#__codelineno-1-3"></a><span class="w"> </span>psql<span class="w"> </span>-U<span class="w"> </span>nocodb<span class="w"> </span>nocodb<span class="w"> </span>&lt;<span class="w"> </span>backups/v1-nocodb-backup.sql
</span><span id="__span-1-4"><a id="__codelineno-1-4" name="__codelineno-1-4" href="#__codelineno-1-4"></a>
</span><span id="__span-1-5"><a id="__codelineno-1-5" name="__codelineno-1-5" href="#__codelineno-1-5"></a><span class="c1"># Verify data integrity</span>
</span><span id="__span-1-6"><a id="__codelineno-1-6" name="__codelineno-1-6" href="#__codelineno-1-6"></a>docker<span class="w"> </span>compose<span class="w"> </span>-f<span class="w"> </span>docker-compose.v1.yml<span class="w"> </span>logs<span class="w"> </span>-f
</span></code></pre></div></p>
</li>
<li>
<p><strong>Verification</strong>:</p>
</li>
<li>Test V1 login</li>
<li>Verify campaign data visible</li>
<li>Check location map loads</li>
<li>Send test campaign email</li>
<li>Verify response wall displays</li>
</ol>
<h3 id="rollback-window">Rollback Window<a class="headerlink" href="#rollback-window" title="Permanent link">&para;</a></h3>
<ul>
<li><strong>First 24 hours</strong>: Simple rollback (V1 backup unchanged)</li>
<li><strong>After 24 hours</strong>: Complex rollback (may need to merge V2 changes back to V1)</li>
<li><strong>After 1 week</strong>: Rollback not recommended (significant V2 data divergence)</li>
</ul>
<div class="admonition warning">
<p class="admonition-title">Rollback Deadline</p>
<p>Plan your migration with a clear rollback deadline. After this window, V2 becomes the source of truth.</p>
</div>
<h2 id="support-resources">Support Resources<a class="headerlink" href="#support-resources" title="Permanent link">&para;</a></h2>
<h3 id="documentation">Documentation<a class="headerlink" href="#documentation" title="Permanent link">&para;</a></h3>
<ul>
<li><strong>Migration Docs</strong>:</li>
<li><a href="breaking-changes/">Breaking Changes</a> - Detailed V1→V2 differences</li>
<li><a href="data-migration/">Data Migration</a> - Step-by-step data transfer</li>
<li><a href="api-changes/">API Changes</a> - Endpoint mapping table</li>
<li>
<p><a href="feature-parity/">Feature Parity</a> - Feature comparison matrix</p>
</li>
<li>
<p><strong>V2 Docs</strong>:</p>
</li>
<li><a href="../getting-started/">Getting Started</a> - V2 installation</li>
<li><a href="../architecture/">Architecture</a> - System design</li>
<li><a href="../deployment/production.md">Deployment</a> - Production setup</li>
</ul>
<h3 id="community-support">Community &amp; Support<a class="headerlink" href="#community-support" title="Permanent link">&para;</a></h3>
<ul>
<li><strong>GitHub Issues</strong>: Report bugs or migration problems</li>
<li><strong>Discussions</strong>: Ask questions, share migration experiences</li>
<li><strong>Email</strong>: support@cmlite.org for direct assistance</li>
</ul>
<h3 id="professional-services">Professional Services<a class="headerlink" href="#professional-services" title="Permanent link">&para;</a></h3>
<p>For organizations requiring:
- Custom data migration scripts
- Zero-downtime migration
- Training for administrators
- Priority support during migration</p>
<p>Contact: enterprise@cmlite.org</p>
<h2 id="prerequisites">Prerequisites<a class="headerlink" href="#prerequisites" title="Permanent link">&para;</a></h2>
<p>Before beginning migration, ensure you have:</p>
<h3 id="v1-environment">V1 Environment<a class="headerlink" href="#v1-environment" title="Permanent link">&para;</a></h3>
<ul class="task-list">
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>V1 backup completed</strong> (database + uploads)</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>V1 environment variables documented</strong> (<code>.env</code> file)</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>V1 access credentials</strong> (NocoDB admin, database passwords)</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>V1 running and healthy</strong> (all services operational)</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>V1 data export tested</strong> (able to export NocoDB tables)</li>
</ul>
<h3 id="v2-environment">V2 Environment<a class="headerlink" href="#v2-environment" title="Permanent link">&para;</a></h3>
<ul class="task-list">
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>V2 repository cloned</strong> (<code>git checkout v2</code>)</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Docker and Docker Compose installed</strong> (20.10+, 2.0+)</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>PostgreSQL 16 compatible</strong> (for V2 database)</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>4GB+ RAM available</strong> (8GB recommended)</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>20GB+ disk space</strong> (for database + uploads)</li>
</ul>
<h3 id="migration-planning">Migration Planning<a class="headerlink" href="#migration-planning" title="Permanent link">&para;</a></h3>
<ul class="task-list">
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Downtime window scheduled</strong> (notify users)</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Rollback plan reviewed</strong> (tested on staging)</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Team assigned</strong> (minimum 2 people recommended)</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Backup storage ready</strong> (S3 bucket or local storage)</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Testing checklist prepared</strong> (critical workflows to verify)</li>
</ul>
<h2 id="migration-steps-overview">Migration Steps Overview<a class="headerlink" href="#migration-steps-overview" title="Permanent link">&para;</a></h2>
<p>This is a high-level overview. Detailed steps are in <a href="data-migration/">Data Migration</a>.</p>
<h3 id="phase-1-preparation-no-downtime">Phase 1: Preparation (No Downtime)<a class="headerlink" href="#phase-1-preparation-no-downtime" title="Permanent link">&para;</a></h3>
<ol>
<li>
<p><strong>Export V1 Data</strong>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-2-1"><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a><span class="c1"># Export all NocoDB tables to JSON</span>
</span><span id="__span-2-2"><a id="__codelineno-2-2" name="__codelineno-2-2" href="#__codelineno-2-2"></a>./scripts/export-v1-data.sh
</span><span id="__span-2-3"><a id="__codelineno-2-3" name="__codelineno-2-3" href="#__codelineno-2-3"></a>
</span><span id="__span-2-4"><a id="__codelineno-2-4" name="__codelineno-2-4" href="#__codelineno-2-4"></a><span class="c1"># Backup file uploads</span>
</span><span id="__span-2-5"><a id="__codelineno-2-5" name="__codelineno-2-5" href="#__codelineno-2-5"></a>tar<span class="w"> </span>-czf<span class="w"> </span>v1-uploads.tar.gz<span class="w"> </span>./uploads/
</span></code></pre></div></p>
</li>
<li>
<p><strong>Set Up V2 Environment</strong>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-3-1"><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a>git<span class="w"> </span>checkout<span class="w"> </span>v2
</span><span id="__span-3-2"><a id="__codelineno-3-2" name="__codelineno-3-2" href="#__codelineno-3-2"></a>cp<span class="w"> </span>.env.example<span class="w"> </span>.env
</span><span id="__span-3-3"><a id="__codelineno-3-3" name="__codelineno-3-3" href="#__codelineno-3-3"></a><span class="c1"># Edit .env with V2 configuration</span>
</span></code></pre></div></p>
</li>
<li>
<p><strong>Start V2 Services</strong> (parallel to V1)
<div class="language-bash highlight"><pre><span></span><code><span id="__span-4-1"><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a>docker<span class="w"> </span>compose<span class="w"> </span>up<span class="w"> </span>-d<span class="w"> </span>v2-postgres<span class="w"> </span>redis
</span><span id="__span-4-2"><a id="__codelineno-4-2" name="__codelineno-4-2" href="#__codelineno-4-2"></a>docker<span class="w"> </span>compose<span class="w"> </span><span class="nb">exec</span><span class="w"> </span>api<span class="w"> </span>npx<span class="w"> </span>prisma<span class="w"> </span>migrate<span class="w"> </span>deploy
</span></code></pre></div></p>
</li>
</ol>
<h3 id="phase-2-data-transformation-no-downtime">Phase 2: Data Transformation (No Downtime)<a class="headerlink" href="#phase-2-data-transformation-no-downtime" title="Permanent link">&para;</a></h3>
<ol>
<li>
<p><strong>Transform V1 Data for V2</strong>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-5-1"><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a><span class="c1"># Run transformation scripts</span>
</span><span id="__span-5-2"><a id="__codelineno-5-2" name="__codelineno-5-2" href="#__codelineno-5-2"></a>node<span class="w"> </span>scripts/transform-users.js
</span><span id="__span-5-3"><a id="__codelineno-5-3" name="__codelineno-5-3" href="#__codelineno-5-3"></a>node<span class="w"> </span>scripts/transform-campaigns.js
</span><span id="__span-5-4"><a id="__codelineno-5-4" name="__codelineno-5-4" href="#__codelineno-5-4"></a>node<span class="w"> </span>scripts/transform-locations.js
</span></code></pre></div></p>
</li>
<li>
<p><strong>Import into V2 Database</strong>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-6-1"><a id="__codelineno-6-1" name="__codelineno-6-1" href="#__codelineno-6-1"></a><span class="c1"># Import transformed data</span>
</span><span id="__span-6-2"><a id="__codelineno-6-2" name="__codelineno-6-2" href="#__codelineno-6-2"></a>docker<span class="w"> </span>compose<span class="w"> </span><span class="nb">exec</span><span class="w"> </span>api<span class="w"> </span>node<span class="w"> </span>scripts/import-data.js
</span></code></pre></div></p>
</li>
<li>
<p><strong>Validate Data Integrity</strong>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-7-1"><a id="__codelineno-7-1" name="__codelineno-7-1" href="#__codelineno-7-1"></a><span class="c1"># Compare record counts</span>
</span><span id="__span-7-2"><a id="__codelineno-7-2" name="__codelineno-7-2" href="#__codelineno-7-2"></a>docker<span class="w"> </span>compose<span class="w"> </span><span class="nb">exec</span><span class="w"> </span>api<span class="w"> </span>node<span class="w"> </span>scripts/validate-migration.js
</span></code></pre></div></p>
</li>
</ol>
<h3 id="phase-3-testing-no-downtime">Phase 3: Testing (No Downtime)<a class="headerlink" href="#phase-3-testing-no-downtime" title="Permanent link">&para;</a></h3>
<ol>
<li><strong>Test V2 Functionality</strong></li>
<li>Login with test users (verify password migration)</li>
<li>View campaigns, locations, shifts</li>
<li>Submit test response</li>
<li>Send test email</li>
<li>
<p>Check admin permissions</p>
</li>
<li>
<p><strong>Performance Testing</strong></p>
</li>
<li>Load campaigns page (check query performance)</li>
<li>Geocode sample addresses</li>
<li>Test map rendering with all locations</li>
<li>Verify Redis caching</li>
</ol>
<h3 id="phase-4-switchover-15-minutes-downtime">Phase 4: Switchover (15 Minutes Downtime)<a class="headerlink" href="#phase-4-switchover-15-minutes-downtime" title="Permanent link">&para;</a></h3>
<ol>
<li>
<p><strong>Enable Maintenance Mode</strong> (V1)
<div class="language-bash highlight"><pre><span></span><code><span id="__span-8-1"><a id="__codelineno-8-1" name="__codelineno-8-1" href="#__codelineno-8-1"></a><span class="c1"># Stop V1 services</span>
</span><span id="__span-8-2"><a id="__codelineno-8-2" name="__codelineno-8-2" href="#__codelineno-8-2"></a>docker<span class="w"> </span>compose<span class="w"> </span>-f<span class="w"> </span>docker-compose.v1.yml<span class="w"> </span>down
</span></code></pre></div></p>
</li>
<li>
<p><strong>Start V2 Services</strong>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-9-1"><a id="__codelineno-9-1" name="__codelineno-9-1" href="#__codelineno-9-1"></a><span class="c1"># Start all V2 services</span>
</span><span id="__span-9-2"><a id="__codelineno-9-2" name="__codelineno-9-2" href="#__codelineno-9-2"></a>docker<span class="w"> </span>compose<span class="w"> </span>up<span class="w"> </span>-d
</span></code></pre></div></p>
</li>
<li>
<p><strong>Update DNS/Proxy</strong></p>
<ul>
<li>Point <code>cmlite.org</code> to V2 nginx</li>
<li>Update Pangolin tunnel endpoint</li>
<li>Verify SSL certificates</li>
</ul>
</li>
</ol>
<h3 id="phase-5-verification-post-migration">Phase 5: Verification (Post-Migration)<a class="headerlink" href="#phase-5-verification-post-migration" title="Permanent link">&para;</a></h3>
<ol>
<li>
<p><strong>Smoke Tests</strong></p>
<ul>
<li>Admin login works</li>
<li>Campaign list loads</li>
<li>Location map renders</li>
<li>Email sending functional</li>
<li>Response wall displays</li>
</ul>
</li>
<li>
<p><strong>Monitor for Issues</strong>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-10-1"><a id="__codelineno-10-1" name="__codelineno-10-1" href="#__codelineno-10-1"></a><span class="c1"># Watch logs for errors</span>
</span><span id="__span-10-2"><a id="__codelineno-10-2" name="__codelineno-10-2" href="#__codelineno-10-2"></a>docker<span class="w"> </span>compose<span class="w"> </span>logs<span class="w"> </span>-f<span class="w"> </span>api<span class="w"> </span>admin
</span><span id="__span-10-3"><a id="__codelineno-10-3" name="__codelineno-10-3" href="#__codelineno-10-3"></a>
</span><span id="__span-10-4"><a id="__codelineno-10-4" name="__codelineno-10-4" href="#__codelineno-10-4"></a><span class="c1"># Check metrics</span>
</span><span id="__span-10-5"><a id="__codelineno-10-5" name="__codelineno-10-5" href="#__codelineno-10-5"></a>open<span class="w"> </span>http://localhost:3001<span class="w"> </span><span class="c1"># Grafana</span>
</span></code></pre></div></p>
</li>
<li>
<p><strong>Announce Migration Complete</strong></p>
<ul>
<li>Email all users with V2 login URL</li>
<li>Update documentation links</li>
<li>Monitor support channels</li>
</ul>
</li>
</ol>
<h2 id="post-migration-checklist">Post-Migration Checklist<a class="headerlink" href="#post-migration-checklist" title="Permanent link">&para;</a></h2>
<p>After successful migration, complete these tasks:</p>
<h3 id="immediate-day-1">Immediate (Day 1)<a class="headerlink" href="#immediate-day-1" title="Permanent link">&para;</a></h3>
<ul class="task-list">
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> Verify all user accounts can login</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> Test campaign email sending (real SMTP, not MailHog)</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> Confirm location geocoding works</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> Check shift signup flow (public)</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> Verify response wall displays correctly</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> Test admin CRUD operations (create campaign, location, shift)</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> Monitor error logs for exceptions</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> Verify Prometheus metrics collecting</li>
</ul>
<h3 id="first-week">First Week<a class="headerlink" href="#first-week" title="Permanent link">&para;</a></h3>
<ul class="task-list">
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> Review Grafana dashboards for anomalies</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> Check BullMQ job queue (no stuck jobs)</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> Verify geocoding cache hit rate</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> Test all user roles (SUPER_ADMIN, MAP_ADMIN, etc.)</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> Confirm Listmonk sync working (if enabled)</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> Validate backup script runs successfully</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> Review user feedback and support tickets</li>
</ul>
<h3 id="first-month">First Month<a class="headerlink" href="#first-month" title="Permanent link">&para;</a></h3>
<ul class="task-list">
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> Optimize slow queries (check Prometheus API duration metrics)</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> Review disk usage (PostgreSQL, uploads, logs)</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> Audit user permissions (remove temp accounts)</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> Update documentation based on issues encountered</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> Train administrators on new V2 features</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> Plan rollout of new features (landing pages, canvassing)</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> Schedule security audit</li>
</ul>
<h2 id="common-migration-scenarios">Common Migration Scenarios<a class="headerlink" href="#common-migration-scenarios" title="Permanent link">&para;</a></h2>
<h3 id="scenario-1-small-organization-1000-locations">Scenario 1: Small Organization (&lt; 1000 locations)<a class="headerlink" href="#scenario-1-small-organization-1000-locations" title="Permanent link">&para;</a></h3>
<ul>
<li><strong>Migration Duration</strong>: 4-6 hours</li>
<li><strong>Downtime</strong>: 10 minutes</li>
<li><strong>Recommended Approach</strong>:</li>
<li>Export V1 data Friday evening</li>
<li>Transform and import over weekend</li>
<li>Test Saturday/Sunday</li>
<li>Switchover Monday morning</li>
<li>Rollback window: 48 hours</li>
</ul>
<h3 id="scenario-2-medium-organization-1000-10000-locations">Scenario 2: Medium Organization (1000-10000 locations)<a class="headerlink" href="#scenario-2-medium-organization-1000-10000-locations" title="Permanent link">&para;</a></h3>
<ul>
<li><strong>Migration Duration</strong>: 8-12 hours</li>
<li><strong>Downtime</strong>: 15 minutes</li>
<li><strong>Recommended Approach</strong>:</li>
<li>Set up V2 staging environment 1 week prior</li>
<li>Perform test migration on staging</li>
<li>Document issues and solutions</li>
<li>Schedule production migration for low-traffic period</li>
<li>Rollback window: 24 hours</li>
</ul>
<h3 id="scenario-3-large-organization-10000-locations">Scenario 3: Large Organization (10000+ locations)<a class="headerlink" href="#scenario-3-large-organization-10000-locations" title="Permanent link">&para;</a></h3>
<ul>
<li><strong>Migration Duration</strong>: 12-20 hours</li>
<li><strong>Downtime</strong>: 20-30 minutes</li>
<li><strong>Recommended Approach</strong>:</li>
<li>Hire professional services (enterprise@cmlite.org)</li>
<li>Perform multiple test migrations on staging</li>
<li>Use incremental data sync (minimize final catchup)</li>
<li>Blue-green deployment (parallel V1/V2 for 1 week)</li>
<li>Rollback window: 1 week with data sync</li>
</ul>
<h3 id="scenario-4-active-campaign-during-migration">Scenario 4: Active Campaign During Migration<a class="headerlink" href="#scenario-4-active-campaign-during-migration" title="Permanent link">&para;</a></h3>
<p><strong>Problem</strong>: Can't afford downtime during critical campaign period.</p>
<p><strong>Solution</strong>:
1. Set up V2 as read-only mirror (import V1 data, disable writes)
2. Continue using V1 for all active operations
3. Schedule final catchup migration after campaign concludes
4. Or: Use blue-green deployment with manual data sync</p>
<div class="admonition danger">
<p class="admonition-title">Active Campaign Warning</p>
<p>Do NOT migrate during active campaign periods. Schedule migration between campaigns or during organizational downtime.</p>
</div>
<h2 id="migration-validation-checklist">Migration Validation Checklist<a class="headerlink" href="#migration-validation-checklist" title="Permanent link">&para;</a></h2>
<p>Use this checklist to verify successful migration:</p>
<h3 id="data-integrity">Data Integrity<a class="headerlink" href="#data-integrity" title="Permanent link">&para;</a></h3>
<ul class="task-list">
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>User count matches</strong>: V1 users = V2 users (excluding duplicates)</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Campaign count matches</strong>: V1 campaigns = V2 campaigns</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Location count matches</strong>: V1 locations = V2 locations</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Shift count matches</strong>: V1 shifts = V2 shifts</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Response count matches</strong>: V1 responses = V2 responses</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Representative cache count</strong>: V1 reps = V2 reps (approximate, can refresh)</li>
</ul>
<h3 id="functional-testing">Functional Testing<a class="headerlink" href="#functional-testing" title="Permanent link">&para;</a></h3>
<ul class="task-list">
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Login works</strong>: Test with 5 different user accounts</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Password authentication</strong>: All migrated passwords validate correctly</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Campaign email sends</strong>: Queue job, verify SMTP delivery</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Representative lookup</strong>: Postal code returns correct reps</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Location geocoding</strong>: Bulk geocode 10 addresses successfully</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Map rendering</strong>: All locations display on map</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Shift signup</strong>: Public user can sign up for shift</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Response submission</strong>: Can submit and view responses</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Admin CRUD</strong>: Create, edit, delete test records</li>
</ul>
<h3 id="performance-testing">Performance Testing<a class="headerlink" href="#performance-testing" title="Permanent link">&para;</a></h3>
<ul class="task-list">
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Campaign list loads &lt; 2 seconds</strong>: 100+ campaigns</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Location map loads &lt; 3 seconds</strong>: 1000+ locations</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Search response time &lt; 500ms</strong>: User, campaign, location search</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Geocoding batch &lt; 30 seconds</strong>: 100 addresses</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Email queue processing</strong>: 10 emails/minute minimum</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>No N+1 queries</strong>: Check Prisma logs for query count</li>
</ul>
<h3 id="security-testing">Security Testing<a class="headerlink" href="#security-testing" title="Permanent link">&para;</a></h3>
<ul class="task-list">
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>JWT authentication works</strong>: Access + refresh token flow</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>RBAC enforced</strong>: SUPER_ADMIN vs USER vs TEMP roles</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Rate limiting active</strong>: Auth endpoints limited to 10/min</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Password policy enforced</strong>: 12+ chars, complexity requirements</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Redis authenticated</strong>: Connection requires password</li>
<li class="task-list-item"><label class="task-list-control"><input type="checkbox" disabled/><span class="task-list-indicator"></span></label> <strong>Encryption key set</strong>: ENCRYPTION_KEY env var different from JWT secrets</li>
</ul>
<h2 id="troubleshooting-migration-issues">Troubleshooting Migration Issues<a class="headerlink" href="#troubleshooting-migration-issues" title="Permanent link">&para;</a></h2>
<p>Common problems and solutions:</p>
<h3 id="issue-user-login-fails-after-migration">Issue: User Login Fails After Migration<a class="headerlink" href="#issue-user-login-fails-after-migration" title="Permanent link">&para;</a></h3>
<p><strong>Symptoms</strong>: Users receive "Invalid credentials" error despite correct password.</p>
<p><strong>Causes</strong>:
- Bcrypt hash corruption during export/import
- Password field length truncation
- Character encoding issues</p>
<p><strong>Solutions</strong>:
<div class="language-bash highlight"><pre><span></span><code><span id="__span-11-1"><a id="__codelineno-11-1" name="__codelineno-11-1" href="#__codelineno-11-1"></a><span class="c1"># Check password hash format in V2</span>
</span><span id="__span-11-2"><a id="__codelineno-11-2" name="__codelineno-11-2" href="#__codelineno-11-2"></a>docker<span class="w"> </span>compose<span class="w"> </span><span class="nb">exec</span><span class="w"> </span>api<span class="w"> </span>npx<span class="w"> </span>prisma<span class="w"> </span>studio
</span><span id="__span-11-3"><a id="__codelineno-11-3" name="__codelineno-11-3" href="#__codelineno-11-3"></a><span class="c1"># User table → password field should start with $2b$</span>
</span><span id="__span-11-4"><a id="__codelineno-11-4" name="__codelineno-11-4" href="#__codelineno-11-4"></a>
</span><span id="__span-11-5"><a id="__codelineno-11-5" name="__codelineno-11-5" href="#__codelineno-11-5"></a><span class="c1"># Reset affected user password</span>
</span><span id="__span-11-6"><a id="__codelineno-11-6" name="__codelineno-11-6" href="#__codelineno-11-6"></a>docker<span class="w"> </span>compose<span class="w"> </span><span class="nb">exec</span><span class="w"> </span>api<span class="w"> </span>node<span class="w"> </span>scripts/reset-password.js<span class="w"> </span>user@example.com
</span></code></pre></div></p>
<h3 id="issue-missing-data-after-import">Issue: Missing Data After Import<a class="headerlink" href="#issue-missing-data-after-import" title="Permanent link">&para;</a></h3>
<p><strong>Symptoms</strong>: User count, campaign count, or location count lower than V1.</p>
<p><strong>Causes</strong>:
- Incomplete V1 export (pagination issues)
- Transformation script errors (check logs)
- Unique constraint violations (duplicates skipped)</p>
<p><strong>Solutions</strong>:
<div class="language-bash highlight"><pre><span></span><code><span id="__span-12-1"><a id="__codelineno-12-1" name="__codelineno-12-1" href="#__codelineno-12-1"></a><span class="c1"># Compare record counts</span>
</span><span id="__span-12-2"><a id="__codelineno-12-2" name="__codelineno-12-2" href="#__codelineno-12-2"></a>docker<span class="w"> </span>compose<span class="w"> </span><span class="nb">exec</span><span class="w"> </span>api<span class="w"> </span>node<span class="w"> </span>scripts/compare-counts.js
</span><span id="__span-12-3"><a id="__codelineno-12-3" name="__codelineno-12-3" href="#__codelineno-12-3"></a>
</span><span id="__span-12-4"><a id="__codelineno-12-4" name="__codelineno-12-4" href="#__codelineno-12-4"></a><span class="c1"># Re-run import for specific table</span>
</span><span id="__span-12-5"><a id="__codelineno-12-5" name="__codelineno-12-5" href="#__codelineno-12-5"></a>docker<span class="w"> </span>compose<span class="w"> </span><span class="nb">exec</span><span class="w"> </span>api<span class="w"> </span>node<span class="w"> </span>scripts/import-data.js<span class="w"> </span>--table<span class="o">=</span>users
</span><span id="__span-12-6"><a id="__codelineno-12-6" name="__codelineno-12-6" href="#__codelineno-12-6"></a>
</span><span id="__span-12-7"><a id="__codelineno-12-7" name="__codelineno-12-7" href="#__codelineno-12-7"></a><span class="c1"># Check import logs for errors</span>
</span><span id="__span-12-8"><a id="__codelineno-12-8" name="__codelineno-12-8" href="#__codelineno-12-8"></a>docker<span class="w"> </span>compose<span class="w"> </span>logs<span class="w"> </span>api<span class="w"> </span><span class="p">|</span><span class="w"> </span>grep<span class="w"> </span>ERROR
</span></code></pre></div></p>
<h3 id="issue-geocoding-data-lost">Issue: Geocoding Data Lost<a class="headerlink" href="#issue-geocoding-data-lost" title="Permanent link">&para;</a></h3>
<p><strong>Symptoms</strong>: Locations missing latitude/longitude coordinates.</p>
<p><strong>Causes</strong>:
- V1 geocoding provider different from V2
- Coordinates not exported from V1
- Transformation script didn't map geocoding fields</p>
<p><strong>Solutions</strong>:
<div class="language-bash highlight"><pre><span></span><code><span id="__span-13-1"><a id="__codelineno-13-1" name="__codelineno-13-1" href="#__codelineno-13-1"></a><span class="c1"># Bulk re-geocode all locations</span>
</span><span id="__span-13-2"><a id="__codelineno-13-2" name="__codelineno-13-2" href="#__codelineno-13-2"></a>curl<span class="w"> </span>-X<span class="w"> </span>POST<span class="w"> </span>http://localhost:4000/api/map/locations/bulk-geocode<span class="w"> </span><span class="se">\</span>
</span><span id="__span-13-3"><a id="__codelineno-13-3" name="__codelineno-13-3" href="#__codelineno-13-3"></a><span class="w"> </span>-H<span class="w"> </span><span class="s2">&quot;Authorization: Bearer </span><span class="nv">$ADMIN_TOKEN</span><span class="s2">&quot;</span>
</span><span id="__span-13-4"><a id="__codelineno-13-4" name="__codelineno-13-4" href="#__codelineno-13-4"></a>
</span><span id="__span-13-5"><a id="__codelineno-13-5" name="__codelineno-13-5" href="#__codelineno-13-5"></a><span class="c1"># Check geocoding provider configuration</span>
</span><span id="__span-13-6"><a id="__codelineno-13-6" name="__codelineno-13-6" href="#__codelineno-13-6"></a>docker<span class="w"> </span>compose<span class="w"> </span><span class="nb">exec</span><span class="w"> </span>api<span class="w"> </span>node<span class="w"> </span>scripts/test-geocoding.js
</span></code></pre></div></p>
<h3 id="issue-campaign-emails-not-sending">Issue: Campaign Emails Not Sending<a class="headerlink" href="#issue-campaign-emails-not-sending" title="Permanent link">&para;</a></h3>
<p><strong>Symptoms</strong>: BullMQ queue shows "failed" jobs.</p>
<p><strong>Causes</strong>:
- SMTP configuration incorrect
- EMAIL_TEST_MODE still enabled (sends to MailHog)
- Nodemailer authentication failure</p>
<p><strong>Solutions</strong>:
<div class="language-bash highlight"><pre><span></span><code><span id="__span-14-1"><a id="__codelineno-14-1" name="__codelineno-14-1" href="#__codelineno-14-1"></a><span class="c1"># Check SMTP configuration</span>
</span><span id="__span-14-2"><a id="__codelineno-14-2" name="__codelineno-14-2" href="#__codelineno-14-2"></a>docker<span class="w"> </span>compose<span class="w"> </span><span class="nb">exec</span><span class="w"> </span>api<span class="w"> </span>node<span class="w"> </span>scripts/test-smtp.js
</span><span id="__span-14-3"><a id="__codelineno-14-3" name="__codelineno-14-3" href="#__codelineno-14-3"></a>
</span><span id="__span-14-4"><a id="__codelineno-14-4" name="__codelineno-14-4" href="#__codelineno-14-4"></a><span class="c1"># View failed job details</span>
</span><span id="__span-14-5"><a id="__codelineno-14-5" name="__codelineno-14-5" href="#__codelineno-14-5"></a><span class="c1"># Visit http://localhost:4000/api/influence/email-queue/stats</span>
</span><span id="__span-14-6"><a id="__codelineno-14-6" name="__codelineno-14-6" href="#__codelineno-14-6"></a>
</span><span id="__span-14-7"><a id="__codelineno-14-7" name="__codelineno-14-7" href="#__codelineno-14-7"></a><span class="c1"># Retry failed jobs</span>
</span><span id="__span-14-8"><a id="__codelineno-14-8" name="__codelineno-14-8" href="#__codelineno-14-8"></a>curl<span class="w"> </span>-X<span class="w"> </span>POST<span class="w"> </span>http://localhost:4000/api/influence/email-queue/retry-failed<span class="w"> </span><span class="se">\</span>
</span><span id="__span-14-9"><a id="__codelineno-14-9" name="__codelineno-14-9" href="#__codelineno-14-9"></a><span class="w"> </span>-H<span class="w"> </span><span class="s2">&quot;Authorization: Bearer </span><span class="nv">$ADMIN_TOKEN</span><span class="s2">&quot;</span>
</span></code></pre></div></p>
<h3 id="issue-high-memory-usage-after-migration">Issue: High Memory Usage After Migration<a class="headerlink" href="#issue-high-memory-usage-after-migration" title="Permanent link">&para;</a></h3>
<p><strong>Symptoms</strong>: V2 services consuming &gt; 4GB RAM, slow response times.</p>
<p><strong>Causes</strong>:
- Prisma connection pool too large
- Redis cache not evicting old entries
- Large JSON fields in database (campaign data, page blocks)</p>
<p><strong>Solutions</strong>:
<div class="language-bash highlight"><pre><span></span><code><span id="__span-15-1"><a id="__codelineno-15-1" name="__codelineno-15-1" href="#__codelineno-15-1"></a><span class="c1"># Reduce Prisma connection pool</span>
</span><span id="__span-15-2"><a id="__codelineno-15-2" name="__codelineno-15-2" href="#__codelineno-15-2"></a><span class="c1"># Edit .env: DATABASE_URL=&quot;...?connection_limit=5&quot;</span>
</span><span id="__span-15-3"><a id="__codelineno-15-3" name="__codelineno-15-3" href="#__codelineno-15-3"></a>
</span><span id="__span-15-4"><a id="__codelineno-15-4" name="__codelineno-15-4" href="#__codelineno-15-4"></a><span class="c1"># Clear Redis cache</span>
</span><span id="__span-15-5"><a id="__codelineno-15-5" name="__codelineno-15-5" href="#__codelineno-15-5"></a>docker<span class="w"> </span>compose<span class="w"> </span><span class="nb">exec</span><span class="w"> </span>redis<span class="w"> </span>redis-cli<span class="w"> </span>FLUSHDB
</span><span id="__span-15-6"><a id="__codelineno-15-6" name="__codelineno-15-6" href="#__codelineno-15-6"></a>
</span><span id="__span-15-7"><a id="__codelineno-15-7" name="__codelineno-15-7" href="#__codelineno-15-7"></a><span class="c1"># Optimize database</span>
</span><span id="__span-15-8"><a id="__codelineno-15-8" name="__codelineno-15-8" href="#__codelineno-15-8"></a>docker<span class="w"> </span>compose<span class="w"> </span><span class="nb">exec</span><span class="w"> </span>v2-postgres<span class="w"> </span>psql<span class="w"> </span>-U<span class="w"> </span>changemaker<span class="w"> </span>-d<span class="w"> </span>changemaker_v2<span class="w"> </span>-c<span class="w"> </span><span class="s2">&quot;VACUUM ANALYZE;&quot;</span>
</span></code></pre></div></p>
<h2 id="related-documentation">Related Documentation<a class="headerlink" href="#related-documentation" title="Permanent link">&para;</a></h2>
<h3 id="migration-guides">Migration Guides<a class="headerlink" href="#migration-guides" title="Permanent link">&para;</a></h3>
<ul>
<li><a href="breaking-changes/">Breaking Changes</a> - Comprehensive V1→V2 differences</li>
<li><a href="data-migration/">Data Migration</a> - Detailed migration procedures</li>
<li><a href="api-changes/">API Changes</a> - Endpoint reference mapping</li>
<li><a href="feature-parity/">Feature Parity</a> - Feature comparison matrix</li>
</ul>
<h3 id="v2-setup-guides">V2 Setup Guides<a class="headerlink" href="#v2-setup-guides" title="Permanent link">&para;</a></h3>
<ul>
<li><a href="../getting-started/quick-start/">Quick Start</a> - 5-minute V2 setup</li>
<li><a href="../deployment/production.md">Production Deployment</a> - Production configuration</li>
<li><a href="../deployment/environment.md">Environment Variables</a> - .env reference</li>
</ul>
<h3 id="v2-architecture">V2 Architecture<a class="headerlink" href="#v2-architecture" title="Permanent link">&para;</a></h3>
<ul>
<li><a href="../architecture/">Architecture Overview</a> - System design</li>
<li><a href="../architecture/dual-api/">Dual API Design</a> - Express + Fastify</li>
<li><a href="../architecture/authentication/">Authentication</a> - JWT flow</li>
<li><a href="../database/schema/">Database Schema</a> - Prisma models</li>
</ul>
<h3 id="post-migration">Post-Migration<a class="headerlink" href="#post-migration" title="Permanent link">&para;</a></h3>
<ul>
<li><a href="../user-guides/admin-guide/">Admin Guide</a> - Platform administration</li>
<li><a href="../features/observability/monitoring.md">Monitoring</a> - Prometheus + Grafana</li>
<li><a href="../deployment/backup-restore/">Backups</a> - Backup procedures</li>
<li><a href="../troubleshooting/common-errors/">Troubleshooting</a> - Common issues</li>
</ul>
<h2 id="next-steps">Next Steps<a class="headerlink" href="#next-steps" title="Permanent link">&para;</a></h2>
<p>Ready to begin migration?</p>
<ol>
<li><strong><a href="breaking-changes/">Review Breaking Changes</a></strong> - Understand all V1→V2 differences</li>
<li><strong><a href="data-migration/">Plan Data Migration</a></strong> - Create migration timeline</li>
<li><strong><a href="../getting-started/quick-start/">Set Up V2 Staging</a></strong> - Test environment</li>
<li><strong><a href="data-migration/#testing-migration">Perform Test Migration</a></strong> - Validate process</li>
<li><strong><a href="data-migration/#production-migration">Execute Production Migration</a></strong> - Go live</li>
</ol>
<div class="admonition success">
<p class="admonition-title">Migration Support</p>
<p>Need help with your migration? Email support@cmlite.org or open a GitHub discussion.</p>
</div>
</article>
</div>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
<button type="button" class="md-top md-icon" data-md-component="top" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg>
Back to top
</button>
</main>
<footer class="md-footer">
<nav class="md-footer__inner md-grid" aria-label="Footer" >
<a href="../troubleshooting/performance-optimization/" class="md-footer__link md-footer__link--prev" aria-label="Previous: Performance Optimization">
<div class="md-footer__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
</div>
<div class="md-footer__title">
<span class="md-footer__direction">
Previous
</span>
<div class="md-ellipsis">
Performance Optimization
</div>
</div>
</a>
<a href="feature-parity/" class="md-footer__link md-footer__link--next" aria-label="Next: Feature Parity">
<div class="md-footer__title">
<span class="md-footer__direction">
Next
</span>
<div class="md-ellipsis">
Feature Parity
</div>
</div>
<div class="md-footer__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 11v2h12l-5.5 5.5 1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5 16 11z"/></svg>
</div>
</a>
</nav>
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
<div class="md-copyright__highlight">
Copyright &copy; 2024 The Bunker Operations <a href="#__consent">Change cookie settings</a>
</div>
</div>
<div class="md-social">
<a href="https://gitea.bnkops.com/admin" target="_blank" rel="noopener" title="Gitea Repository" class="md-social__link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg>
</a>
<a href="https://listmonk.bnkops.com/subscription/form" target="_blank" rel="noopener" title="Newsletter" class="md-social__link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M536.4-26.3c9.8-3.5 20.6-1 28 6.3s9.8 18.2 6.3 28l-178 496.9c-5 13.9-18.1 23.1-32.8 23.1-14.2 0-27-8.6-32.3-21.7l-64.2-158c-4.5-11-2.5-23.6 5.2-32.6l94.5-112.4c5.1-6.1 4.7-15-.9-20.6s-14.6-6-20.6-.9l-112.4 94.3c-9.1 7.6-21.6 9.6-32.6 5.2L38.1 216.8c-13.1-5.3-21.7-18.1-21.7-32.3 0-14.7 9.2-27.8 23.1-32.8z"/></svg>
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"annotate": null, "base": "../..", "features": ["announce.dismiss", "content.action.edit", "content.action.view", "content.code.annotate", "content.code.copy", "content.tooltips", "navigation.expand", "navigation.footer", "navigation.indexes", "navigation.path", "navigation.prune", "navigation.sections", "navigation.tabs", "navigation.tabs.sticky", "navigation.top", "navigation.tracking", "search.highlight", "search.share", "search.suggest", "toc.follow"], "search": "../../assets/javascripts/workers/search.2c215733.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": null}</script>
<script src="../../assets/javascripts/bundle.79ae519e.min.js"></script>
<script src="../../javascripts/home.js"></script>
<script src="../../javascripts/github-widget.js"></script>
<script src="../../javascripts/gitea-widget.js"></script>
<script src="../../assets/js/env-config.js"></script>
<script src="../../assets/js/video-player.js"></script>
</body>
</html>