7668 lines
243 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/development/local-setup/">
<link rel="prev" href="../">
<link rel="next" href="../docker-workflow/">
<link rel="icon" href="../../../assets/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.1">
<title>Local Setup - 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="Local Setup - 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/development/local-setup.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/development/local-setup/" />
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:title" content="Local Setup - 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/development/local-setup.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="#local-development-setup" 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">
Local Setup
</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--active md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_9" checked>
<div class="md-nav__link md-nav__container">
<a href="../" 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="true">
<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 md-nav__item--active">
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
<label class="md-nav__link md-nav__link--active" for="__toc">
<span class="md-ellipsis">
Local Setup
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<a href="./" class="md-nav__link md-nav__link--active">
<span class="md-ellipsis">
Local Setup
</span>
</a>
<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>
</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="#required-software" class="md-nav__link">
<span class="md-ellipsis">
Required Software
</span>
</a>
<nav class="md-nav" aria-label="Required Software">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#nodejs-and-npm" class="md-nav__link">
<span class="md-ellipsis">
Node.js and npm
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#docker-and-docker-compose" class="md-nav__link">
<span class="md-ellipsis">
Docker and Docker Compose
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#git" class="md-nav__link">
<span class="md-ellipsis">
Git
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#optional-tools" class="md-nav__link">
<span class="md-ellipsis">
Optional Tools
</span>
</a>
<nav class="md-nav" aria-label="Optional Tools">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#postgresql-client-tools" class="md-nav__link">
<span class="md-ellipsis">
PostgreSQL Client Tools
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#redis-cli" class="md-nav__link">
<span class="md-ellipsis">
Redis CLI
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#visual-studio-code" class="md-nav__link">
<span class="md-ellipsis">
Visual Studio Code
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#system-requirements" class="md-nav__link">
<span class="md-ellipsis">
System Requirements
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#repository-setup" class="md-nav__link">
<span class="md-ellipsis">
Repository Setup
</span>
</a>
<nav class="md-nav" aria-label="Repository Setup">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#clone-repository" class="md-nav__link">
<span class="md-ellipsis">
Clone Repository
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#repository-structure" class="md-nav__link">
<span class="md-ellipsis">
Repository Structure
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#verify-files" class="md-nav__link">
<span class="md-ellipsis">
Verify Files
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#environment-configuration" class="md-nav__link">
<span class="md-ellipsis">
Environment Configuration
</span>
</a>
<nav class="md-nav" aria-label="Environment Configuration">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#create-env-file" class="md-nav__link">
<span class="md-ellipsis">
Create .env File
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#configure-essential-variables" class="md-nav__link">
<span class="md-ellipsis">
Configure Essential Variables
</span>
</a>
<nav class="md-nav" aria-label="Configure Essential Variables">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#database-passwords" class="md-nav__link">
<span class="md-ellipsis">
Database Passwords
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#jwt-secrets" class="md-nav__link">
<span class="md-ellipsis">
JWT Secrets
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#email-configuration-development" class="md-nav__link">
<span class="md-ellipsis">
Email Configuration (Development)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#optional-features" class="md-nav__link">
<span class="md-ellipsis">
Optional Features
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#complete-env-template" class="md-nav__link">
<span class="md-ellipsis">
Complete .env Template
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#verify-configuration" class="md-nav__link">
<span class="md-ellipsis">
Verify Configuration
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#database-setup" class="md-nav__link">
<span class="md-ellipsis">
Database Setup
</span>
</a>
<nav class="md-nav" aria-label="Database Setup">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#start-database-services" class="md-nav__link">
<span class="md-ellipsis">
Start Database Services
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#verify-database-connection" class="md-nav__link">
<span class="md-ellipsis">
Verify Database Connection
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#install-api-dependencies" class="md-nav__link">
<span class="md-ellipsis">
Install API Dependencies
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#run-database-migrations" class="md-nav__link">
<span class="md-ellipsis">
Run Database Migrations
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#seed-database" class="md-nav__link">
<span class="md-ellipsis">
Seed Database
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#verify-database-schema" class="md-nav__link">
<span class="md-ellipsis">
Verify Database Schema
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#return-to-project-root" class="md-nav__link">
<span class="md-ellipsis">
Return to Project Root
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#starting-services" class="md-nav__link">
<span class="md-ellipsis">
Starting Services
</span>
</a>
<nav class="md-nav" aria-label="Starting Services">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#option-1-docker-based-development-recommended" class="md-nav__link">
<span class="md-ellipsis">
Option 1: Docker-based Development (Recommended)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#option-2-local-npm-development" class="md-nav__link">
<span class="md-ellipsis">
Option 2: Local npm Development
</span>
</a>
<nav class="md-nav" aria-label="Option 2: Local npm Development">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#terminal-1-api-server" class="md-nav__link">
<span class="md-ellipsis">
Terminal 1: API Server
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#terminal-2-admin-server" class="md-nav__link">
<span class="md-ellipsis">
Terminal 2: Admin Server
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#terminal-3-media-api-optional" class="md-nav__link">
<span class="md-ellipsis">
Terminal 3: Media API (Optional)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#background-services" class="md-nav__link">
<span class="md-ellipsis">
Background Services
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#which-approach-to-use" class="md-nav__link">
<span class="md-ellipsis">
Which Approach to Use?
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#verifying-setup" class="md-nav__link">
<span class="md-ellipsis">
Verifying Setup
</span>
</a>
<nav class="md-nav" aria-label="Verifying Setup">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#health-check-endpoints" class="md-nav__link">
<span class="md-ellipsis">
Health Check Endpoints
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#test-authentication" class="md-nav__link">
<span class="md-ellipsis">
Test Authentication
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#login-to-admin-gui" class="md-nav__link">
<span class="md-ellipsis">
Login to Admin GUI
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#verify-database-connection_1" class="md-nav__link">
<span class="md-ellipsis">
Verify Database Connection
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#test-email-capture-mailhog" class="md-nav__link">
<span class="md-ellipsis">
Test Email Capture (MailHog)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#ide-setup" class="md-nav__link">
<span class="md-ellipsis">
IDE Setup
</span>
</a>
<nav class="md-nav" aria-label="IDE Setup">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#visual-studio-code_1" class="md-nav__link">
<span class="md-ellipsis">
Visual Studio Code
</span>
</a>
<nav class="md-nav" aria-label="Visual Studio Code">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#recommended-extensions" class="md-nav__link">
<span class="md-ellipsis">
Recommended Extensions
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#workspace-settings" class="md-nav__link">
<span class="md-ellipsis">
Workspace Settings
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#launch-configuration" class="md-nav__link">
<span class="md-ellipsis">
Launch Configuration
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#workspace-file" class="md-nav__link">
<span class="md-ellipsis">
Workspace File
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#other-ides" class="md-nav__link">
<span class="md-ellipsis">
Other IDEs
</span>
</a>
<nav class="md-nav" aria-label="Other IDEs">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#webstorm-intellij-idea" class="md-nav__link">
<span class="md-ellipsis">
WebStorm / IntelliJ IDEA
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#neovim-vim" class="md-nav__link">
<span class="md-ellipsis">
Neovim / Vim
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#troubleshooting" class="md-nav__link">
<span class="md-ellipsis">
Troubleshooting
</span>
</a>
<nav class="md-nav" aria-label="Troubleshooting">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#port-conflicts" class="md-nav__link">
<span class="md-ellipsis">
Port Conflicts
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#database-connection-errors" class="md-nav__link">
<span class="md-ellipsis">
Database Connection Errors
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#redis-connection-errors" class="md-nav__link">
<span class="md-ellipsis">
Redis Connection Errors
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#migration-errors" class="md-nav__link">
<span class="md-ellipsis">
Migration Errors
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#npm-install-failures" class="md-nav__link">
<span class="md-ellipsis">
npm Install Failures
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#hot-reload-not-working" class="md-nav__link">
<span class="md-ellipsis">
Hot Reload Not Working
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#admin-build-errors" class="md-nav__link">
<span class="md-ellipsis">
Admin Build Errors
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#docker-container-crashes" class="md-nav__link">
<span class="md-ellipsis">
Docker Container Crashes
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#browser-cors-errors" class="md-nav__link">
<span class="md-ellipsis">
Browser CORS Errors
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#hot-reload" class="md-nav__link">
<span class="md-ellipsis">
Hot Reload
</span>
</a>
<nav class="md-nav" aria-label="Hot Reload">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#api-hot-reload-tsx-watch" class="md-nav__link">
<span class="md-ellipsis">
API Hot Reload (tsx watch)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#admin-hot-reload-vite-hmr" class="md-nav__link">
<span class="md-ellipsis">
Admin Hot Reload (Vite HMR)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#docker-hot-reload" class="md-nav__link">
<span class="md-ellipsis">
Docker Hot Reload
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#debugging" class="md-nav__link">
<span class="md-ellipsis">
Debugging
</span>
</a>
<nav class="md-nav" aria-label="Debugging">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#api-debugging-vscode" class="md-nav__link">
<span class="md-ellipsis">
API Debugging (VSCode)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#frontend-debugging-chrome-devtools" class="md-nav__link">
<span class="md-ellipsis">
Frontend Debugging (Chrome DevTools)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#zustand-devtools" class="md-nav__link">
<span class="md-ellipsis">
Zustand DevTools
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#common-workflows" class="md-nav__link">
<span class="md-ellipsis">
Common Workflows
</span>
</a>
<nav class="md-nav" aria-label="Common Workflows">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#starting-fresh-development-day" class="md-nav__link">
<span class="md-ellipsis">
Starting Fresh Development Day
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#feature-development-workflow" class="md-nav__link">
<span class="md-ellipsis">
Feature Development Workflow
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#database-schema-changes" class="md-nav__link">
<span class="md-ellipsis">
Database Schema Changes
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#bug-fixing-workflow" class="md-nav__link">
<span class="md-ellipsis">
Bug Fixing Workflow
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#switching-between-docker-and-local" class="md-nav__link">
<span class="md-ellipsis">
Switching Between Docker and Local
</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>
<li class="md-nav__item">
<a href="#related-documentation" class="md-nav__link">
<span class="md-ellipsis">
Related Documentation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#getting-help" class="md-nav__link">
<span class="md-ellipsis">
Getting Help
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#summary" class="md-nav__link">
<span class="md-ellipsis">
Summary
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../docker-workflow/" class="md-nav__link">
<span class="md-ellipsis">
Docker Workflow
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../git-workflow/" class="md-nav__link">
<span class="md-ellipsis">
Git Workflow
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../npm-commands/" class="md-nav__link">
<span class="md-ellipsis">
NPM Commands
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../migrations/" class="md-nav__link">
<span class="md-ellipsis">
Migrations
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../typescript/" class="md-nav__link">
<span class="md-ellipsis">
TypeScript
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../testing/" class="md-nav__link">
<span class="md-ellipsis">
Testing
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../debugging/" class="md-nav__link">
<span class="md-ellipsis">
Debugging
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../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--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_13" >
<div class="md-nav__link md-nav__container">
<a href="../../migration/" class="md-nav__link ">
<span class="md-ellipsis">
Migration
</span>
</a>
<label class="md-nav__link " 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="false">
<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="../../migration/feature-parity/" class="md-nav__link">
<span class="md-ellipsis">
Feature Parity
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../migration/breaking-changes/" class="md-nav__link">
<span class="md-ellipsis">
Breaking Changes
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../migration/api-changes/" class="md-nav__link">
<span class="md-ellipsis">
API Changes
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../migration/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>
</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="#required-software" class="md-nav__link">
<span class="md-ellipsis">
Required Software
</span>
</a>
<nav class="md-nav" aria-label="Required Software">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#nodejs-and-npm" class="md-nav__link">
<span class="md-ellipsis">
Node.js and npm
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#docker-and-docker-compose" class="md-nav__link">
<span class="md-ellipsis">
Docker and Docker Compose
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#git" class="md-nav__link">
<span class="md-ellipsis">
Git
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#optional-tools" class="md-nav__link">
<span class="md-ellipsis">
Optional Tools
</span>
</a>
<nav class="md-nav" aria-label="Optional Tools">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#postgresql-client-tools" class="md-nav__link">
<span class="md-ellipsis">
PostgreSQL Client Tools
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#redis-cli" class="md-nav__link">
<span class="md-ellipsis">
Redis CLI
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#visual-studio-code" class="md-nav__link">
<span class="md-ellipsis">
Visual Studio Code
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#system-requirements" class="md-nav__link">
<span class="md-ellipsis">
System Requirements
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#repository-setup" class="md-nav__link">
<span class="md-ellipsis">
Repository Setup
</span>
</a>
<nav class="md-nav" aria-label="Repository Setup">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#clone-repository" class="md-nav__link">
<span class="md-ellipsis">
Clone Repository
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#repository-structure" class="md-nav__link">
<span class="md-ellipsis">
Repository Structure
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#verify-files" class="md-nav__link">
<span class="md-ellipsis">
Verify Files
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#environment-configuration" class="md-nav__link">
<span class="md-ellipsis">
Environment Configuration
</span>
</a>
<nav class="md-nav" aria-label="Environment Configuration">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#create-env-file" class="md-nav__link">
<span class="md-ellipsis">
Create .env File
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#configure-essential-variables" class="md-nav__link">
<span class="md-ellipsis">
Configure Essential Variables
</span>
</a>
<nav class="md-nav" aria-label="Configure Essential Variables">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#database-passwords" class="md-nav__link">
<span class="md-ellipsis">
Database Passwords
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#jwt-secrets" class="md-nav__link">
<span class="md-ellipsis">
JWT Secrets
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#email-configuration-development" class="md-nav__link">
<span class="md-ellipsis">
Email Configuration (Development)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#optional-features" class="md-nav__link">
<span class="md-ellipsis">
Optional Features
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#complete-env-template" class="md-nav__link">
<span class="md-ellipsis">
Complete .env Template
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#verify-configuration" class="md-nav__link">
<span class="md-ellipsis">
Verify Configuration
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#database-setup" class="md-nav__link">
<span class="md-ellipsis">
Database Setup
</span>
</a>
<nav class="md-nav" aria-label="Database Setup">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#start-database-services" class="md-nav__link">
<span class="md-ellipsis">
Start Database Services
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#verify-database-connection" class="md-nav__link">
<span class="md-ellipsis">
Verify Database Connection
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#install-api-dependencies" class="md-nav__link">
<span class="md-ellipsis">
Install API Dependencies
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#run-database-migrations" class="md-nav__link">
<span class="md-ellipsis">
Run Database Migrations
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#seed-database" class="md-nav__link">
<span class="md-ellipsis">
Seed Database
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#verify-database-schema" class="md-nav__link">
<span class="md-ellipsis">
Verify Database Schema
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#return-to-project-root" class="md-nav__link">
<span class="md-ellipsis">
Return to Project Root
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#starting-services" class="md-nav__link">
<span class="md-ellipsis">
Starting Services
</span>
</a>
<nav class="md-nav" aria-label="Starting Services">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#option-1-docker-based-development-recommended" class="md-nav__link">
<span class="md-ellipsis">
Option 1: Docker-based Development (Recommended)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#option-2-local-npm-development" class="md-nav__link">
<span class="md-ellipsis">
Option 2: Local npm Development
</span>
</a>
<nav class="md-nav" aria-label="Option 2: Local npm Development">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#terminal-1-api-server" class="md-nav__link">
<span class="md-ellipsis">
Terminal 1: API Server
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#terminal-2-admin-server" class="md-nav__link">
<span class="md-ellipsis">
Terminal 2: Admin Server
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#terminal-3-media-api-optional" class="md-nav__link">
<span class="md-ellipsis">
Terminal 3: Media API (Optional)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#background-services" class="md-nav__link">
<span class="md-ellipsis">
Background Services
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#which-approach-to-use" class="md-nav__link">
<span class="md-ellipsis">
Which Approach to Use?
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#verifying-setup" class="md-nav__link">
<span class="md-ellipsis">
Verifying Setup
</span>
</a>
<nav class="md-nav" aria-label="Verifying Setup">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#health-check-endpoints" class="md-nav__link">
<span class="md-ellipsis">
Health Check Endpoints
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#test-authentication" class="md-nav__link">
<span class="md-ellipsis">
Test Authentication
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#login-to-admin-gui" class="md-nav__link">
<span class="md-ellipsis">
Login to Admin GUI
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#verify-database-connection_1" class="md-nav__link">
<span class="md-ellipsis">
Verify Database Connection
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#test-email-capture-mailhog" class="md-nav__link">
<span class="md-ellipsis">
Test Email Capture (MailHog)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#ide-setup" class="md-nav__link">
<span class="md-ellipsis">
IDE Setup
</span>
</a>
<nav class="md-nav" aria-label="IDE Setup">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#visual-studio-code_1" class="md-nav__link">
<span class="md-ellipsis">
Visual Studio Code
</span>
</a>
<nav class="md-nav" aria-label="Visual Studio Code">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#recommended-extensions" class="md-nav__link">
<span class="md-ellipsis">
Recommended Extensions
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#workspace-settings" class="md-nav__link">
<span class="md-ellipsis">
Workspace Settings
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#launch-configuration" class="md-nav__link">
<span class="md-ellipsis">
Launch Configuration
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#workspace-file" class="md-nav__link">
<span class="md-ellipsis">
Workspace File
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#other-ides" class="md-nav__link">
<span class="md-ellipsis">
Other IDEs
</span>
</a>
<nav class="md-nav" aria-label="Other IDEs">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#webstorm-intellij-idea" class="md-nav__link">
<span class="md-ellipsis">
WebStorm / IntelliJ IDEA
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#neovim-vim" class="md-nav__link">
<span class="md-ellipsis">
Neovim / Vim
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#troubleshooting" class="md-nav__link">
<span class="md-ellipsis">
Troubleshooting
</span>
</a>
<nav class="md-nav" aria-label="Troubleshooting">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#port-conflicts" class="md-nav__link">
<span class="md-ellipsis">
Port Conflicts
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#database-connection-errors" class="md-nav__link">
<span class="md-ellipsis">
Database Connection Errors
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#redis-connection-errors" class="md-nav__link">
<span class="md-ellipsis">
Redis Connection Errors
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#migration-errors" class="md-nav__link">
<span class="md-ellipsis">
Migration Errors
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#npm-install-failures" class="md-nav__link">
<span class="md-ellipsis">
npm Install Failures
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#hot-reload-not-working" class="md-nav__link">
<span class="md-ellipsis">
Hot Reload Not Working
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#admin-build-errors" class="md-nav__link">
<span class="md-ellipsis">
Admin Build Errors
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#docker-container-crashes" class="md-nav__link">
<span class="md-ellipsis">
Docker Container Crashes
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#browser-cors-errors" class="md-nav__link">
<span class="md-ellipsis">
Browser CORS Errors
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#hot-reload" class="md-nav__link">
<span class="md-ellipsis">
Hot Reload
</span>
</a>
<nav class="md-nav" aria-label="Hot Reload">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#api-hot-reload-tsx-watch" class="md-nav__link">
<span class="md-ellipsis">
API Hot Reload (tsx watch)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#admin-hot-reload-vite-hmr" class="md-nav__link">
<span class="md-ellipsis">
Admin Hot Reload (Vite HMR)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#docker-hot-reload" class="md-nav__link">
<span class="md-ellipsis">
Docker Hot Reload
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#debugging" class="md-nav__link">
<span class="md-ellipsis">
Debugging
</span>
</a>
<nav class="md-nav" aria-label="Debugging">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#api-debugging-vscode" class="md-nav__link">
<span class="md-ellipsis">
API Debugging (VSCode)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#frontend-debugging-chrome-devtools" class="md-nav__link">
<span class="md-ellipsis">
Frontend Debugging (Chrome DevTools)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#zustand-devtools" class="md-nav__link">
<span class="md-ellipsis">
Zustand DevTools
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#common-workflows" class="md-nav__link">
<span class="md-ellipsis">
Common Workflows
</span>
</a>
<nav class="md-nav" aria-label="Common Workflows">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#starting-fresh-development-day" class="md-nav__link">
<span class="md-ellipsis">
Starting Fresh Development Day
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#feature-development-workflow" class="md-nav__link">
<span class="md-ellipsis">
Feature Development Workflow
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#database-schema-changes" class="md-nav__link">
<span class="md-ellipsis">
Database Schema Changes
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#bug-fixing-workflow" class="md-nav__link">
<span class="md-ellipsis">
Bug Fixing Workflow
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#switching-between-docker-and-local" class="md-nav__link">
<span class="md-ellipsis">
Switching Between Docker and Local
</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>
<li class="md-nav__item">
<a href="#related-documentation" class="md-nav__link">
<span class="md-ellipsis">
Related Documentation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#getting-help" class="md-nav__link">
<span class="md-ellipsis">
Getting Help
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#summary" class="md-nav__link">
<span class="md-ellipsis">
Summary
</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">
Development
</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/development/local-setup.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/development/local-setup.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="local-development-setup">Local Development Setup<a class="headerlink" href="#local-development-setup" title="Permanent link">&para;</a></h1>
<p>This guide walks you through setting up Changemaker Lite V2 for local development on your machine.</p>
<h2 id="overview">Overview<a class="headerlink" href="#overview" title="Permanent link">&para;</a></h2>
<p>Changemaker Lite V2 supports two development approaches:</p>
<ol>
<li><strong>Docker-based development</strong> - Run API and Admin in containers (recommended for consistency)</li>
<li><strong>Local npm development</strong> - Run services directly on your host machine (faster hot reload)</li>
</ol>
<p>This guide covers both approaches. Choose the one that fits your workflow.</p>
<h2 id="prerequisites">Prerequisites<a class="headerlink" href="#prerequisites" title="Permanent link">&para;</a></h2>
<h3 id="required-software">Required Software<a class="headerlink" href="#required-software" title="Permanent link">&para;</a></h3>
<h4 id="nodejs-and-npm">Node.js and npm<a class="headerlink" href="#nodejs-and-npm" title="Permanent link">&para;</a></h4>
<ul>
<li><strong>Node.js 20.x LTS</strong> or higher</li>
<li><strong>npm 10.x</strong> or higher</li>
</ul>
<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"># Check versions</span>
</span><span id="__span-0-2"><a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a>node<span class="w"> </span>--version<span class="w"> </span><span class="c1"># Should be v20.x.x or higher</span>
</span><span id="__span-0-3"><a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a>npm<span class="w"> </span>--version<span class="w"> </span><span class="c1"># Should be 10.x.x or higher</span>
</span></code></pre></div>
<p><strong>Installation:</strong>
- Download from <a href="https://nodejs.org/">nodejs.org</a>
- Or use <a href="https://github.com/nvm-sh/nvm">nvm</a> for version management:</p>
<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>nvm<span class="w"> </span>install<span class="w"> </span><span class="m">20</span>
</span><span id="__span-1-2"><a id="__codelineno-1-2" name="__codelineno-1-2" href="#__codelineno-1-2"></a>nvm<span class="w"> </span>use<span class="w"> </span><span class="m">20</span>
</span></code></pre></div>
<h4 id="docker-and-docker-compose">Docker and Docker Compose<a class="headerlink" href="#docker-and-docker-compose" title="Permanent link">&para;</a></h4>
<ul>
<li><strong>Docker Engine 24.x</strong> or higher</li>
<li><strong>Docker Compose 2.x</strong> or higher (included with Docker Desktop)</li>
</ul>
<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"># Check versions</span>
</span><span id="__span-2-2"><a id="__codelineno-2-2" name="__codelineno-2-2" href="#__codelineno-2-2"></a>docker<span class="w"> </span>--version<span class="w"> </span><span class="c1"># Should be 24.x.x or higher</span>
</span><span id="__span-2-3"><a id="__codelineno-2-3" name="__codelineno-2-3" href="#__codelineno-2-3"></a>docker<span class="w"> </span>compose<span class="w"> </span>version<span class="w"> </span><span class="c1"># Should be 2.x.x or higher</span>
</span></code></pre></div>
<p><strong>Installation:</strong>
- Docker Desktop: <a href="https://www.docker.com/get-started">docker.com/get-started</a>
- Linux: <a href="https://docs.docker.com/engine/install/">docs.docker.com/engine/install</a></p>
<h4 id="git">Git<a class="headerlink" href="#git" title="Permanent link">&para;</a></h4>
<ul>
<li><strong>Git 2.30</strong> or higher</li>
</ul>
<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><span class="c1"># Check version</span>
</span><span id="__span-3-2"><a id="__codelineno-3-2" name="__codelineno-3-2" href="#__codelineno-3-2"></a>git<span class="w"> </span>--version<span class="w"> </span><span class="c1"># Should be 2.30.x or higher</span>
</span></code></pre></div>
<p><strong>Installation:</strong>
- Download from <a href="https://git-scm.com/">git-scm.com</a>
- Or use package manager (apt, brew, etc.)</p>
<h3 id="optional-tools">Optional Tools<a class="headerlink" href="#optional-tools" title="Permanent link">&para;</a></h3>
<h4 id="postgresql-client-tools">PostgreSQL Client Tools<a class="headerlink" href="#postgresql-client-tools" title="Permanent link">&para;</a></h4>
<p>Useful for database inspection and debugging:</p>
<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><span class="c1"># Ubuntu/Debian</span>
</span><span id="__span-4-2"><a id="__codelineno-4-2" name="__codelineno-4-2" href="#__codelineno-4-2"></a>sudo<span class="w"> </span>apt<span class="w"> </span>install<span class="w"> </span>postgresql-client
</span><span id="__span-4-3"><a id="__codelineno-4-3" name="__codelineno-4-3" href="#__codelineno-4-3"></a>
</span><span id="__span-4-4"><a id="__codelineno-4-4" name="__codelineno-4-4" href="#__codelineno-4-4"></a><span class="c1"># macOS</span>
</span><span id="__span-4-5"><a id="__codelineno-4-5" name="__codelineno-4-5" href="#__codelineno-4-5"></a>brew<span class="w"> </span>install<span class="w"> </span>postgresql@16
</span><span id="__span-4-6"><a id="__codelineno-4-6" name="__codelineno-4-6" href="#__codelineno-4-6"></a>
</span><span id="__span-4-7"><a id="__codelineno-4-7" name="__codelineno-4-7" href="#__codelineno-4-7"></a><span class="c1"># Check installation</span>
</span><span id="__span-4-8"><a id="__codelineno-4-8" name="__codelineno-4-8" href="#__codelineno-4-8"></a>psql<span class="w"> </span>--version
</span></code></pre></div>
<h4 id="redis-cli">Redis CLI<a class="headerlink" href="#redis-cli" title="Permanent link">&para;</a></h4>
<p>For cache/queue debugging:</p>
<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"># Ubuntu/Debian</span>
</span><span id="__span-5-2"><a id="__codelineno-5-2" name="__codelineno-5-2" href="#__codelineno-5-2"></a>sudo<span class="w"> </span>apt<span class="w"> </span>install<span class="w"> </span>redis-tools
</span><span id="__span-5-3"><a id="__codelineno-5-3" name="__codelineno-5-3" href="#__codelineno-5-3"></a>
</span><span id="__span-5-4"><a id="__codelineno-5-4" name="__codelineno-5-4" href="#__codelineno-5-4"></a><span class="c1"># macOS</span>
</span><span id="__span-5-5"><a id="__codelineno-5-5" name="__codelineno-5-5" href="#__codelineno-5-5"></a>brew<span class="w"> </span>install<span class="w"> </span>redis
</span><span id="__span-5-6"><a id="__codelineno-5-6" name="__codelineno-5-6" href="#__codelineno-5-6"></a>
</span><span id="__span-5-7"><a id="__codelineno-5-7" name="__codelineno-5-7" href="#__codelineno-5-7"></a><span class="c1"># Check installation</span>
</span><span id="__span-5-8"><a id="__codelineno-5-8" name="__codelineno-5-8" href="#__codelineno-5-8"></a>redis-cli<span class="w"> </span>--version
</span></code></pre></div>
<h4 id="visual-studio-code">Visual Studio Code<a class="headerlink" href="#visual-studio-code" title="Permanent link">&para;</a></h4>
<p>Recommended IDE with excellent TypeScript support:</p>
<ul>
<li>Download from <a href="https://code.visualstudio.com/">code.visualstudio.com</a></li>
<li>See <a href="#ide-setup">IDE Setup</a> section for recommended extensions</li>
</ul>
<h3 id="system-requirements">System Requirements<a class="headerlink" href="#system-requirements" title="Permanent link">&para;</a></h3>
<p><strong>Minimum:</strong>
- 8 GB RAM
- 20 GB free disk space
- 2 CPU cores</p>
<p><strong>Recommended:</strong>
- 16 GB RAM
- 50 GB free disk space
- 4+ CPU cores</p>
<h2 id="repository-setup">Repository Setup<a class="headerlink" href="#repository-setup" title="Permanent link">&para;</a></h2>
<h3 id="clone-repository">Clone Repository<a class="headerlink" href="#clone-repository" title="Permanent link">&para;</a></h3>
<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"># Clone the repository</span>
</span><span id="__span-6-2"><a id="__codelineno-6-2" name="__codelineno-6-2" href="#__codelineno-6-2"></a>git<span class="w"> </span>clone<span class="w"> </span>&lt;repo-url&gt;<span class="w"> </span>changemaker.lite
</span><span id="__span-6-3"><a id="__codelineno-6-3" name="__codelineno-6-3" href="#__codelineno-6-3"></a><span class="nb">cd</span><span class="w"> </span>changemaker.lite
</span><span id="__span-6-4"><a id="__codelineno-6-4" name="__codelineno-6-4" href="#__codelineno-6-4"></a>
</span><span id="__span-6-5"><a id="__codelineno-6-5" name="__codelineno-6-5" href="#__codelineno-6-5"></a><span class="c1"># Checkout v2 branch</span>
</span><span id="__span-6-6"><a id="__codelineno-6-6" name="__codelineno-6-6" href="#__codelineno-6-6"></a>git<span class="w"> </span>checkout<span class="w"> </span>v2
</span><span id="__span-6-7"><a id="__codelineno-6-7" name="__codelineno-6-7" href="#__codelineno-6-7"></a>
</span><span id="__span-6-8"><a id="__codelineno-6-8" name="__codelineno-6-8" href="#__codelineno-6-8"></a><span class="c1"># Verify branch</span>
</span><span id="__span-6-9"><a id="__codelineno-6-9" name="__codelineno-6-9" href="#__codelineno-6-9"></a>git<span class="w"> </span>branch<span class="w"> </span>--show-current
</span><span id="__span-6-10"><a id="__codelineno-6-10" name="__codelineno-6-10" href="#__codelineno-6-10"></a><span class="c1"># Output: v2</span>
</span></code></pre></div>
<h3 id="repository-structure">Repository Structure<a class="headerlink" href="#repository-structure" title="Permanent link">&para;</a></h3>
<p>After cloning, your directory structure should look like:</p>
<div class="language-text highlight"><pre><span></span><code><span id="__span-7-1"><a id="__codelineno-7-1" name="__codelineno-7-1" href="#__codelineno-7-1"></a>changemaker.lite/
</span><span id="__span-7-2"><a id="__codelineno-7-2" name="__codelineno-7-2" href="#__codelineno-7-2"></a>├── api/ # Express.js + Fastify backend
</span><span id="__span-7-3"><a id="__codelineno-7-3" name="__codelineno-7-3" href="#__codelineno-7-3"></a>├── admin/ # React frontend
</span><span id="__span-7-4"><a id="__codelineno-7-4" name="__codelineno-7-4" href="#__codelineno-7-4"></a>├── configs/ # Monitoring configs (Prometheus, Grafana)
</span><span id="__span-7-5"><a id="__codelineno-7-5" name="__codelineno-7-5" href="#__codelineno-7-5"></a>├── nginx/ # Reverse proxy configuration
</span><span id="__span-7-6"><a id="__codelineno-7-6" name="__codelineno-7-6" href="#__codelineno-7-6"></a>├── scripts/ # Utility scripts
</span><span id="__span-7-7"><a id="__codelineno-7-7" name="__codelineno-7-7" href="#__codelineno-7-7"></a>├── docker-compose.yml # V2 orchestration
</span><span id="__span-7-8"><a id="__codelineno-7-8" name="__codelineno-7-8" href="#__codelineno-7-8"></a>├── .env.example # Environment template
</span><span id="__span-7-9"><a id="__codelineno-7-9" name="__codelineno-7-9" href="#__codelineno-7-9"></a>└── V2_PLAN.md # Development roadmap
</span></code></pre></div>
<h3 id="verify-files">Verify Files<a class="headerlink" href="#verify-files" title="Permanent link">&para;</a></h3>
<p>Check that key files exist:</p>
<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>ls<span class="w"> </span>-la<span class="w"> </span>api/package.json<span class="w"> </span>admin/package.json<span class="w"> </span>docker-compose.yml<span class="w"> </span>.env.example
</span></code></pre></div>
<p>If any files are missing, ensure you're on the <code>v2</code> branch.</p>
<h2 id="environment-configuration">Environment Configuration<a class="headerlink" href="#environment-configuration" title="Permanent link">&para;</a></h2>
<h3 id="create-env-file">Create .env File<a class="headerlink" href="#create-env-file" title="Permanent link">&para;</a></h3>
<p>Copy the example environment file:</p>
<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>cp<span class="w"> </span>.env.example<span class="w"> </span>.env
</span></code></pre></div>
<h3 id="configure-essential-variables">Configure Essential Variables<a class="headerlink" href="#configure-essential-variables" title="Permanent link">&para;</a></h3>
<p>Open <code>.env</code> in your editor and set the following critical variables:</p>
<h4 id="database-passwords">Database Passwords<a class="headerlink" href="#database-passwords" title="Permanent link">&para;</a></h4>
<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"># PostgreSQL password (use a strong password)</span>
</span><span id="__span-10-2"><a id="__codelineno-10-2" name="__codelineno-10-2" href="#__codelineno-10-2"></a><span class="nv">V2_POSTGRES_PASSWORD</span><span class="o">=</span>your_strong_password_here
</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"># Redis password (use a strong password)</span>
</span><span id="__span-10-5"><a id="__codelineno-10-5" name="__codelineno-10-5" href="#__codelineno-10-5"></a><span class="nv">REDIS_PASSWORD</span><span class="o">=</span>your_redis_password_here
</span></code></pre></div>
<h4 id="jwt-secrets">JWT Secrets<a class="headerlink" href="#jwt-secrets" title="Permanent link">&para;</a></h4>
<p>Generate secure random secrets:</p>
<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"># Generate secrets (run these commands separately)</span>
</span><span id="__span-11-2"><a id="__codelineno-11-2" name="__codelineno-11-2" href="#__codelineno-11-2"></a>openssl<span class="w"> </span>rand<span class="w"> </span>-hex<span class="w"> </span><span class="m">32</span><span class="w"> </span><span class="c1"># For JWT_ACCESS_SECRET</span>
</span><span id="__span-11-3"><a id="__codelineno-11-3" name="__codelineno-11-3" href="#__codelineno-11-3"></a>openssl<span class="w"> </span>rand<span class="w"> </span>-hex<span class="w"> </span><span class="m">32</span><span class="w"> </span><span class="c1"># For JWT_REFRESH_SECRET</span>
</span><span id="__span-11-4"><a id="__codelineno-11-4" name="__codelineno-11-4" href="#__codelineno-11-4"></a>openssl<span class="w"> </span>rand<span class="w"> </span>-hex<span class="w"> </span><span class="m">32</span><span class="w"> </span><span class="c1"># For ENCRYPTION_KEY</span>
</span></code></pre></div>
<p>Add to <code>.env</code>:</p>
<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"># JWT secrets (use different values for each!)</span>
</span><span id="__span-12-2"><a id="__codelineno-12-2" name="__codelineno-12-2" href="#__codelineno-12-2"></a><span class="nv">JWT_ACCESS_SECRET</span><span class="o">=</span>&lt;output<span class="w"> </span>from<span class="w"> </span>first<span class="w"> </span>command&gt;
</span><span id="__span-12-3"><a id="__codelineno-12-3" name="__codelineno-12-3" href="#__codelineno-12-3"></a><span class="nv">JWT_REFRESH_SECRET</span><span class="o">=</span>&lt;output<span class="w"> </span>from<span class="w"> </span>second<span class="w"> </span>command&gt;
</span><span id="__span-12-4"><a id="__codelineno-12-4" name="__codelineno-12-4" href="#__codelineno-12-4"></a><span class="nv">ENCRYPTION_KEY</span><span class="o">=</span>&lt;output<span class="w"> </span>from<span class="w"> </span>third<span class="w"> </span>command&gt;
</span></code></pre></div>
<p><strong>IMPORTANT:</strong> All three secrets must be different values!</p>
<h4 id="email-configuration-development">Email Configuration (Development)<a class="headerlink" href="#email-configuration-development" title="Permanent link">&para;</a></h4>
<p>For development, use MailHog to capture emails locally:</p>
<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"># Email test mode (sends to MailHog instead of real SMTP)</span>
</span><span id="__span-13-2"><a id="__codelineno-13-2" name="__codelineno-13-2" href="#__codelineno-13-2"></a><span class="nv">EMAIL_TEST_MODE</span><span class="o">=</span><span class="nb">true</span>
</span><span id="__span-13-3"><a id="__codelineno-13-3" name="__codelineno-13-3" href="#__codelineno-13-3"></a>
</span><span id="__span-13-4"><a id="__codelineno-13-4" name="__codelineno-13-4" href="#__codelineno-13-4"></a><span class="c1"># MailHog SMTP settings</span>
</span><span id="__span-13-5"><a id="__codelineno-13-5" name="__codelineno-13-5" href="#__codelineno-13-5"></a><span class="nv">EMAIL_SMTP_HOST</span><span class="o">=</span>localhost
</span><span id="__span-13-6"><a id="__codelineno-13-6" name="__codelineno-13-6" href="#__codelineno-13-6"></a><span class="nv">EMAIL_SMTP_PORT</span><span class="o">=</span><span class="m">1025</span>
</span><span id="__span-13-7"><a id="__codelineno-13-7" name="__codelineno-13-7" href="#__codelineno-13-7"></a><span class="nv">EMAIL_SMTP_SECURE</span><span class="o">=</span><span class="nb">false</span>
</span><span id="__span-13-8"><a id="__codelineno-13-8" name="__codelineno-13-8" href="#__codelineno-13-8"></a><span class="nv">EMAIL_FROM_ADDRESS</span><span class="o">=</span>noreply@cmlite.org
</span><span id="__span-13-9"><a id="__codelineno-13-9" name="__codelineno-13-9" href="#__codelineno-13-9"></a><span class="nv">EMAIL_FROM_NAME</span><span class="o">=</span>Changemaker<span class="w"> </span>Lite
</span></code></pre></div>
<h4 id="optional-features">Optional Features<a class="headerlink" href="#optional-features" title="Permanent link">&para;</a></h4>
<p>Enable optional features as needed:</p>
<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"># Media Manager (video library)</span>
</span><span id="__span-14-2"><a id="__codelineno-14-2" name="__codelineno-14-2" href="#__codelineno-14-2"></a><span class="nv">ENABLE_MEDIA_FEATURES</span><span class="o">=</span><span class="nb">true</span>
</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"># Listmonk newsletter sync</span>
</span><span id="__span-14-5"><a id="__codelineno-14-5" name="__codelineno-14-5" href="#__codelineno-14-5"></a><span class="nv">LISTMONK_SYNC_ENABLED</span><span class="o">=</span><span class="nb">false</span><span class="w"> </span><span class="c1"># Enable later if needed</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"># API ports (defaults work for most setups)</span>
</span><span id="__span-14-8"><a id="__codelineno-14-8" name="__codelineno-14-8" href="#__codelineno-14-8"></a><span class="nv">API_PORT</span><span class="o">=</span><span class="m">4000</span>
</span><span id="__span-14-9"><a id="__codelineno-14-9" name="__codelineno-14-9" href="#__codelineno-14-9"></a><span class="nv">ADMIN_PORT</span><span class="o">=</span><span class="m">3000</span>
</span><span id="__span-14-10"><a id="__codelineno-14-10" name="__codelineno-14-10" href="#__codelineno-14-10"></a><span class="nv">MEDIA_API_PORT</span><span class="o">=</span><span class="m">4100</span>
</span></code></pre></div>
<h3 id="complete-env-template">Complete .env Template<a class="headerlink" href="#complete-env-template" title="Permanent link">&para;</a></h3>
<p>Here's a minimal <code>.env</code> for local development:</p>
<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"># Database</span>
</span><span id="__span-15-2"><a id="__codelineno-15-2" name="__codelineno-15-2" href="#__codelineno-15-2"></a><span class="nv">V2_POSTGRES_PASSWORD</span><span class="o">=</span>your_strong_password
</span><span id="__span-15-3"><a id="__codelineno-15-3" name="__codelineno-15-3" href="#__codelineno-15-3"></a><span class="nv">DATABASE_URL</span><span class="o">=</span>postgresql://changemaker_v2:your_strong_password@localhost:5433/changemaker_v2_db
</span><span id="__span-15-4"><a id="__codelineno-15-4" name="__codelineno-15-4" href="#__codelineno-15-4"></a>
</span><span id="__span-15-5"><a id="__codelineno-15-5" name="__codelineno-15-5" href="#__codelineno-15-5"></a><span class="c1"># Redis</span>
</span><span id="__span-15-6"><a id="__codelineno-15-6" name="__codelineno-15-6" href="#__codelineno-15-6"></a><span class="nv">REDIS_PASSWORD</span><span class="o">=</span>your_redis_password
</span><span id="__span-15-7"><a id="__codelineno-15-7" name="__codelineno-15-7" href="#__codelineno-15-7"></a><span class="nv">REDIS_URL</span><span class="o">=</span>redis://:your_redis_password@localhost:6379
</span><span id="__span-15-8"><a id="__codelineno-15-8" name="__codelineno-15-8" href="#__codelineno-15-8"></a>
</span><span id="__span-15-9"><a id="__codelineno-15-9" name="__codelineno-15-9" href="#__codelineno-15-9"></a><span class="c1"># JWT</span>
</span><span id="__span-15-10"><a id="__codelineno-15-10" name="__codelineno-15-10" href="#__codelineno-15-10"></a><span class="nv">JWT_ACCESS_SECRET</span><span class="o">=</span>&lt;<span class="m">32</span>-byte<span class="w"> </span>hex<span class="w"> </span>from<span class="w"> </span>openssl&gt;
</span><span id="__span-15-11"><a id="__codelineno-15-11" name="__codelineno-15-11" href="#__codelineno-15-11"></a><span class="nv">JWT_REFRESH_SECRET</span><span class="o">=</span>&lt;<span class="m">32</span>-byte<span class="w"> </span>hex<span class="w"> </span>from<span class="w"> </span>openssl&gt;
</span><span id="__span-15-12"><a id="__codelineno-15-12" name="__codelineno-15-12" href="#__codelineno-15-12"></a><span class="nv">ENCRYPTION_KEY</span><span class="o">=</span>&lt;<span class="m">32</span>-byte<span class="w"> </span>hex<span class="w"> </span>from<span class="w"> </span>openssl&gt;
</span><span id="__span-15-13"><a id="__codelineno-15-13" name="__codelineno-15-13" href="#__codelineno-15-13"></a>
</span><span id="__span-15-14"><a id="__codelineno-15-14" name="__codelineno-15-14" href="#__codelineno-15-14"></a><span class="c1"># Email (MailHog for dev)</span>
</span><span id="__span-15-15"><a id="__codelineno-15-15" name="__codelineno-15-15" href="#__codelineno-15-15"></a><span class="nv">EMAIL_TEST_MODE</span><span class="o">=</span><span class="nb">true</span>
</span><span id="__span-15-16"><a id="__codelineno-15-16" name="__codelineno-15-16" href="#__codelineno-15-16"></a><span class="nv">EMAIL_SMTP_HOST</span><span class="o">=</span>localhost
</span><span id="__span-15-17"><a id="__codelineno-15-17" name="__codelineno-15-17" href="#__codelineno-15-17"></a><span class="nv">EMAIL_SMTP_PORT</span><span class="o">=</span><span class="m">1025</span>
</span><span id="__span-15-18"><a id="__codelineno-15-18" name="__codelineno-15-18" href="#__codelineno-15-18"></a><span class="nv">EMAIL_SMTP_SECURE</span><span class="o">=</span><span class="nb">false</span>
</span><span id="__span-15-19"><a id="__codelineno-15-19" name="__codelineno-15-19" href="#__codelineno-15-19"></a><span class="nv">EMAIL_FROM_ADDRESS</span><span class="o">=</span>noreply@cmlite.org
</span><span id="__span-15-20"><a id="__codelineno-15-20" name="__codelineno-15-20" href="#__codelineno-15-20"></a><span class="nv">EMAIL_FROM_NAME</span><span class="o">=</span>Changemaker<span class="w"> </span>Lite
</span><span id="__span-15-21"><a id="__codelineno-15-21" name="__codelineno-15-21" href="#__codelineno-15-21"></a>
</span><span id="__span-15-22"><a id="__codelineno-15-22" name="__codelineno-15-22" href="#__codelineno-15-22"></a><span class="c1"># Ports</span>
</span><span id="__span-15-23"><a id="__codelineno-15-23" name="__codelineno-15-23" href="#__codelineno-15-23"></a><span class="nv">API_PORT</span><span class="o">=</span><span class="m">4000</span>
</span><span id="__span-15-24"><a id="__codelineno-15-24" name="__codelineno-15-24" href="#__codelineno-15-24"></a><span class="nv">ADMIN_PORT</span><span class="o">=</span><span class="m">3000</span>
</span><span id="__span-15-25"><a id="__codelineno-15-25" name="__codelineno-15-25" href="#__codelineno-15-25"></a><span class="nv">MEDIA_API_PORT</span><span class="o">=</span><span class="m">4100</span>
</span><span id="__span-15-26"><a id="__codelineno-15-26" name="__codelineno-15-26" href="#__codelineno-15-26"></a>
</span><span id="__span-15-27"><a id="__codelineno-15-27" name="__codelineno-15-27" href="#__codelineno-15-27"></a><span class="c1"># Features</span>
</span><span id="__span-15-28"><a id="__codelineno-15-28" name="__codelineno-15-28" href="#__codelineno-15-28"></a><span class="nv">ENABLE_MEDIA_FEATURES</span><span class="o">=</span><span class="nb">true</span>
</span><span id="__span-15-29"><a id="__codelineno-15-29" name="__codelineno-15-29" href="#__codelineno-15-29"></a><span class="nv">LISTMONK_SYNC_ENABLED</span><span class="o">=</span><span class="nb">false</span>
</span><span id="__span-15-30"><a id="__codelineno-15-30" name="__codelineno-15-30" href="#__codelineno-15-30"></a>
</span><span id="__span-15-31"><a id="__codelineno-15-31" name="__codelineno-15-31" href="#__codelineno-15-31"></a><span class="c1"># Node environment</span>
</span><span id="__span-15-32"><a id="__codelineno-15-32" name="__codelineno-15-32" href="#__codelineno-15-32"></a><span class="nv">NODE_ENV</span><span class="o">=</span>development
</span></code></pre></div>
<h3 id="verify-configuration">Verify Configuration<a class="headerlink" href="#verify-configuration" title="Permanent link">&para;</a></h3>
<p>Check that required variables are set:</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-16-1"><a id="__codelineno-16-1" name="__codelineno-16-1" href="#__codelineno-16-1"></a>grep<span class="w"> </span>-E<span class="w"> </span><span class="s1">&#39;^(V2_POSTGRES_PASSWORD|REDIS_PASSWORD|JWT_ACCESS_SECRET|JWT_REFRESH_SECRET|ENCRYPTION_KEY)=&#39;</span><span class="w"> </span>.env
</span></code></pre></div>
<p>You should see 5 lines with non-empty values.</p>
<h2 id="database-setup">Database Setup<a class="headerlink" href="#database-setup" title="Permanent link">&para;</a></h2>
<h3 id="start-database-services">Start Database Services<a class="headerlink" href="#start-database-services" title="Permanent link">&para;</a></h3>
<p>Start PostgreSQL and Redis containers:</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-17-1"><a id="__codelineno-17-1" name="__codelineno-17-1" href="#__codelineno-17-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></code></pre></div>
<p><strong>Wait for databases to initialize</strong> (first run takes 30-60 seconds):</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-18-1"><a id="__codelineno-18-1" name="__codelineno-18-1" href="#__codelineno-18-1"></a><span class="c1"># Watch logs</span>
</span><span id="__span-18-2"><a id="__codelineno-18-2" name="__codelineno-18-2" href="#__codelineno-18-2"></a>docker<span class="w"> </span>compose<span class="w"> </span>logs<span class="w"> </span>-f<span class="w"> </span>v2-postgres<span class="w"> </span>redis
</span><span id="__span-18-3"><a id="__codelineno-18-3" name="__codelineno-18-3" href="#__codelineno-18-3"></a>
</span><span id="__span-18-4"><a id="__codelineno-18-4" name="__codelineno-18-4" href="#__codelineno-18-4"></a><span class="c1"># Look for:</span>
</span><span id="__span-18-5"><a id="__codelineno-18-5" name="__codelineno-18-5" href="#__codelineno-18-5"></a><span class="c1"># v2-postgres: &quot;database system is ready to accept connections&quot;</span>
</span><span id="__span-18-6"><a id="__codelineno-18-6" name="__codelineno-18-6" href="#__codelineno-18-6"></a><span class="c1"># redis: &quot;Ready to accept connections&quot;</span>
</span><span id="__span-18-7"><a id="__codelineno-18-7" name="__codelineno-18-7" href="#__codelineno-18-7"></a>
</span><span id="__span-18-8"><a id="__codelineno-18-8" name="__codelineno-18-8" href="#__codelineno-18-8"></a><span class="c1"># Press Ctrl+C to exit logs</span>
</span></code></pre></div>
<h3 id="verify-database-connection">Verify Database Connection<a class="headerlink" href="#verify-database-connection" title="Permanent link">&para;</a></h3>
<p>Test PostgreSQL connection:</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-19-1"><a id="__codelineno-19-1" name="__codelineno-19-1" href="#__codelineno-19-1"></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_v2<span class="w"> </span>-d<span class="w"> </span>changemaker_v2_db<span class="w"> </span>-c<span class="w"> </span><span class="s2">&quot;SELECT version();&quot;</span>
</span></code></pre></div>
<p>You should see PostgreSQL version information.</p>
<p>Test Redis connection:</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-20-1"><a id="__codelineno-20-1" name="__codelineno-20-1" href="#__codelineno-20-1"></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>-a<span class="w"> </span>your_redis_password<span class="w"> </span>ping
</span><span id="__span-20-2"><a id="__codelineno-20-2" name="__codelineno-20-2" href="#__codelineno-20-2"></a><span class="c1"># Output: PONG</span>
</span></code></pre></div>
<h3 id="install-api-dependencies">Install API Dependencies<a class="headerlink" href="#install-api-dependencies" title="Permanent link">&para;</a></h3>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-21-1"><a id="__codelineno-21-1" name="__codelineno-21-1" href="#__codelineno-21-1"></a><span class="nb">cd</span><span class="w"> </span>api
</span><span id="__span-21-2"><a id="__codelineno-21-2" name="__codelineno-21-2" href="#__codelineno-21-2"></a>npm<span class="w"> </span>install
</span></code></pre></div>
<p><strong>Expected output:</strong>
- Installs ~300+ packages
- May show peer dependency warnings (safe to ignore)
- Should complete without errors</p>
<h3 id="run-database-migrations">Run Database Migrations<a class="headerlink" href="#run-database-migrations" title="Permanent link">&para;</a></h3>
<p>Apply Prisma migrations to create database schema:</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-22-1"><a id="__codelineno-22-1" name="__codelineno-22-1" href="#__codelineno-22-1"></a><span class="c1"># From api/ directory</span>
</span><span id="__span-22-2"><a id="__codelineno-22-2" name="__codelineno-22-2" href="#__codelineno-22-2"></a>npx<span class="w"> </span>prisma<span class="w"> </span>migrate<span class="w"> </span>deploy
</span></code></pre></div>
<p><strong>Expected output:</strong>
<div class="language-text highlight"><pre><span></span><code><span id="__span-23-1"><a id="__codelineno-23-1" name="__codelineno-23-1" href="#__codelineno-23-1"></a>Environment variables loaded from .env
</span><span id="__span-23-2"><a id="__codelineno-23-2" name="__codelineno-23-2" href="#__codelineno-23-2"></a>Prisma schema loaded from prisma/schema.prisma
</span><span id="__span-23-3"><a id="__codelineno-23-3" name="__codelineno-23-3" href="#__codelineno-23-3"></a>Datasource &quot;db&quot;: PostgreSQL database &quot;changemaker_v2_db&quot;
</span><span id="__span-23-4"><a id="__codelineno-23-4" name="__codelineno-23-4" href="#__codelineno-23-4"></a>
</span><span id="__span-23-5"><a id="__codelineno-23-5" name="__codelineno-23-5" href="#__codelineno-23-5"></a>20 migrations found in prisma/migrations
</span><span id="__span-23-6"><a id="__codelineno-23-6" name="__codelineno-23-6" href="#__codelineno-23-6"></a>
</span><span id="__span-23-7"><a id="__codelineno-23-7" name="__codelineno-23-7" href="#__codelineno-23-7"></a>Applying migration `20260101000000_init`
</span><span id="__span-23-8"><a id="__codelineno-23-8" name="__codelineno-23-8" href="#__codelineno-23-8"></a>Applying migration `20260105000000_add_campaigns`
</span><span id="__span-23-9"><a id="__codelineno-23-9" name="__codelineno-23-9" href="#__codelineno-23-9"></a>...
</span><span id="__span-23-10"><a id="__codelineno-23-10" name="__codelineno-23-10" href="#__codelineno-23-10"></a>All migrations have been successfully applied.
</span></code></pre></div></p>
<h3 id="seed-database">Seed Database<a class="headerlink" href="#seed-database" title="Permanent link">&para;</a></h3>
<p>Populate database with initial data (admin user, settings, etc.):</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-24-1"><a id="__codelineno-24-1" name="__codelineno-24-1" href="#__codelineno-24-1"></a><span class="c1"># From api/ directory</span>
</span><span id="__span-24-2"><a id="__codelineno-24-2" name="__codelineno-24-2" href="#__codelineno-24-2"></a>npx<span class="w"> </span>prisma<span class="w"> </span>db<span class="w"> </span>seed
</span></code></pre></div>
<p><strong>Expected output:</strong>
<div class="language-text highlight"><pre><span></span><code><span id="__span-25-1"><a id="__codelineno-25-1" name="__codelineno-25-1" href="#__codelineno-25-1"></a>Running seed command `tsx prisma/seed.ts` ...
</span><span id="__span-25-2"><a id="__codelineno-25-2" name="__codelineno-25-2" href="#__codelineno-25-2"></a>Seeding database...
</span><span id="__span-25-3"><a id="__codelineno-25-3" name="__codelineno-25-3" href="#__codelineno-25-3"></a>Created default settings
</span><span id="__span-25-4"><a id="__codelineno-25-4" name="__codelineno-25-4" href="#__codelineno-25-4"></a>Created admin user: admin@example.com
</span><span id="__span-25-5"><a id="__codelineno-25-5" name="__codelineno-25-5" href="#__codelineno-25-5"></a>Created 10 sample blocks
</span><span id="__span-25-6"><a id="__codelineno-25-6" name="__codelineno-25-6" href="#__codelineno-25-6"></a>...
</span><span id="__span-25-7"><a id="__codelineno-25-7" name="__codelineno-25-7" href="#__codelineno-25-7"></a>Seed completed successfully
</span></code></pre></div></p>
<p><strong>Default Admin Credentials:</strong>
- Email: <code>admin@example.com</code>
- Password: <code>Admin123!</code>
- <strong>Change this password immediately after first login!</strong></p>
<h3 id="verify-database-schema">Verify Database Schema<a class="headerlink" href="#verify-database-schema" title="Permanent link">&para;</a></h3>
<p>Open Prisma Studio to browse the database:</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-26-1"><a id="__codelineno-26-1" name="__codelineno-26-1" href="#__codelineno-26-1"></a><span class="c1"># From api/ directory</span>
</span><span id="__span-26-2"><a id="__codelineno-26-2" name="__codelineno-26-2" href="#__codelineno-26-2"></a>npx<span class="w"> </span>prisma<span class="w"> </span>studio
</span></code></pre></div>
<p>This opens a browser at <code>http://localhost:5555</code> showing:
- 30+ tables (User, Campaign, Location, Shift, etc.)
- Seeded data (1 admin user, settings, blocks)</p>
<p>Press Ctrl+C to close Prisma Studio when done.</p>
<h3 id="return-to-project-root">Return to Project Root<a class="headerlink" href="#return-to-project-root" title="Permanent link">&para;</a></h3>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-27-1"><a id="__codelineno-27-1" name="__codelineno-27-1" href="#__codelineno-27-1"></a><span class="nb">cd</span><span class="w"> </span>..<span class="w"> </span><span class="c1"># Back to changemaker.lite/</span>
</span></code></pre></div>
<h2 id="starting-services">Starting Services<a class="headerlink" href="#starting-services" title="Permanent link">&para;</a></h2>
<p>You have two options for running the development servers:</p>
<h3 id="option-1-docker-based-development-recommended">Option 1: Docker-based Development (Recommended)<a class="headerlink" href="#option-1-docker-based-development-recommended" title="Permanent link">&para;</a></h3>
<p>Run API and Admin in Docker containers with volume mounts for hot reload:</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-28-1"><a id="__codelineno-28-1" name="__codelineno-28-1" href="#__codelineno-28-1"></a><span class="c1"># Start API and Admin containers</span>
</span><span id="__span-28-2"><a id="__codelineno-28-2" name="__codelineno-28-2" href="#__codelineno-28-2"></a>docker<span class="w"> </span>compose<span class="w"> </span>up<span class="w"> </span>-d<span class="w"> </span>api<span class="w"> </span>admin
</span><span id="__span-28-3"><a id="__codelineno-28-3" name="__codelineno-28-3" href="#__codelineno-28-3"></a>
</span><span id="__span-28-4"><a id="__codelineno-28-4" name="__codelineno-28-4" href="#__codelineno-28-4"></a><span class="c1"># Optional: Start MailHog for email testing</span>
</span><span id="__span-28-5"><a id="__codelineno-28-5" name="__codelineno-28-5" href="#__codelineno-28-5"></a>docker<span class="w"> </span>compose<span class="w"> </span>up<span class="w"> </span>-d<span class="w"> </span>mailhog
</span><span id="__span-28-6"><a id="__codelineno-28-6" name="__codelineno-28-6" href="#__codelineno-28-6"></a>
</span><span id="__span-28-7"><a id="__codelineno-28-7" name="__codelineno-28-7" href="#__codelineno-28-7"></a><span class="c1"># Optional: Start Media API</span>
</span><span id="__span-28-8"><a id="__codelineno-28-8" name="__codelineno-28-8" href="#__codelineno-28-8"></a>docker<span class="w"> </span>compose<span class="w"> </span>up<span class="w"> </span>-d<span class="w"> </span>media-api
</span></code></pre></div>
<p><strong>Watch logs:</strong></p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-29-1"><a id="__codelineno-29-1" name="__codelineno-29-1" href="#__codelineno-29-1"></a><span class="c1"># All services</span>
</span><span id="__span-29-2"><a id="__codelineno-29-2" name="__codelineno-29-2" href="#__codelineno-29-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-29-3"><a id="__codelineno-29-3" name="__codelineno-29-3" href="#__codelineno-29-3"></a>
</span><span id="__span-29-4"><a id="__codelineno-29-4" name="__codelineno-29-4" href="#__codelineno-29-4"></a><span class="c1"># Just API</span>
</span><span id="__span-29-5"><a id="__codelineno-29-5" name="__codelineno-29-5" href="#__codelineno-29-5"></a>docker<span class="w"> </span>compose<span class="w"> </span>logs<span class="w"> </span>-f<span class="w"> </span>api
</span><span id="__span-29-6"><a id="__codelineno-29-6" name="__codelineno-29-6" href="#__codelineno-29-6"></a>
</span><span id="__span-29-7"><a id="__codelineno-29-7" name="__codelineno-29-7" href="#__codelineno-29-7"></a><span class="c1"># Just Admin</span>
</span><span id="__span-29-8"><a id="__codelineno-29-8" name="__codelineno-29-8" href="#__codelineno-29-8"></a>docker<span class="w"> </span>compose<span class="w"> </span>logs<span class="w"> </span>-f<span class="w"> </span>admin
</span></code></pre></div>
<p><strong>Verify services started:</strong></p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-30-1"><a id="__codelineno-30-1" name="__codelineno-30-1" href="#__codelineno-30-1"></a>docker<span class="w"> </span>compose<span class="w"> </span>ps
</span></code></pre></div>
<p>You should see:
- <code>api</code> - running on port 4000
- <code>admin</code> - running on port 3000
- <code>v2-postgres</code> - running on port 5433
- <code>redis</code> - running on port 6379
- <code>mailhog</code> - running on port 8025 (if started)</p>
<p><strong>Hot Reload in Docker:</strong></p>
<p>Volume mounts automatically sync code changes:
- API: <code>tsx watch</code> restarts server on file changes
- Admin: Vite HMR updates browser without full reload</p>
<h3 id="option-2-local-npm-development">Option 2: Local npm Development<a class="headerlink" href="#option-2-local-npm-development" title="Permanent link">&para;</a></h3>
<p>Run services directly on your host machine (faster hot reload):</p>
<h4 id="terminal-1-api-server">Terminal 1: API Server<a class="headerlink" href="#terminal-1-api-server" title="Permanent link">&para;</a></h4>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-31-1"><a id="__codelineno-31-1" name="__codelineno-31-1" href="#__codelineno-31-1"></a><span class="nb">cd</span><span class="w"> </span>api
</span><span id="__span-31-2"><a id="__codelineno-31-2" name="__codelineno-31-2" href="#__codelineno-31-2"></a>npm<span class="w"> </span>run<span class="w"> </span>dev
</span></code></pre></div>
<p><strong>Expected output:</strong>
<div class="language-text highlight"><pre><span></span><code><span id="__span-32-1"><a id="__codelineno-32-1" name="__codelineno-32-1" href="#__codelineno-32-1"></a>&gt; api@2.0.0 dev
</span><span id="__span-32-2"><a id="__codelineno-32-2" name="__codelineno-32-2" href="#__codelineno-32-2"></a>&gt; tsx watch src/server.ts
</span><span id="__span-32-3"><a id="__codelineno-32-3" name="__codelineno-32-3" href="#__codelineno-32-3"></a>
</span><span id="__span-32-4"><a id="__codelineno-32-4" name="__codelineno-32-4" href="#__codelineno-32-4"></a>Server running on port 4000
</span><span id="__span-32-5"><a id="__codelineno-32-5" name="__codelineno-32-5" href="#__codelineno-32-5"></a>Database connected
</span><span id="__span-32-6"><a id="__codelineno-32-6" name="__codelineno-32-6" href="#__codelineno-32-6"></a>Redis connected
</span><span id="__span-32-7"><a id="__codelineno-32-7" name="__codelineno-32-7" href="#__codelineno-32-7"></a>BullMQ worker started
</span></code></pre></div></p>
<h4 id="terminal-2-admin-server">Terminal 2: Admin Server<a class="headerlink" href="#terminal-2-admin-server" title="Permanent link">&para;</a></h4>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-33-1"><a id="__codelineno-33-1" name="__codelineno-33-1" href="#__codelineno-33-1"></a><span class="nb">cd</span><span class="w"> </span>admin
</span><span id="__span-33-2"><a id="__codelineno-33-2" name="__codelineno-33-2" href="#__codelineno-33-2"></a>npm<span class="w"> </span>install<span class="w"> </span><span class="c1"># First time only</span>
</span><span id="__span-33-3"><a id="__codelineno-33-3" name="__codelineno-33-3" href="#__codelineno-33-3"></a>npm<span class="w"> </span>run<span class="w"> </span>dev
</span></code></pre></div>
<p><strong>Expected output:</strong>
<div class="language-text highlight"><pre><span></span><code><span id="__span-34-1"><a id="__codelineno-34-1" name="__codelineno-34-1" href="#__codelineno-34-1"></a>&gt; admin@2.0.0 dev
</span><span id="__span-34-2"><a id="__codelineno-34-2" name="__codelineno-34-2" href="#__codelineno-34-2"></a>&gt; vite
</span><span id="__span-34-3"><a id="__codelineno-34-3" name="__codelineno-34-3" href="#__codelineno-34-3"></a>
</span><span id="__span-34-4"><a id="__codelineno-34-4" name="__codelineno-34-4" href="#__codelineno-34-4"></a> VITE v5.x.x ready in 500 ms
</span><span id="__span-34-5"><a id="__codelineno-34-5" name="__codelineno-34-5" href="#__codelineno-34-5"></a>
</span><span id="__span-34-6"><a id="__codelineno-34-6" name="__codelineno-34-6" href="#__codelineno-34-6"></a> ➜ Local: http://localhost:3000/
</span><span id="__span-34-7"><a id="__codelineno-34-7" name="__codelineno-34-7" href="#__codelineno-34-7"></a> ➜ Network: use --host to expose
</span></code></pre></div></p>
<h4 id="terminal-3-media-api-optional">Terminal 3: Media API (Optional)<a class="headerlink" href="#terminal-3-media-api-optional" title="Permanent link">&para;</a></h4>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-35-1"><a id="__codelineno-35-1" name="__codelineno-35-1" href="#__codelineno-35-1"></a><span class="nb">cd</span><span class="w"> </span>api
</span><span id="__span-35-2"><a id="__codelineno-35-2" name="__codelineno-35-2" href="#__codelineno-35-2"></a>npm<span class="w"> </span>run<span class="w"> </span>dev:media
</span></code></pre></div>
<p><strong>Expected output:</strong>
<div class="language-text highlight"><pre><span></span><code><span id="__span-36-1"><a id="__codelineno-36-1" name="__codelineno-36-1" href="#__codelineno-36-1"></a>&gt; api@2.0.0 dev:media
</span><span id="__span-36-2"><a id="__codelineno-36-2" name="__codelineno-36-2" href="#__codelineno-36-2"></a>&gt; tsx watch src/media-server.ts
</span><span id="__span-36-3"><a id="__codelineno-36-3" name="__codelineno-36-3" href="#__codelineno-36-3"></a>
</span><span id="__span-36-4"><a id="__codelineno-36-4" name="__codelineno-36-4" href="#__codelineno-36-4"></a>Media API server running on port 4100
</span><span id="__span-36-5"><a id="__codelineno-36-5" name="__codelineno-36-5" href="#__codelineno-36-5"></a>Database connected
</span></code></pre></div></p>
<h4 id="background-services">Background Services<a class="headerlink" href="#background-services" title="Permanent link">&para;</a></h4>
<p>You still need Docker for PostgreSQL, Redis, and MailHog:</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-37-1"><a id="__codelineno-37-1" name="__codelineno-37-1" href="#__codelineno-37-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 class="w"> </span>mailhog
</span></code></pre></div>
<h3 id="which-approach-to-use">Which Approach to Use?<a class="headerlink" href="#which-approach-to-use" title="Permanent link">&para;</a></h3>
<p><strong>Use Docker-based development if:</strong>
- You want consistent environment across team
- You're new to the project
- You prefer simpler setup</p>
<p><strong>Use local npm development if:</strong>
- You want faster hot reload (especially for frontend)
- You're actively developing API changes
- You prefer direct access to Node.js processes</p>
<p><strong>You can mix approaches:</strong>
- Run API in Docker, Admin locally
- Run databases in Docker, both API/Admin locally</p>
<h2 id="verifying-setup">Verifying Setup<a class="headerlink" href="#verifying-setup" title="Permanent link">&para;</a></h2>
<h3 id="health-check-endpoints">Health Check Endpoints<a class="headerlink" href="#health-check-endpoints" title="Permanent link">&para;</a></h3>
<p>Test that services are responding:</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-38-1"><a id="__codelineno-38-1" name="__codelineno-38-1" href="#__codelineno-38-1"></a><span class="c1"># API health check</span>
</span><span id="__span-38-2"><a id="__codelineno-38-2" name="__codelineno-38-2" href="#__codelineno-38-2"></a>curl<span class="w"> </span>http://localhost:4000/health
</span><span id="__span-38-3"><a id="__codelineno-38-3" name="__codelineno-38-3" href="#__codelineno-38-3"></a><span class="c1"># Expected: {&quot;status&quot;:&quot;ok&quot;,&quot;timestamp&quot;:&quot;2026-02-13T...&quot;}</span>
</span><span id="__span-38-4"><a id="__codelineno-38-4" name="__codelineno-38-4" href="#__codelineno-38-4"></a>
</span><span id="__span-38-5"><a id="__codelineno-38-5" name="__codelineno-38-5" href="#__codelineno-38-5"></a><span class="c1"># Admin (open in browser)</span>
</span><span id="__span-38-6"><a id="__codelineno-38-6" name="__codelineno-38-6" href="#__codelineno-38-6"></a>open<span class="w"> </span>http://localhost:3000
</span><span id="__span-38-7"><a id="__codelineno-38-7" name="__codelineno-38-7" href="#__codelineno-38-7"></a><span class="c1"># Or visit manually: http://localhost:3000</span>
</span><span id="__span-38-8"><a id="__codelineno-38-8" name="__codelineno-38-8" href="#__codelineno-38-8"></a>
</span><span id="__span-38-9"><a id="__codelineno-38-9" name="__codelineno-38-9" href="#__codelineno-38-9"></a><span class="c1"># Media API health check (if enabled)</span>
</span><span id="__span-38-10"><a id="__codelineno-38-10" name="__codelineno-38-10" href="#__codelineno-38-10"></a>curl<span class="w"> </span>http://localhost:4100/health
</span><span id="__span-38-11"><a id="__codelineno-38-11" name="__codelineno-38-11" href="#__codelineno-38-11"></a><span class="c1"># Expected: {&quot;status&quot;:&quot;ok&quot;,&quot;timestamp&quot;:&quot;2026-02-13T...&quot;}</span>
</span></code></pre></div>
<h3 id="test-authentication">Test Authentication<a class="headerlink" href="#test-authentication" title="Permanent link">&para;</a></h3>
<p>Test login endpoint:</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-39-1"><a id="__codelineno-39-1" name="__codelineno-39-1" href="#__codelineno-39-1"></a>curl<span class="w"> </span>-X<span class="w"> </span>POST<span class="w"> </span>http://localhost:4000/api/auth/login<span class="w"> </span><span class="se">\</span>
</span><span id="__span-39-2"><a id="__codelineno-39-2" name="__codelineno-39-2" href="#__codelineno-39-2"></a><span class="w"> </span>-H<span class="w"> </span><span class="s2">&quot;Content-Type: application/json&quot;</span><span class="w"> </span><span class="se">\</span>
</span><span id="__span-39-3"><a id="__codelineno-39-3" name="__codelineno-39-3" href="#__codelineno-39-3"></a><span class="w"> </span>-d<span class="w"> </span><span class="s1">&#39;{</span>
</span><span id="__span-39-4"><a id="__codelineno-39-4" name="__codelineno-39-4" href="#__codelineno-39-4"></a><span class="s1"> &quot;email&quot;: &quot;admin@example.com&quot;,</span>
</span><span id="__span-39-5"><a id="__codelineno-39-5" name="__codelineno-39-5" href="#__codelineno-39-5"></a><span class="s1"> &quot;password&quot;: &quot;Admin123!&quot;</span>
</span><span id="__span-39-6"><a id="__codelineno-39-6" name="__codelineno-39-6" href="#__codelineno-39-6"></a><span class="s1"> }&#39;</span>
</span></code></pre></div>
<p><strong>Expected response:</strong>
<div class="language-json highlight"><pre><span></span><code><span id="__span-40-1"><a id="__codelineno-40-1" name="__codelineno-40-1" href="#__codelineno-40-1"></a><span class="p">{</span>
</span><span id="__span-40-2"><a id="__codelineno-40-2" name="__codelineno-40-2" href="#__codelineno-40-2"></a><span class="w"> </span><span class="nt">&quot;accessToken&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...&quot;</span><span class="p">,</span>
</span><span id="__span-40-3"><a id="__codelineno-40-3" name="__codelineno-40-3" href="#__codelineno-40-3"></a><span class="w"> </span><span class="nt">&quot;refreshToken&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...&quot;</span><span class="p">,</span>
</span><span id="__span-40-4"><a id="__codelineno-40-4" name="__codelineno-40-4" href="#__codelineno-40-4"></a><span class="w"> </span><span class="nt">&quot;user&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-40-5"><a id="__codelineno-40-5" name="__codelineno-40-5" href="#__codelineno-40-5"></a><span class="w"> </span><span class="nt">&quot;id&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span>
</span><span id="__span-40-6"><a id="__codelineno-40-6" name="__codelineno-40-6" href="#__codelineno-40-6"></a><span class="w"> </span><span class="nt">&quot;email&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;admin@example.com&quot;</span><span class="p">,</span>
</span><span id="__span-40-7"><a id="__codelineno-40-7" name="__codelineno-40-7" href="#__codelineno-40-7"></a><span class="w"> </span><span class="nt">&quot;role&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;SUPER_ADMIN&quot;</span>
</span><span id="__span-40-8"><a id="__codelineno-40-8" name="__codelineno-40-8" href="#__codelineno-40-8"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-40-9"><a id="__codelineno-40-9" name="__codelineno-40-9" href="#__codelineno-40-9"></a><span class="p">}</span>
</span></code></pre></div></p>
<h3 id="login-to-admin-gui">Login to Admin GUI<a class="headerlink" href="#login-to-admin-gui" title="Permanent link">&para;</a></h3>
<ol>
<li>Open http://localhost:3000 in browser</li>
<li>Login with:</li>
<li>Email: <code>admin@example.com</code></li>
<li>Password: <code>Admin123!</code></li>
<li>You should be redirected to <code>/app</code> (admin dashboard)</li>
<li><strong>Change password immediately:</strong></li>
<li>Click user menu (top right)</li>
<li>Settings → Change Password</li>
<li>Set new password (12+ chars, uppercase, lowercase, digit)</li>
</ol>
<h3 id="verify-database-connection_1">Verify Database Connection<a class="headerlink" href="#verify-database-connection_1" title="Permanent link">&para;</a></h3>
<p>Check that API can query database:</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-41-1"><a id="__codelineno-41-1" name="__codelineno-41-1" href="#__codelineno-41-1"></a>curl<span class="w"> </span>http://localhost:4000/api/users<span class="w"> </span><span class="se">\</span>
</span><span id="__span-41-2"><a id="__codelineno-41-2" name="__codelineno-41-2" href="#__codelineno-41-2"></a><span class="w"> </span>-H<span class="w"> </span><span class="s2">&quot;Authorization: Bearer &lt;access_token_from_login&gt;&quot;</span>
</span></code></pre></div>
<p><strong>Expected response:</strong>
<div class="language-json highlight"><pre><span></span><code><span id="__span-42-1"><a id="__codelineno-42-1" name="__codelineno-42-1" href="#__codelineno-42-1"></a><span class="p">{</span>
</span><span id="__span-42-2"><a id="__codelineno-42-2" name="__codelineno-42-2" href="#__codelineno-42-2"></a><span class="w"> </span><span class="nt">&quot;users&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
</span><span id="__span-42-3"><a id="__codelineno-42-3" name="__codelineno-42-3" href="#__codelineno-42-3"></a><span class="w"> </span><span class="p">{</span>
</span><span id="__span-42-4"><a id="__codelineno-42-4" name="__codelineno-42-4" href="#__codelineno-42-4"></a><span class="w"> </span><span class="nt">&quot;id&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span>
</span><span id="__span-42-5"><a id="__codelineno-42-5" name="__codelineno-42-5" href="#__codelineno-42-5"></a><span class="w"> </span><span class="nt">&quot;email&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;admin@example.com&quot;</span><span class="p">,</span>
</span><span id="__span-42-6"><a id="__codelineno-42-6" name="__codelineno-42-6" href="#__codelineno-42-6"></a><span class="w"> </span><span class="nt">&quot;role&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;SUPER_ADMIN&quot;</span><span class="p">,</span>
</span><span id="__span-42-7"><a id="__codelineno-42-7" name="__codelineno-42-7" href="#__codelineno-42-7"></a><span class="w"> </span><span class="err">...</span>
</span><span id="__span-42-8"><a id="__codelineno-42-8" name="__codelineno-42-8" href="#__codelineno-42-8"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-42-9"><a id="__codelineno-42-9" name="__codelineno-42-9" href="#__codelineno-42-9"></a><span class="w"> </span><span class="p">],</span>
</span><span id="__span-42-10"><a id="__codelineno-42-10" name="__codelineno-42-10" href="#__codelineno-42-10"></a><span class="w"> </span><span class="nt">&quot;total&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span>
</span><span id="__span-42-11"><a id="__codelineno-42-11" name="__codelineno-42-11" href="#__codelineno-42-11"></a><span class="w"> </span><span class="nt">&quot;page&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span>
</span><span id="__span-42-12"><a id="__codelineno-42-12" name="__codelineno-42-12" href="#__codelineno-42-12"></a><span class="w"> </span><span class="nt">&quot;limit&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">50</span>
</span><span id="__span-42-13"><a id="__codelineno-42-13" name="__codelineno-42-13" href="#__codelineno-42-13"></a><span class="p">}</span>
</span></code></pre></div></p>
<h3 id="test-email-capture-mailhog">Test Email Capture (MailHog)<a class="headerlink" href="#test-email-capture-mailhog" title="Permanent link">&para;</a></h3>
<ol>
<li>Open http://localhost:8025 in browser</li>
<li>You should see MailHog web UI</li>
<li>Trigger a test email (e.g., shift signup)</li>
<li>Email appears in MailHog inbox</li>
</ol>
<h2 id="ide-setup">IDE Setup<a class="headerlink" href="#ide-setup" title="Permanent link">&para;</a></h2>
<h3 id="visual-studio-code_1">Visual Studio Code<a class="headerlink" href="#visual-studio-code_1" title="Permanent link">&para;</a></h3>
<p>Recommended IDE with excellent TypeScript/React support.</p>
<h4 id="recommended-extensions">Recommended Extensions<a class="headerlink" href="#recommended-extensions" title="Permanent link">&para;</a></h4>
<p>Install these extensions for best developer experience:</p>
<p><strong>Essential:</strong>
- <strong>ESLint</strong> (<code>dbaeumer.vscode-eslint</code>) - Linting
- <strong>Prettier</strong> (<code>esbenp.prettier-vscode</code>) - Code formatting
- <strong>Prisma</strong> (<code>Prisma.prisma</code>) - Prisma schema support
- <strong>TypeScript Vue Plugin (Volar)</strong> (<code>Vue.volar</code>) - Vue/JSX support</p>
<p><strong>Highly Recommended:</strong>
- <strong>GitLens</strong> (<code>eamodio.gitlens</code>) - Git insights
- <strong>Docker</strong> (<code>ms-azuretools.vscode-docker</code>) - Docker management
- <strong>Thunder Client</strong> (<code>rangav.vscode-thunder-client</code>) - API testing
- <strong>Error Lens</strong> (<code>usernamehw.errorlens</code>) - Inline errors
- <strong>Auto Rename Tag</strong> (<code>formulahendry.auto-rename-tag</code>) - HTML/JSX tag pairs
- <strong>Path Intellisense</strong> (<code>christian-kohler.path-intellisense</code>) - Path autocomplete</p>
<p><strong>Optional:</strong>
- <strong>Tailwind CSS IntelliSense</strong> (<code>bradlc.vscode-tailwindcss</code>) - Tailwind support
- <strong>DotENV</strong> (<code>mikestead.dotenv</code>) - .env syntax highlighting
- <strong>Import Cost</strong> (<code>wix.vscode-import-cost</code>) - Bundle size info</p>
<h4 id="workspace-settings">Workspace Settings<a class="headerlink" href="#workspace-settings" title="Permanent link">&para;</a></h4>
<p>Create <code>.vscode/settings.json</code> in project root:</p>
<div class="language-json highlight"><pre><span></span><code><span id="__span-43-1"><a id="__codelineno-43-1" name="__codelineno-43-1" href="#__codelineno-43-1"></a><span class="p">{</span>
</span><span id="__span-43-2"><a id="__codelineno-43-2" name="__codelineno-43-2" href="#__codelineno-43-2"></a><span class="w"> </span><span class="c1">// Editor</span>
</span><span id="__span-43-3"><a id="__codelineno-43-3" name="__codelineno-43-3" href="#__codelineno-43-3"></a><span class="w"> </span><span class="nt">&quot;editor.formatOnSave&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
</span><span id="__span-43-4"><a id="__codelineno-43-4" name="__codelineno-43-4" href="#__codelineno-43-4"></a><span class="w"> </span><span class="nt">&quot;editor.defaultFormatter&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;esbenp.prettier-vscode&quot;</span><span class="p">,</span>
</span><span id="__span-43-5"><a id="__codelineno-43-5" name="__codelineno-43-5" href="#__codelineno-43-5"></a><span class="w"> </span><span class="nt">&quot;editor.codeActionsOnSave&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-43-6"><a id="__codelineno-43-6" name="__codelineno-43-6" href="#__codelineno-43-6"></a><span class="w"> </span><span class="nt">&quot;source.fixAll.eslint&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span>
</span><span id="__span-43-7"><a id="__codelineno-43-7" name="__codelineno-43-7" href="#__codelineno-43-7"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-43-8"><a id="__codelineno-43-8" name="__codelineno-43-8" href="#__codelineno-43-8"></a><span class="w"> </span><span class="nt">&quot;editor.tabSize&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span>
</span><span id="__span-43-9"><a id="__codelineno-43-9" name="__codelineno-43-9" href="#__codelineno-43-9"></a><span class="w"> </span><span class="nt">&quot;editor.insertSpaces&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
</span><span id="__span-43-10"><a id="__codelineno-43-10" name="__codelineno-43-10" href="#__codelineno-43-10"></a>
</span><span id="__span-43-11"><a id="__codelineno-43-11" name="__codelineno-43-11" href="#__codelineno-43-11"></a><span class="w"> </span><span class="c1">// Files</span>
</span><span id="__span-43-12"><a id="__codelineno-43-12" name="__codelineno-43-12" href="#__codelineno-43-12"></a><span class="w"> </span><span class="nt">&quot;files.eol&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;\n&quot;</span><span class="p">,</span>
</span><span id="__span-43-13"><a id="__codelineno-43-13" name="__codelineno-43-13" href="#__codelineno-43-13"></a><span class="w"> </span><span class="nt">&quot;files.trimTrailingWhitespace&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
</span><span id="__span-43-14"><a id="__codelineno-43-14" name="__codelineno-43-14" href="#__codelineno-43-14"></a><span class="w"> </span><span class="nt">&quot;files.insertFinalNewline&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
</span><span id="__span-43-15"><a id="__codelineno-43-15" name="__codelineno-43-15" href="#__codelineno-43-15"></a>
</span><span id="__span-43-16"><a id="__codelineno-43-16" name="__codelineno-43-16" href="#__codelineno-43-16"></a><span class="w"> </span><span class="c1">// TypeScript</span>
</span><span id="__span-43-17"><a id="__codelineno-43-17" name="__codelineno-43-17" href="#__codelineno-43-17"></a><span class="w"> </span><span class="nt">&quot;typescript.tsdk&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;node_modules/typescript/lib&quot;</span><span class="p">,</span>
</span><span id="__span-43-18"><a id="__codelineno-43-18" name="__codelineno-43-18" href="#__codelineno-43-18"></a><span class="w"> </span><span class="nt">&quot;typescript.enablePromptUseWorkspaceTsdk&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
</span><span id="__span-43-19"><a id="__codelineno-43-19" name="__codelineno-43-19" href="#__codelineno-43-19"></a><span class="w"> </span><span class="nt">&quot;typescript.preferences.importModuleSpecifier&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;relative&quot;</span><span class="p">,</span>
</span><span id="__span-43-20"><a id="__codelineno-43-20" name="__codelineno-43-20" href="#__codelineno-43-20"></a>
</span><span id="__span-43-21"><a id="__codelineno-43-21" name="__codelineno-43-21" href="#__codelineno-43-21"></a><span class="w"> </span><span class="c1">// Prisma</span>
</span><span id="__span-43-22"><a id="__codelineno-43-22" name="__codelineno-43-22" href="#__codelineno-43-22"></a><span class="w"> </span><span class="nt">&quot;[prisma]&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-43-23"><a id="__codelineno-43-23" name="__codelineno-43-23" href="#__codelineno-43-23"></a><span class="w"> </span><span class="nt">&quot;editor.defaultFormatter&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Prisma.prisma&quot;</span>
</span><span id="__span-43-24"><a id="__codelineno-43-24" name="__codelineno-43-24" href="#__codelineno-43-24"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-43-25"><a id="__codelineno-43-25" name="__codelineno-43-25" href="#__codelineno-43-25"></a>
</span><span id="__span-43-26"><a id="__codelineno-43-26" name="__codelineno-43-26" href="#__codelineno-43-26"></a><span class="w"> </span><span class="c1">// ESLint</span>
</span><span id="__span-43-27"><a id="__codelineno-43-27" name="__codelineno-43-27" href="#__codelineno-43-27"></a><span class="w"> </span><span class="nt">&quot;eslint.validate&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
</span><span id="__span-43-28"><a id="__codelineno-43-28" name="__codelineno-43-28" href="#__codelineno-43-28"></a><span class="w"> </span><span class="s2">&quot;javascript&quot;</span><span class="p">,</span>
</span><span id="__span-43-29"><a id="__codelineno-43-29" name="__codelineno-43-29" href="#__codelineno-43-29"></a><span class="w"> </span><span class="s2">&quot;javascriptreact&quot;</span><span class="p">,</span>
</span><span id="__span-43-30"><a id="__codelineno-43-30" name="__codelineno-43-30" href="#__codelineno-43-30"></a><span class="w"> </span><span class="s2">&quot;typescript&quot;</span><span class="p">,</span>
</span><span id="__span-43-31"><a id="__codelineno-43-31" name="__codelineno-43-31" href="#__codelineno-43-31"></a><span class="w"> </span><span class="s2">&quot;typescriptreact&quot;</span>
</span><span id="__span-43-32"><a id="__codelineno-43-32" name="__codelineno-43-32" href="#__codelineno-43-32"></a><span class="w"> </span><span class="p">],</span>
</span><span id="__span-43-33"><a id="__codelineno-43-33" name="__codelineno-43-33" href="#__codelineno-43-33"></a>
</span><span id="__span-43-34"><a id="__codelineno-43-34" name="__codelineno-43-34" href="#__codelineno-43-34"></a><span class="w"> </span><span class="c1">// Search exclusions (performance)</span>
</span><span id="__span-43-35"><a id="__codelineno-43-35" name="__codelineno-43-35" href="#__codelineno-43-35"></a><span class="w"> </span><span class="nt">&quot;search.exclude&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-43-36"><a id="__codelineno-43-36" name="__codelineno-43-36" href="#__codelineno-43-36"></a><span class="w"> </span><span class="nt">&quot;**/node_modules&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
</span><span id="__span-43-37"><a id="__codelineno-43-37" name="__codelineno-43-37" href="#__codelineno-43-37"></a><span class="w"> </span><span class="nt">&quot;**/dist&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
</span><span id="__span-43-38"><a id="__codelineno-43-38" name="__codelineno-43-38" href="#__codelineno-43-38"></a><span class="w"> </span><span class="nt">&quot;**/build&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
</span><span id="__span-43-39"><a id="__codelineno-43-39" name="__codelineno-43-39" href="#__codelineno-43-39"></a><span class="w"> </span><span class="nt">&quot;**/.git&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
</span><span id="__span-43-40"><a id="__codelineno-43-40" name="__codelineno-43-40" href="#__codelineno-43-40"></a><span class="w"> </span><span class="nt">&quot;**/coverage&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span>
</span><span id="__span-43-41"><a id="__codelineno-43-41" name="__codelineno-43-41" href="#__codelineno-43-41"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-43-42"><a id="__codelineno-43-42" name="__codelineno-43-42" href="#__codelineno-43-42"></a>
</span><span id="__span-43-43"><a id="__codelineno-43-43" name="__codelineno-43-43" href="#__codelineno-43-43"></a><span class="w"> </span><span class="c1">// File associations</span>
</span><span id="__span-43-44"><a id="__codelineno-43-44" name="__codelineno-43-44" href="#__codelineno-43-44"></a><span class="w"> </span><span class="nt">&quot;files.associations&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-43-45"><a id="__codelineno-43-45" name="__codelineno-43-45" href="#__codelineno-43-45"></a><span class="w"> </span><span class="nt">&quot;*.css&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;css&quot;</span><span class="p">,</span>
</span><span id="__span-43-46"><a id="__codelineno-43-46" name="__codelineno-43-46" href="#__codelineno-43-46"></a><span class="w"> </span><span class="nt">&quot;.env*&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;dotenv&quot;</span>
</span><span id="__span-43-47"><a id="__codelineno-43-47" name="__codelineno-43-47" href="#__codelineno-43-47"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-43-48"><a id="__codelineno-43-48" name="__codelineno-43-48" href="#__codelineno-43-48"></a><span class="p">}</span>
</span></code></pre></div>
<h4 id="launch-configuration">Launch Configuration<a class="headerlink" href="#launch-configuration" title="Permanent link">&para;</a></h4>
<p>Create <code>.vscode/launch.json</code> for debugging:</p>
<div class="language-json highlight"><pre><span></span><code><span id="__span-44-1"><a id="__codelineno-44-1" name="__codelineno-44-1" href="#__codelineno-44-1"></a><span class="p">{</span>
</span><span id="__span-44-2"><a id="__codelineno-44-2" name="__codelineno-44-2" href="#__codelineno-44-2"></a><span class="w"> </span><span class="nt">&quot;version&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;0.2.0&quot;</span><span class="p">,</span>
</span><span id="__span-44-3"><a id="__codelineno-44-3" name="__codelineno-44-3" href="#__codelineno-44-3"></a><span class="w"> </span><span class="nt">&quot;configurations&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
</span><span id="__span-44-4"><a id="__codelineno-44-4" name="__codelineno-44-4" href="#__codelineno-44-4"></a><span class="w"> </span><span class="p">{</span>
</span><span id="__span-44-5"><a id="__codelineno-44-5" name="__codelineno-44-5" href="#__codelineno-44-5"></a><span class="w"> </span><span class="nt">&quot;name&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Debug API&quot;</span><span class="p">,</span>
</span><span id="__span-44-6"><a id="__codelineno-44-6" name="__codelineno-44-6" href="#__codelineno-44-6"></a><span class="w"> </span><span class="nt">&quot;type&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;node&quot;</span><span class="p">,</span>
</span><span id="__span-44-7"><a id="__codelineno-44-7" name="__codelineno-44-7" href="#__codelineno-44-7"></a><span class="w"> </span><span class="nt">&quot;request&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;launch&quot;</span><span class="p">,</span>
</span><span id="__span-44-8"><a id="__codelineno-44-8" name="__codelineno-44-8" href="#__codelineno-44-8"></a><span class="w"> </span><span class="nt">&quot;runtimeExecutable&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;npm&quot;</span><span class="p">,</span>
</span><span id="__span-44-9"><a id="__codelineno-44-9" name="__codelineno-44-9" href="#__codelineno-44-9"></a><span class="w"> </span><span class="nt">&quot;runtimeArgs&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&quot;run&quot;</span><span class="p">,</span><span class="w"> </span><span class="s2">&quot;dev&quot;</span><span class="p">],</span>
</span><span id="__span-44-10"><a id="__codelineno-44-10" name="__codelineno-44-10" href="#__codelineno-44-10"></a><span class="w"> </span><span class="nt">&quot;cwd&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;${workspaceFolder}/api&quot;</span><span class="p">,</span>
</span><span id="__span-44-11"><a id="__codelineno-44-11" name="__codelineno-44-11" href="#__codelineno-44-11"></a><span class="w"> </span><span class="nt">&quot;console&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;integratedTerminal&quot;</span><span class="p">,</span>
</span><span id="__span-44-12"><a id="__codelineno-44-12" name="__codelineno-44-12" href="#__codelineno-44-12"></a><span class="w"> </span><span class="nt">&quot;skipFiles&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&quot;&lt;node_internals&gt;/**&quot;</span><span class="p">],</span>
</span><span id="__span-44-13"><a id="__codelineno-44-13" name="__codelineno-44-13" href="#__codelineno-44-13"></a><span class="w"> </span><span class="nt">&quot;envFile&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;${workspaceFolder}/.env&quot;</span>
</span><span id="__span-44-14"><a id="__codelineno-44-14" name="__codelineno-44-14" href="#__codelineno-44-14"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-44-15"><a id="__codelineno-44-15" name="__codelineno-44-15" href="#__codelineno-44-15"></a><span class="w"> </span><span class="p">{</span>
</span><span id="__span-44-16"><a id="__codelineno-44-16" name="__codelineno-44-16" href="#__codelineno-44-16"></a><span class="w"> </span><span class="nt">&quot;name&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Debug Admin (Chrome)&quot;</span><span class="p">,</span>
</span><span id="__span-44-17"><a id="__codelineno-44-17" name="__codelineno-44-17" href="#__codelineno-44-17"></a><span class="w"> </span><span class="nt">&quot;type&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;chrome&quot;</span><span class="p">,</span>
</span><span id="__span-44-18"><a id="__codelineno-44-18" name="__codelineno-44-18" href="#__codelineno-44-18"></a><span class="w"> </span><span class="nt">&quot;request&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;launch&quot;</span><span class="p">,</span>
</span><span id="__span-44-19"><a id="__codelineno-44-19" name="__codelineno-44-19" href="#__codelineno-44-19"></a><span class="w"> </span><span class="nt">&quot;url&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;http://localhost:3000&quot;</span><span class="p">,</span>
</span><span id="__span-44-20"><a id="__codelineno-44-20" name="__codelineno-44-20" href="#__codelineno-44-20"></a><span class="w"> </span><span class="nt">&quot;webRoot&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;${workspaceFolder}/admin/src&quot;</span><span class="p">,</span>
</span><span id="__span-44-21"><a id="__codelineno-44-21" name="__codelineno-44-21" href="#__codelineno-44-21"></a><span class="w"> </span><span class="nt">&quot;sourceMapPathOverrides&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-44-22"><a id="__codelineno-44-22" name="__codelineno-44-22" href="#__codelineno-44-22"></a><span class="w"> </span><span class="nt">&quot;webpack:///./src/*&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;${webRoot}/*&quot;</span>
</span><span id="__span-44-23"><a id="__codelineno-44-23" name="__codelineno-44-23" href="#__codelineno-44-23"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-44-24"><a id="__codelineno-44-24" name="__codelineno-44-24" href="#__codelineno-44-24"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-44-25"><a id="__codelineno-44-25" name="__codelineno-44-25" href="#__codelineno-44-25"></a><span class="w"> </span><span class="p">{</span>
</span><span id="__span-44-26"><a id="__codelineno-44-26" name="__codelineno-44-26" href="#__codelineno-44-26"></a><span class="w"> </span><span class="nt">&quot;name&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Debug Media API&quot;</span><span class="p">,</span>
</span><span id="__span-44-27"><a id="__codelineno-44-27" name="__codelineno-44-27" href="#__codelineno-44-27"></a><span class="w"> </span><span class="nt">&quot;type&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;node&quot;</span><span class="p">,</span>
</span><span id="__span-44-28"><a id="__codelineno-44-28" name="__codelineno-44-28" href="#__codelineno-44-28"></a><span class="w"> </span><span class="nt">&quot;request&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;launch&quot;</span><span class="p">,</span>
</span><span id="__span-44-29"><a id="__codelineno-44-29" name="__codelineno-44-29" href="#__codelineno-44-29"></a><span class="w"> </span><span class="nt">&quot;runtimeExecutable&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;npm&quot;</span><span class="p">,</span>
</span><span id="__span-44-30"><a id="__codelineno-44-30" name="__codelineno-44-30" href="#__codelineno-44-30"></a><span class="w"> </span><span class="nt">&quot;runtimeArgs&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&quot;run&quot;</span><span class="p">,</span><span class="w"> </span><span class="s2">&quot;dev:media&quot;</span><span class="p">],</span>
</span><span id="__span-44-31"><a id="__codelineno-44-31" name="__codelineno-44-31" href="#__codelineno-44-31"></a><span class="w"> </span><span class="nt">&quot;cwd&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;${workspaceFolder}/api&quot;</span><span class="p">,</span>
</span><span id="__span-44-32"><a id="__codelineno-44-32" name="__codelineno-44-32" href="#__codelineno-44-32"></a><span class="w"> </span><span class="nt">&quot;console&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;integratedTerminal&quot;</span><span class="p">,</span>
</span><span id="__span-44-33"><a id="__codelineno-44-33" name="__codelineno-44-33" href="#__codelineno-44-33"></a><span class="w"> </span><span class="nt">&quot;skipFiles&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&quot;&lt;node_internals&gt;/**&quot;</span><span class="p">],</span>
</span><span id="__span-44-34"><a id="__codelineno-44-34" name="__codelineno-44-34" href="#__codelineno-44-34"></a><span class="w"> </span><span class="nt">&quot;envFile&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;${workspaceFolder}/.env&quot;</span>
</span><span id="__span-44-35"><a id="__codelineno-44-35" name="__codelineno-44-35" href="#__codelineno-44-35"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-44-36"><a id="__codelineno-44-36" name="__codelineno-44-36" href="#__codelineno-44-36"></a><span class="w"> </span><span class="p">]</span>
</span><span id="__span-44-37"><a id="__codelineno-44-37" name="__codelineno-44-37" href="#__codelineno-44-37"></a><span class="p">}</span>
</span></code></pre></div>
<h4 id="workspace-file">Workspace File<a class="headerlink" href="#workspace-file" title="Permanent link">&para;</a></h4>
<p>Create <code>changemaker-lite.code-workspace</code>:</p>
<div class="language-json highlight"><pre><span></span><code><span id="__span-45-1"><a id="__codelineno-45-1" name="__codelineno-45-1" href="#__codelineno-45-1"></a><span class="p">{</span>
</span><span id="__span-45-2"><a id="__codelineno-45-2" name="__codelineno-45-2" href="#__codelineno-45-2"></a><span class="w"> </span><span class="nt">&quot;folders&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
</span><span id="__span-45-3"><a id="__codelineno-45-3" name="__codelineno-45-3" href="#__codelineno-45-3"></a><span class="w"> </span><span class="p">{</span>
</span><span id="__span-45-4"><a id="__codelineno-45-4" name="__codelineno-45-4" href="#__codelineno-45-4"></a><span class="w"> </span><span class="nt">&quot;name&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Root&quot;</span><span class="p">,</span>
</span><span id="__span-45-5"><a id="__codelineno-45-5" name="__codelineno-45-5" href="#__codelineno-45-5"></a><span class="w"> </span><span class="nt">&quot;path&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;.&quot;</span>
</span><span id="__span-45-6"><a id="__codelineno-45-6" name="__codelineno-45-6" href="#__codelineno-45-6"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-45-7"><a id="__codelineno-45-7" name="__codelineno-45-7" href="#__codelineno-45-7"></a><span class="w"> </span><span class="p">{</span>
</span><span id="__span-45-8"><a id="__codelineno-45-8" name="__codelineno-45-8" href="#__codelineno-45-8"></a><span class="w"> </span><span class="nt">&quot;name&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;API&quot;</span><span class="p">,</span>
</span><span id="__span-45-9"><a id="__codelineno-45-9" name="__codelineno-45-9" href="#__codelineno-45-9"></a><span class="w"> </span><span class="nt">&quot;path&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;api&quot;</span>
</span><span id="__span-45-10"><a id="__codelineno-45-10" name="__codelineno-45-10" href="#__codelineno-45-10"></a><span class="w"> </span><span class="p">},</span>
</span><span id="__span-45-11"><a id="__codelineno-45-11" name="__codelineno-45-11" href="#__codelineno-45-11"></a><span class="w"> </span><span class="p">{</span>
</span><span id="__span-45-12"><a id="__codelineno-45-12" name="__codelineno-45-12" href="#__codelineno-45-12"></a><span class="w"> </span><span class="nt">&quot;name&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Admin&quot;</span><span class="p">,</span>
</span><span id="__span-45-13"><a id="__codelineno-45-13" name="__codelineno-45-13" href="#__codelineno-45-13"></a><span class="w"> </span><span class="nt">&quot;path&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;admin&quot;</span>
</span><span id="__span-45-14"><a id="__codelineno-45-14" name="__codelineno-45-14" href="#__codelineno-45-14"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-45-15"><a id="__codelineno-45-15" name="__codelineno-45-15" href="#__codelineno-45-15"></a><span class="w"> </span><span class="p">],</span>
</span><span id="__span-45-16"><a id="__codelineno-45-16" name="__codelineno-45-16" href="#__codelineno-45-16"></a><span class="w"> </span><span class="nt">&quot;settings&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
</span><span id="__span-45-17"><a id="__codelineno-45-17" name="__codelineno-45-17" href="#__codelineno-45-17"></a><span class="w"> </span><span class="c1">// Workspace-level settings (inherits from .vscode/settings.json)</span>
</span><span id="__span-45-18"><a id="__codelineno-45-18" name="__codelineno-45-18" href="#__codelineno-45-18"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-45-19"><a id="__codelineno-45-19" name="__codelineno-45-19" href="#__codelineno-45-19"></a><span class="p">}</span>
</span></code></pre></div>
<p>Open workspace: <code>code changemaker-lite.code-workspace</code></p>
<h3 id="other-ides">Other IDEs<a class="headerlink" href="#other-ides" title="Permanent link">&para;</a></h3>
<h4 id="webstorm-intellij-idea">WebStorm / IntelliJ IDEA<a class="headerlink" href="#webstorm-intellij-idea" title="Permanent link">&para;</a></h4>
<ul>
<li>Built-in TypeScript support</li>
<li>Built-in Prisma plugin</li>
<li>Configure ESLint/Prettier in Preferences → Languages &amp; Frameworks</li>
</ul>
<h4 id="neovim-vim">Neovim / Vim<a class="headerlink" href="#neovim-vim" title="Permanent link">&para;</a></h4>
<ul>
<li>Use LSP with <code>typescript-language-server</code></li>
<li>Prisma LSP: <code>@prisma/language-server</code></li>
<li>ESLint/Prettier via null-ls or ALE</li>
</ul>
<h2 id="troubleshooting">Troubleshooting<a class="headerlink" href="#troubleshooting" title="Permanent link">&para;</a></h2>
<h3 id="port-conflicts">Port Conflicts<a class="headerlink" href="#port-conflicts" title="Permanent link">&para;</a></h3>
<p><strong>Problem:</strong> Port already in use errors</p>
<div class="language-text highlight"><pre><span></span><code><span id="__span-46-1"><a id="__codelineno-46-1" name="__codelineno-46-1" href="#__codelineno-46-1"></a>Error: listen EADDRINUSE: address already in use :::4000
</span></code></pre></div>
<p><strong>Solution 1:</strong> Find and kill the process using the port</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-47-1"><a id="__codelineno-47-1" name="__codelineno-47-1" href="#__codelineno-47-1"></a><span class="c1"># Linux/macOS</span>
</span><span id="__span-47-2"><a id="__codelineno-47-2" name="__codelineno-47-2" href="#__codelineno-47-2"></a>lsof<span class="w"> </span>-ti:4000<span class="w"> </span><span class="p">|</span><span class="w"> </span>xargs<span class="w"> </span><span class="nb">kill</span><span class="w"> </span>-9
</span><span id="__span-47-3"><a id="__codelineno-47-3" name="__codelineno-47-3" href="#__codelineno-47-3"></a>
</span><span id="__span-47-4"><a id="__codelineno-47-4" name="__codelineno-47-4" href="#__codelineno-47-4"></a><span class="c1"># Or change port in .env</span>
</span><span id="__span-47-5"><a id="__codelineno-47-5" name="__codelineno-47-5" href="#__codelineno-47-5"></a><span class="nv">API_PORT</span><span class="o">=</span><span class="m">4002</span>
</span></code></pre></div>
<p><strong>Solution 2:</strong> Use different ports in <code>.env</code></p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-48-1"><a id="__codelineno-48-1" name="__codelineno-48-1" href="#__codelineno-48-1"></a><span class="nv">API_PORT</span><span class="o">=</span><span class="m">4002</span>
</span><span id="__span-48-2"><a id="__codelineno-48-2" name="__codelineno-48-2" href="#__codelineno-48-2"></a><span class="nv">ADMIN_PORT</span><span class="o">=</span><span class="m">3002</span>
</span><span id="__span-48-3"><a id="__codelineno-48-3" name="__codelineno-48-3" href="#__codelineno-48-3"></a><span class="nv">MEDIA_API_PORT</span><span class="o">=</span><span class="m">4102</span>
</span></code></pre></div>
<h3 id="database-connection-errors">Database Connection Errors<a class="headerlink" href="#database-connection-errors" title="Permanent link">&para;</a></h3>
<p><strong>Problem:</strong> API cannot connect to PostgreSQL</p>
<div class="language-text highlight"><pre><span></span><code><span id="__span-49-1"><a id="__codelineno-49-1" name="__codelineno-49-1" href="#__codelineno-49-1"></a>Error: connect ECONNREFUSED 127.0.0.1:5433
</span></code></pre></div>
<p><strong>Solution 1:</strong> Verify PostgreSQL is running</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-50-1"><a id="__codelineno-50-1" name="__codelineno-50-1" href="#__codelineno-50-1"></a>docker<span class="w"> </span>compose<span class="w"> </span>ps<span class="w"> </span>v2-postgres
</span><span id="__span-50-2"><a id="__codelineno-50-2" name="__codelineno-50-2" href="#__codelineno-50-2"></a><span class="c1"># Should show &quot;running&quot;</span>
</span></code></pre></div>
<p><strong>Solution 2:</strong> Check DATABASE_URL in .env</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-51-1"><a id="__codelineno-51-1" name="__codelineno-51-1" href="#__codelineno-51-1"></a><span class="c1"># Should match your password and port</span>
</span><span id="__span-51-2"><a id="__codelineno-51-2" name="__codelineno-51-2" href="#__codelineno-51-2"></a><span class="nv">DATABASE_URL</span><span class="o">=</span>postgresql://changemaker_v2:your_password@localhost:5433/changemaker_v2_db
</span></code></pre></div>
<p><strong>Solution 3:</strong> Restart PostgreSQL container</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-52-1"><a id="__codelineno-52-1" name="__codelineno-52-1" href="#__codelineno-52-1"></a>docker<span class="w"> </span>compose<span class="w"> </span>restart<span class="w"> </span>v2-postgres
</span><span id="__span-52-2"><a id="__codelineno-52-2" name="__codelineno-52-2" href="#__codelineno-52-2"></a>docker<span class="w"> </span>compose<span class="w"> </span>logs<span class="w"> </span>-f<span class="w"> </span>v2-postgres
</span><span id="__span-52-3"><a id="__codelineno-52-3" name="__codelineno-52-3" href="#__codelineno-52-3"></a><span class="c1"># Wait for &quot;ready to accept connections&quot;</span>
</span></code></pre></div>
<h3 id="redis-connection-errors">Redis Connection Errors<a class="headerlink" href="#redis-connection-errors" title="Permanent link">&para;</a></h3>
<p><strong>Problem:</strong> API cannot connect to Redis</p>
<div class="language-text highlight"><pre><span></span><code><span id="__span-53-1"><a id="__codelineno-53-1" name="__codelineno-53-1" href="#__codelineno-53-1"></a>Error: Redis connection refused
</span></code></pre></div>
<p><strong>Solution 1:</strong> Verify Redis is running</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-54-1"><a id="__codelineno-54-1" name="__codelineno-54-1" href="#__codelineno-54-1"></a>docker<span class="w"> </span>compose<span class="w"> </span>ps<span class="w"> </span>redis
</span><span id="__span-54-2"><a id="__codelineno-54-2" name="__codelineno-54-2" href="#__codelineno-54-2"></a><span class="c1"># Should show &quot;running&quot;</span>
</span></code></pre></div>
<p><strong>Solution 2:</strong> Check REDIS_URL and password</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-55-1"><a id="__codelineno-55-1" name="__codelineno-55-1" href="#__codelineno-55-1"></a><span class="c1"># Should match your password</span>
</span><span id="__span-55-2"><a id="__codelineno-55-2" name="__codelineno-55-2" href="#__codelineno-55-2"></a><span class="nv">REDIS_URL</span><span class="o">=</span>redis://:your_redis_password@localhost:6379
</span><span id="__span-55-3"><a id="__codelineno-55-3" name="__codelineno-55-3" href="#__codelineno-55-3"></a><span class="nv">REDIS_PASSWORD</span><span class="o">=</span>your_redis_password
</span></code></pre></div>
<p><strong>Solution 3:</strong> Test Redis connection directly</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-56-1"><a id="__codelineno-56-1" name="__codelineno-56-1" href="#__codelineno-56-1"></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>-a<span class="w"> </span>your_redis_password<span class="w"> </span>ping
</span><span id="__span-56-2"><a id="__codelineno-56-2" name="__codelineno-56-2" href="#__codelineno-56-2"></a><span class="c1"># Should output: PONG</span>
</span></code></pre></div>
<h3 id="migration-errors">Migration Errors<a class="headerlink" href="#migration-errors" title="Permanent link">&para;</a></h3>
<p><strong>Problem:</strong> Prisma migration fails</p>
<div class="language-text highlight"><pre><span></span><code><span id="__span-57-1"><a id="__codelineno-57-1" name="__codelineno-57-1" href="#__codelineno-57-1"></a>Error: P3005 Database schema is not empty
</span></code></pre></div>
<p><strong>Solution 1:</strong> Reset database (DEVELOPMENT ONLY)</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-58-1"><a id="__codelineno-58-1" name="__codelineno-58-1" href="#__codelineno-58-1"></a><span class="nb">cd</span><span class="w"> </span>api
</span><span id="__span-58-2"><a id="__codelineno-58-2" name="__codelineno-58-2" href="#__codelineno-58-2"></a>npx<span class="w"> </span>prisma<span class="w"> </span>migrate<span class="w"> </span>reset
</span><span id="__span-58-3"><a id="__codelineno-58-3" name="__codelineno-58-3" href="#__codelineno-58-3"></a><span class="c1"># WARNING: This deletes all data!</span>
</span></code></pre></div>
<p><strong>Solution 2:</strong> Force deploy migrations</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-59-1"><a id="__codelineno-59-1" name="__codelineno-59-1" href="#__codelineno-59-1"></a><span class="nb">cd</span><span class="w"> </span>api
</span><span id="__span-59-2"><a id="__codelineno-59-2" name="__codelineno-59-2" href="#__codelineno-59-2"></a>npx<span class="w"> </span>prisma<span class="w"> </span>migrate<span class="w"> </span>deploy<span class="w"> </span>--force
</span></code></pre></div>
<p><strong>Solution 3:</strong> Check migration history</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-60-1"><a id="__codelineno-60-1" name="__codelineno-60-1" href="#__codelineno-60-1"></a><span class="nb">cd</span><span class="w"> </span>api
</span><span id="__span-60-2"><a id="__codelineno-60-2" name="__codelineno-60-2" href="#__codelineno-60-2"></a>npx<span class="w"> </span>prisma<span class="w"> </span>migrate<span class="w"> </span>status
</span></code></pre></div>
<h3 id="npm-install-failures">npm Install Failures<a class="headerlink" href="#npm-install-failures" title="Permanent link">&para;</a></h3>
<p><strong>Problem:</strong> npm install fails with permission errors</p>
<p><strong>Solution 1:</strong> Clear npm cache</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-61-1"><a id="__codelineno-61-1" name="__codelineno-61-1" href="#__codelineno-61-1"></a>npm<span class="w"> </span>cache<span class="w"> </span>clean<span class="w"> </span>--force
</span><span id="__span-61-2"><a id="__codelineno-61-2" name="__codelineno-61-2" href="#__codelineno-61-2"></a>rm<span class="w"> </span>-rf<span class="w"> </span>node_modules<span class="w"> </span>package-lock.json
</span><span id="__span-61-3"><a id="__codelineno-61-3" name="__codelineno-61-3" href="#__codelineno-61-3"></a>npm<span class="w"> </span>install
</span></code></pre></div>
<p><strong>Solution 2:</strong> Use correct Node.js version</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-62-1"><a id="__codelineno-62-1" name="__codelineno-62-1" href="#__codelineno-62-1"></a>node<span class="w"> </span>--version<span class="w"> </span><span class="c1"># Should be v20.x.x</span>
</span><span id="__span-62-2"><a id="__codelineno-62-2" name="__codelineno-62-2" href="#__codelineno-62-2"></a>nvm<span class="w"> </span>use<span class="w"> </span><span class="m">20</span>
</span></code></pre></div>
<p><strong>Solution 3:</strong> Check disk space</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-63-1"><a id="__codelineno-63-1" name="__codelineno-63-1" href="#__codelineno-63-1"></a>df<span class="w"> </span>-h
</span><span id="__span-63-2"><a id="__codelineno-63-2" name="__codelineno-63-2" href="#__codelineno-63-2"></a><span class="c1"># Ensure sufficient space (10GB+ free)</span>
</span></code></pre></div>
<h3 id="hot-reload-not-working">Hot Reload Not Working<a class="headerlink" href="#hot-reload-not-working" title="Permanent link">&para;</a></h3>
<p><strong>Problem:</strong> Code changes don't trigger reload</p>
<p><strong>Solution 1 (Docker):</strong> Verify volume mounts in docker-compose.yml</p>
<div class="language-yaml highlight"><pre><span></span><code><span id="__span-64-1"><a id="__codelineno-64-1" name="__codelineno-64-1" href="#__codelineno-64-1"></a><span class="nt">api</span><span class="p">:</span>
</span><span id="__span-64-2"><a id="__codelineno-64-2" name="__codelineno-64-2" href="#__codelineno-64-2"></a><span class="w"> </span><span class="nt">volumes</span><span class="p">:</span>
</span><span id="__span-64-3"><a id="__codelineno-64-3" name="__codelineno-64-3" href="#__codelineno-64-3"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">./api:/app</span><span class="w"> </span><span class="c1"># Must be present</span>
</span></code></pre></div>
<p><strong>Solution 2 (Local):</strong> Restart dev server</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-65-1"><a id="__codelineno-65-1" name="__codelineno-65-1" href="#__codelineno-65-1"></a><span class="c1"># Stop (Ctrl+C) and restart</span>
</span><span id="__span-65-2"><a id="__codelineno-65-2" name="__codelineno-65-2" href="#__codelineno-65-2"></a>npm<span class="w"> </span>run<span class="w"> </span>dev
</span></code></pre></div>
<p><strong>Solution 3 (Admin/Vite):</strong> Clear Vite cache</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-66-1"><a id="__codelineno-66-1" name="__codelineno-66-1" href="#__codelineno-66-1"></a><span class="nb">cd</span><span class="w"> </span>admin
</span><span id="__span-66-2"><a id="__codelineno-66-2" name="__codelineno-66-2" href="#__codelineno-66-2"></a>rm<span class="w"> </span>-rf<span class="w"> </span>node_modules/.vite
</span><span id="__span-66-3"><a id="__codelineno-66-3" name="__codelineno-66-3" href="#__codelineno-66-3"></a>npm<span class="w"> </span>run<span class="w"> </span>dev
</span></code></pre></div>
<h3 id="admin-build-errors">Admin Build Errors<a class="headerlink" href="#admin-build-errors" title="Permanent link">&para;</a></h3>
<p><strong>Problem:</strong> TypeScript errors on build</p>
<div class="language-text highlight"><pre><span></span><code><span id="__span-67-1"><a id="__codelineno-67-1" name="__codelineno-67-1" href="#__codelineno-67-1"></a>error TS2339: Property &#39;foo&#39; does not exist on type &#39;Bar&#39;
</span></code></pre></div>
<p><strong>Solution 1:</strong> Type-check without emit</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-68-1"><a id="__codelineno-68-1" name="__codelineno-68-1" href="#__codelineno-68-1"></a><span class="nb">cd</span><span class="w"> </span>admin
</span><span id="__span-68-2"><a id="__codelineno-68-2" name="__codelineno-68-2" href="#__codelineno-68-2"></a>npx<span class="w"> </span>tsc<span class="w"> </span>--noEmit
</span><span id="__span-68-3"><a id="__codelineno-68-3" name="__codelineno-68-3" href="#__codelineno-68-3"></a><span class="c1"># Shows all type errors</span>
</span></code></pre></div>
<p><strong>Solution 2:</strong> Update type definitions</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-69-1"><a id="__codelineno-69-1" name="__codelineno-69-1" href="#__codelineno-69-1"></a><span class="nb">cd</span><span class="w"> </span>admin
</span><span id="__span-69-2"><a id="__codelineno-69-2" name="__codelineno-69-2" href="#__codelineno-69-2"></a>npm<span class="w"> </span>install<span class="w"> </span>--save-dev<span class="w"> </span>@types/react@latest<span class="w"> </span>@types/react-dom@latest
</span></code></pre></div>
<p><strong>Solution 3:</strong> Check tsconfig.json</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-70-1"><a id="__codelineno-70-1" name="__codelineno-70-1" href="#__codelineno-70-1"></a><span class="nb">cd</span><span class="w"> </span>admin
</span><span id="__span-70-2"><a id="__codelineno-70-2" name="__codelineno-70-2" href="#__codelineno-70-2"></a>cat<span class="w"> </span>tsconfig.json
</span><span id="__span-70-3"><a id="__codelineno-70-3" name="__codelineno-70-3" href="#__codelineno-70-3"></a><span class="c1"># Ensure &quot;strict&quot;: true and &quot;skipLibCheck&quot;: false</span>
</span></code></pre></div>
<h3 id="docker-container-crashes">Docker Container Crashes<a class="headerlink" href="#docker-container-crashes" title="Permanent link">&para;</a></h3>
<p><strong>Problem:</strong> API/Admin container exits immediately</p>
<p><strong>Solution 1:</strong> Check logs</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-71-1"><a id="__codelineno-71-1" name="__codelineno-71-1" href="#__codelineno-71-1"></a>docker<span class="w"> </span>compose<span class="w"> </span>logs<span class="w"> </span>api
</span><span id="__span-71-2"><a id="__codelineno-71-2" name="__codelineno-71-2" href="#__codelineno-71-2"></a><span class="c1"># Look for error messages</span>
</span></code></pre></div>
<p><strong>Solution 2:</strong> Verify .env file exists</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-72-1"><a id="__codelineno-72-1" name="__codelineno-72-1" href="#__codelineno-72-1"></a>ls<span class="w"> </span>-la<span class="w"> </span>.env
</span><span id="__span-72-2"><a id="__codelineno-72-2" name="__codelineno-72-2" href="#__codelineno-72-2"></a><span class="c1"># Should exist in project root</span>
</span></code></pre></div>
<p><strong>Solution 3:</strong> Rebuild containers</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-73-1"><a id="__codelineno-73-1" name="__codelineno-73-1" href="#__codelineno-73-1"></a>docker<span class="w"> </span>compose<span class="w"> </span>down
</span><span id="__span-73-2"><a id="__codelineno-73-2" name="__codelineno-73-2" href="#__codelineno-73-2"></a>docker<span class="w"> </span>compose<span class="w"> </span>build<span class="w"> </span>--no-cache<span class="w"> </span>api<span class="w"> </span>admin
</span><span id="__span-73-3"><a id="__codelineno-73-3" name="__codelineno-73-3" href="#__codelineno-73-3"></a>docker<span class="w"> </span>compose<span class="w"> </span>up<span class="w"> </span>-d<span class="w"> </span>api<span class="w"> </span>admin
</span></code></pre></div>
<h3 id="browser-cors-errors">Browser CORS Errors<a class="headerlink" href="#browser-cors-errors" title="Permanent link">&para;</a></h3>
<p><strong>Problem:</strong> Admin cannot call API (CORS errors in browser console)</p>
<p><strong>Solution 1:</strong> Check CORS_ORIGIN in .env</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-74-1"><a id="__codelineno-74-1" name="__codelineno-74-1" href="#__codelineno-74-1"></a><span class="c1"># For local development</span>
</span><span id="__span-74-2"><a id="__codelineno-74-2" name="__codelineno-74-2" href="#__codelineno-74-2"></a><span class="nv">CORS_ORIGIN</span><span class="o">=</span>http://localhost:3000
</span></code></pre></div>
<p><strong>Solution 2:</strong> Verify API_URL in admin</p>
<p>For Docker-based API, admin vite.config.ts proxy should work automatically.</p>
<p>For local API, ensure <code>VITE_API_URL</code> is NOT set (defaults to localhost:4000).</p>
<p><strong>Solution 3:</strong> Clear browser cache</p>
<ul>
<li>Open DevTools → Network tab → Disable cache</li>
<li>Hard reload (Cmd+Shift+R / Ctrl+Shift+R)</li>
</ul>
<h2 id="hot-reload">Hot Reload<a class="headerlink" href="#hot-reload" title="Permanent link">&para;</a></h2>
<h3 id="api-hot-reload-tsx-watch">API Hot Reload (tsx watch)<a class="headerlink" href="#api-hot-reload-tsx-watch" title="Permanent link">&para;</a></h3>
<p>API uses <code>tsx watch</code> for automatic restart on file changes:</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-75-1"><a id="__codelineno-75-1" name="__codelineno-75-1" href="#__codelineno-75-1"></a><span class="c1"># Started automatically with npm run dev</span>
</span><span id="__span-75-2"><a id="__codelineno-75-2" name="__codelineno-75-2" href="#__codelineno-75-2"></a><span class="nb">cd</span><span class="w"> </span>api
</span><span id="__span-75-3"><a id="__codelineno-75-3" name="__codelineno-75-3" href="#__codelineno-75-3"></a>npm<span class="w"> </span>run<span class="w"> </span>dev
</span></code></pre></div>
<p><strong>What triggers reload:</strong>
- Changes to <code>.ts</code> files in <code>src/</code>
- Changes to <code>.prisma</code> files (after running migrate)</p>
<p><strong>What does NOT trigger reload:</strong>
- Changes to <code>.env</code> (restart manually)
- Changes to <code>node_modules/</code> (reinstall packages)</p>
<p><strong>Manual restart:</strong>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-76-1"><a id="__codelineno-76-1" name="__codelineno-76-1" href="#__codelineno-76-1"></a><span class="c1"># If using npm run dev, just Ctrl+C and restart</span>
</span><span id="__span-76-2"><a id="__codelineno-76-2" name="__codelineno-76-2" href="#__codelineno-76-2"></a>npm<span class="w"> </span>run<span class="w"> </span>dev
</span></code></pre></div></p>
<h3 id="admin-hot-reload-vite-hmr">Admin Hot Reload (Vite HMR)<a class="headerlink" href="#admin-hot-reload-vite-hmr" title="Permanent link">&para;</a></h3>
<p>Admin uses Vite's Hot Module Replacement (HMR):</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-77-1"><a id="__codelineno-77-1" name="__codelineno-77-1" href="#__codelineno-77-1"></a><span class="nb">cd</span><span class="w"> </span>admin
</span><span id="__span-77-2"><a id="__codelineno-77-2" name="__codelineno-77-2" href="#__codelineno-77-2"></a>npm<span class="w"> </span>run<span class="w"> </span>dev
</span></code></pre></div>
<p><strong>What triggers HMR:</strong>
- Changes to <code>.tsx</code> / <code>.ts</code> files
- Changes to <code>.css</code> files
- Changes to imported assets</p>
<p><strong>HMR Behavior:</strong>
- Component changes: Updates without full reload
- Hook changes: May require full reload
- Route changes: Full reload</p>
<p><strong>Force full reload:</strong>
- Press <code>r</code> in terminal running Vite
- Or refresh browser (Cmd+R / Ctrl+R)</p>
<h3 id="docker-hot-reload">Docker Hot Reload<a class="headerlink" href="#docker-hot-reload" title="Permanent link">&para;</a></h3>
<p>Docker volume mounts enable hot reload in containers:</p>
<div class="language-yaml highlight"><pre><span></span><code><span id="__span-78-1"><a id="__codelineno-78-1" name="__codelineno-78-1" href="#__codelineno-78-1"></a><span class="c1"># docker-compose.yml</span>
</span><span id="__span-78-2"><a id="__codelineno-78-2" name="__codelineno-78-2" href="#__codelineno-78-2"></a><span class="nt">api</span><span class="p">:</span>
</span><span id="__span-78-3"><a id="__codelineno-78-3" name="__codelineno-78-3" href="#__codelineno-78-3"></a><span class="w"> </span><span class="nt">volumes</span><span class="p">:</span>
</span><span id="__span-78-4"><a id="__codelineno-78-4" name="__codelineno-78-4" href="#__codelineno-78-4"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">./api:/app</span><span class="w"> </span><span class="c1"># Syncs code changes</span>
</span><span id="__span-78-5"><a id="__codelineno-78-5" name="__codelineno-78-5" href="#__codelineno-78-5"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">/app/node_modules</span><span class="w"> </span><span class="c1"># Preserves container&#39;s node_modules</span>
</span></code></pre></div>
<p><strong>Same reload behavior as local:</strong>
- API: tsx watch restarts on .ts changes
- Admin: Vite HMR updates browser</p>
<p><strong>Performance note:</strong>
- macOS/Windows: Volume mounts slightly slower than Linux
- For intensive development, consider running locally instead</p>
<h2 id="debugging">Debugging<a class="headerlink" href="#debugging" title="Permanent link">&para;</a></h2>
<h3 id="api-debugging-vscode">API Debugging (VSCode)<a class="headerlink" href="#api-debugging-vscode" title="Permanent link">&para;</a></h3>
<ol>
<li>Open VSCode</li>
<li>Open Run and Debug panel (Cmd+Shift+D / Ctrl+Shift+D)</li>
<li>Select "Debug API" configuration</li>
<li>Press F5 to start debugging</li>
<li>Set breakpoints by clicking line numbers</li>
<li>Trigger API endpoint to hit breakpoint</li>
</ol>
<p><strong>Debugging features:</strong>
- Step through code (F10, F11)
- Inspect variables
- Evaluate expressions in Debug Console
- Call stack navigation</p>
<h3 id="frontend-debugging-chrome-devtools">Frontend Debugging (Chrome DevTools)<a class="headerlink" href="#frontend-debugging-chrome-devtools" title="Permanent link">&para;</a></h3>
<ol>
<li>Open Admin in Chrome: http://localhost:3000</li>
<li>Open DevTools (F12 / Cmd+Option+I)</li>
<li>Go to Sources tab</li>
<li>Find your component in file tree (webpack://./src/)</li>
<li>Set breakpoints by clicking line numbers</li>
<li>Interact with UI to trigger breakpoint</li>
</ol>
<p><strong>React DevTools:</strong>
- Install React DevTools browser extension
- Inspect component tree
- View/edit props and state
- Profile component renders</p>
<h3 id="zustand-devtools">Zustand DevTools<a class="headerlink" href="#zustand-devtools" title="Permanent link">&para;</a></h3>
<p>Enable Redux DevTools for Zustand stores:</p>
<div class="language-typescript highlight"><pre><span></span><code><span id="__span-79-1"><a id="__codelineno-79-1" name="__codelineno-79-1" href="#__codelineno-79-1"></a><span class="c1">// Already configured in auth.store.ts and canvass.store.ts</span>
</span><span id="__span-79-2"><a id="__codelineno-79-2" name="__codelineno-79-2" href="#__codelineno-79-2"></a><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">devtools</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;zustand/middleware&#39;</span><span class="p">;</span>
</span><span id="__span-79-3"><a id="__codelineno-79-3" name="__codelineno-79-3" href="#__codelineno-79-3"></a>
</span><span id="__span-79-4"><a id="__codelineno-79-4" name="__codelineno-79-4" href="#__codelineno-79-4"></a><span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">useAuthStore</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">create</span><span class="o">&lt;</span><span class="nx">AuthState</span><span class="o">&gt;</span><span class="p">()(</span>
</span><span id="__span-79-5"><a id="__codelineno-79-5" name="__codelineno-79-5" href="#__codelineno-79-5"></a><span class="w"> </span><span class="nx">devtools</span><span class="p">(</span>
</span><span id="__span-79-6"><a id="__codelineno-79-6" name="__codelineno-79-6" href="#__codelineno-79-6"></a><span class="w"> </span><span class="p">(</span><span class="nx">set</span><span class="p">,</span><span class="w"> </span><span class="nx">get</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">({</span>
</span><span id="__span-79-7"><a id="__codelineno-79-7" name="__codelineno-79-7" href="#__codelineno-79-7"></a><span class="w"> </span><span class="c1">// ... store implementation</span>
</span><span id="__span-79-8"><a id="__codelineno-79-8" name="__codelineno-79-8" href="#__codelineno-79-8"></a><span class="w"> </span><span class="p">}),</span>
</span><span id="__span-79-9"><a id="__codelineno-79-9" name="__codelineno-79-9" href="#__codelineno-79-9"></a><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;AuthStore&#39;</span><span class="w"> </span><span class="p">}</span>
</span><span id="__span-79-10"><a id="__codelineno-79-10" name="__codelineno-79-10" href="#__codelineno-79-10"></a><span class="w"> </span><span class="p">)</span>
</span><span id="__span-79-11"><a id="__codelineno-79-11" name="__codelineno-79-11" href="#__codelineno-79-11"></a><span class="p">);</span>
</span></code></pre></div>
<p><strong>Usage:</strong>
1. Install Redux DevTools browser extension
2. Open extension
3. Select "AuthStore" or "CanvassStore"
4. See action history and state changes</p>
<h2 id="common-workflows">Common Workflows<a class="headerlink" href="#common-workflows" title="Permanent link">&para;</a></h2>
<h3 id="starting-fresh-development-day">Starting Fresh Development Day<a class="headerlink" href="#starting-fresh-development-day" title="Permanent link">&para;</a></h3>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-80-1"><a id="__codelineno-80-1" name="__codelineno-80-1" href="#__codelineno-80-1"></a><span class="c1"># 1. Pull latest changes</span>
</span><span id="__span-80-2"><a id="__codelineno-80-2" name="__codelineno-80-2" href="#__codelineno-80-2"></a>git<span class="w"> </span>pull<span class="w"> </span>origin<span class="w"> </span>v2
</span><span id="__span-80-3"><a id="__codelineno-80-3" name="__codelineno-80-3" href="#__codelineno-80-3"></a>
</span><span id="__span-80-4"><a id="__codelineno-80-4" name="__codelineno-80-4" href="#__codelineno-80-4"></a><span class="c1"># 2. Check for dependency updates</span>
</span><span id="__span-80-5"><a id="__codelineno-80-5" name="__codelineno-80-5" href="#__codelineno-80-5"></a><span class="nb">cd</span><span class="w"> </span>api<span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span>npm<span class="w"> </span>install<span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="nb">cd</span><span class="w"> </span>..
</span><span id="__span-80-6"><a id="__codelineno-80-6" name="__codelineno-80-6" href="#__codelineno-80-6"></a><span class="nb">cd</span><span class="w"> </span>admin<span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span>npm<span class="w"> </span>install<span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="nb">cd</span><span class="w"> </span>..
</span><span id="__span-80-7"><a id="__codelineno-80-7" name="__codelineno-80-7" href="#__codelineno-80-7"></a>
</span><span id="__span-80-8"><a id="__codelineno-80-8" name="__codelineno-80-8" href="#__codelineno-80-8"></a><span class="c1"># 3. Apply any new migrations</span>
</span><span id="__span-80-9"><a id="__codelineno-80-9" name="__codelineno-80-9" href="#__codelineno-80-9"></a><span class="nb">cd</span><span class="w"> </span>api<span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span>npx<span class="w"> </span>prisma<span class="w"> </span>migrate<span class="w"> </span>deploy<span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="nb">cd</span><span class="w"> </span>..
</span><span id="__span-80-10"><a id="__codelineno-80-10" name="__codelineno-80-10" href="#__codelineno-80-10"></a>
</span><span id="__span-80-11"><a id="__codelineno-80-11" name="__codelineno-80-11" href="#__codelineno-80-11"></a><span class="c1"># 4. Start services</span>
</span><span id="__span-80-12"><a id="__codelineno-80-12" name="__codelineno-80-12" href="#__codelineno-80-12"></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 class="w"> </span>mailhog
</span><span id="__span-80-13"><a id="__codelineno-80-13" name="__codelineno-80-13" href="#__codelineno-80-13"></a><span class="c1"># Either:</span>
</span><span id="__span-80-14"><a id="__codelineno-80-14" name="__codelineno-80-14" href="#__codelineno-80-14"></a>docker<span class="w"> </span>compose<span class="w"> </span>up<span class="w"> </span>-d<span class="w"> </span>api<span class="w"> </span>admin<span class="w"> </span><span class="c1"># Docker approach</span>
</span><span id="__span-80-15"><a id="__codelineno-80-15" name="__codelineno-80-15" href="#__codelineno-80-15"></a><span class="c1"># Or:</span>
</span><span id="__span-80-16"><a id="__codelineno-80-16" name="__codelineno-80-16" href="#__codelineno-80-16"></a><span class="nb">cd</span><span class="w"> </span>api<span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span>npm<span class="w"> </span>run<span class="w"> </span>dev<span class="w"> </span><span class="c1"># Terminal 1 (local approach)</span>
</span><span id="__span-80-17"><a id="__codelineno-80-17" name="__codelineno-80-17" href="#__codelineno-80-17"></a><span class="nb">cd</span><span class="w"> </span>admin<span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span>npm<span class="w"> </span>run<span class="w"> </span>dev<span class="w"> </span><span class="c1"># Terminal 2 (local approach)</span>
</span><span id="__span-80-18"><a id="__codelineno-80-18" name="__codelineno-80-18" href="#__codelineno-80-18"></a>
</span><span id="__span-80-19"><a id="__codelineno-80-19" name="__codelineno-80-19" href="#__codelineno-80-19"></a><span class="c1"># 5. Open browser</span>
</span><span id="__span-80-20"><a id="__codelineno-80-20" name="__codelineno-80-20" href="#__codelineno-80-20"></a>open<span class="w"> </span>http://localhost:3000
</span></code></pre></div>
<h3 id="feature-development-workflow">Feature Development Workflow<a class="headerlink" href="#feature-development-workflow" title="Permanent link">&para;</a></h3>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-81-1"><a id="__codelineno-81-1" name="__codelineno-81-1" href="#__codelineno-81-1"></a><span class="c1"># 1. Create feature branch</span>
</span><span id="__span-81-2"><a id="__codelineno-81-2" name="__codelineno-81-2" href="#__codelineno-81-2"></a>git<span class="w"> </span>checkout<span class="w"> </span>-b<span class="w"> </span>feature/my-new-feature
</span><span id="__span-81-3"><a id="__codelineno-81-3" name="__codelineno-81-3" href="#__codelineno-81-3"></a>
</span><span id="__span-81-4"><a id="__codelineno-81-4" name="__codelineno-81-4" href="#__codelineno-81-4"></a><span class="c1"># 2. Start development servers (see above)</span>
</span><span id="__span-81-5"><a id="__codelineno-81-5" name="__codelineno-81-5" href="#__codelineno-81-5"></a>
</span><span id="__span-81-6"><a id="__codelineno-81-6" name="__codelineno-81-6" href="#__codelineno-81-6"></a><span class="c1"># 3. Make changes</span>
</span><span id="__span-81-7"><a id="__codelineno-81-7" name="__codelineno-81-7" href="#__codelineno-81-7"></a><span class="c1"># - Edit code</span>
</span><span id="__span-81-8"><a id="__codelineno-81-8" name="__codelineno-81-8" href="#__codelineno-81-8"></a><span class="c1"># - Test in browser</span>
</span><span id="__span-81-9"><a id="__codelineno-81-9" name="__codelineno-81-9" href="#__codelineno-81-9"></a><span class="c1"># - Check API responses</span>
</span><span id="__span-81-10"><a id="__codelineno-81-10" name="__codelineno-81-10" href="#__codelineno-81-10"></a>
</span><span id="__span-81-11"><a id="__codelineno-81-11" name="__codelineno-81-11" href="#__codelineno-81-11"></a><span class="c1"># 4. Type-check</span>
</span><span id="__span-81-12"><a id="__codelineno-81-12" name="__codelineno-81-12" href="#__codelineno-81-12"></a><span class="nb">cd</span><span class="w"> </span>api<span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span>npx<span class="w"> </span>tsc<span class="w"> </span>--noEmit<span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="nb">cd</span><span class="w"> </span>..
</span><span id="__span-81-13"><a id="__codelineno-81-13" name="__codelineno-81-13" href="#__codelineno-81-13"></a><span class="nb">cd</span><span class="w"> </span>admin<span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span>npx<span class="w"> </span>tsc<span class="w"> </span>--noEmit<span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="nb">cd</span><span class="w"> </span>..
</span><span id="__span-81-14"><a id="__codelineno-81-14" name="__codelineno-81-14" href="#__codelineno-81-14"></a>
</span><span id="__span-81-15"><a id="__codelineno-81-15" name="__codelineno-81-15" href="#__codelineno-81-15"></a><span class="c1"># 5. Run tests (when available)</span>
</span><span id="__span-81-16"><a id="__codelineno-81-16" name="__codelineno-81-16" href="#__codelineno-81-16"></a><span class="c1"># cd api &amp;&amp; npm test &amp;&amp; cd ..</span>
</span><span id="__span-81-17"><a id="__codelineno-81-17" name="__codelineno-81-17" href="#__codelineno-81-17"></a><span class="c1"># cd admin &amp;&amp; npm test &amp;&amp; cd ..</span>
</span><span id="__span-81-18"><a id="__codelineno-81-18" name="__codelineno-81-18" href="#__codelineno-81-18"></a>
</span><span id="__span-81-19"><a id="__codelineno-81-19" name="__codelineno-81-19" href="#__codelineno-81-19"></a><span class="c1"># 6. Commit changes</span>
</span><span id="__span-81-20"><a id="__codelineno-81-20" name="__codelineno-81-20" href="#__codelineno-81-20"></a>git<span class="w"> </span>add<span class="w"> </span>.
</span><span id="__span-81-21"><a id="__codelineno-81-21" name="__codelineno-81-21" href="#__codelineno-81-21"></a>git<span class="w"> </span>commit<span class="w"> </span>-m<span class="w"> </span><span class="s2">&quot;feat: add new feature&quot;</span>
</span><span id="__span-81-22"><a id="__codelineno-81-22" name="__codelineno-81-22" href="#__codelineno-81-22"></a>
</span><span id="__span-81-23"><a id="__codelineno-81-23" name="__codelineno-81-23" href="#__codelineno-81-23"></a><span class="c1"># 7. Push and create PR</span>
</span><span id="__span-81-24"><a id="__codelineno-81-24" name="__codelineno-81-24" href="#__codelineno-81-24"></a>git<span class="w"> </span>push<span class="w"> </span>origin<span class="w"> </span>feature/my-new-feature
</span><span id="__span-81-25"><a id="__codelineno-81-25" name="__codelineno-81-25" href="#__codelineno-81-25"></a><span class="c1"># Open PR on GitHub/GitLab</span>
</span></code></pre></div>
<h3 id="database-schema-changes">Database Schema Changes<a class="headerlink" href="#database-schema-changes" title="Permanent link">&para;</a></h3>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-82-1"><a id="__codelineno-82-1" name="__codelineno-82-1" href="#__codelineno-82-1"></a><span class="c1"># 1. Edit Prisma schema</span>
</span><span id="__span-82-2"><a id="__codelineno-82-2" name="__codelineno-82-2" href="#__codelineno-82-2"></a><span class="nb">cd</span><span class="w"> </span>api
</span><span id="__span-82-3"><a id="__codelineno-82-3" name="__codelineno-82-3" href="#__codelineno-82-3"></a>vi<span class="w"> </span>prisma/schema.prisma<span class="w"> </span><span class="c1"># Add/modify models</span>
</span><span id="__span-82-4"><a id="__codelineno-82-4" name="__codelineno-82-4" href="#__codelineno-82-4"></a>
</span><span id="__span-82-5"><a id="__codelineno-82-5" name="__codelineno-82-5" href="#__codelineno-82-5"></a><span class="c1"># 2. Create migration</span>
</span><span id="__span-82-6"><a id="__codelineno-82-6" name="__codelineno-82-6" href="#__codelineno-82-6"></a>npx<span class="w"> </span>prisma<span class="w"> </span>migrate<span class="w"> </span>dev<span class="w"> </span>--name<span class="w"> </span>add_new_field
</span><span id="__span-82-7"><a id="__codelineno-82-7" name="__codelineno-82-7" href="#__codelineno-82-7"></a>
</span><span id="__span-82-8"><a id="__codelineno-82-8" name="__codelineno-82-8" href="#__codelineno-82-8"></a><span class="c1"># 3. Migration auto-applies to dev database</span>
</span><span id="__span-82-9"><a id="__codelineno-82-9" name="__codelineno-82-9" href="#__codelineno-82-9"></a><span class="c1"># Check generated SQL in prisma/migrations/</span>
</span><span id="__span-82-10"><a id="__codelineno-82-10" name="__codelineno-82-10" href="#__codelineno-82-10"></a>
</span><span id="__span-82-11"><a id="__codelineno-82-11" name="__codelineno-82-11" href="#__codelineno-82-11"></a><span class="c1"># 4. Update seed if needed</span>
</span><span id="__span-82-12"><a id="__codelineno-82-12" name="__codelineno-82-12" href="#__codelineno-82-12"></a>vi<span class="w"> </span>prisma/seed.ts
</span><span id="__span-82-13"><a id="__codelineno-82-13" name="__codelineno-82-13" href="#__codelineno-82-13"></a>
</span><span id="__span-82-14"><a id="__codelineno-82-14" name="__codelineno-82-14" href="#__codelineno-82-14"></a><span class="c1"># 5. Test migration on clean database</span>
</span><span id="__span-82-15"><a id="__codelineno-82-15" name="__codelineno-82-15" href="#__codelineno-82-15"></a>npx<span class="w"> </span>prisma<span class="w"> </span>migrate<span class="w"> </span>reset<span class="w"> </span><span class="c1"># WARNING: Deletes data</span>
</span><span id="__span-82-16"><a id="__codelineno-82-16" name="__codelineno-82-16" href="#__codelineno-82-16"></a><span class="c1"># Re-run migrations + seed</span>
</span><span id="__span-82-17"><a id="__codelineno-82-17" name="__codelineno-82-17" href="#__codelineno-82-17"></a>
</span><span id="__span-82-18"><a id="__codelineno-82-18" name="__codelineno-82-18" href="#__codelineno-82-18"></a><span class="c1"># 6. Commit migration files</span>
</span><span id="__span-82-19"><a id="__codelineno-82-19" name="__codelineno-82-19" href="#__codelineno-82-19"></a>git<span class="w"> </span>add<span class="w"> </span>prisma/migrations/<span class="w"> </span>prisma/schema.prisma
</span><span id="__span-82-20"><a id="__codelineno-82-20" name="__codelineno-82-20" href="#__codelineno-82-20"></a>git<span class="w"> </span>commit<span class="w"> </span>-m<span class="w"> </span><span class="s2">&quot;feat(db): add new field to User model&quot;</span>
</span></code></pre></div>
<h3 id="bug-fixing-workflow">Bug Fixing Workflow<a class="headerlink" href="#bug-fixing-workflow" title="Permanent link">&para;</a></h3>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-83-1"><a id="__codelineno-83-1" name="__codelineno-83-1" href="#__codelineno-83-1"></a><span class="c1"># 1. Reproduce bug locally</span>
</span><span id="__span-83-2"><a id="__codelineno-83-2" name="__codelineno-83-2" href="#__codelineno-83-2"></a><span class="c1"># - Follow steps from bug report</span>
</span><span id="__span-83-3"><a id="__codelineno-83-3" name="__codelineno-83-3" href="#__codelineno-83-3"></a><span class="c1"># - Check browser console</span>
</span><span id="__span-83-4"><a id="__codelineno-83-4" name="__codelineno-83-4" href="#__codelineno-83-4"></a><span class="c1"># - Check API logs (docker compose logs -f api)</span>
</span><span id="__span-83-5"><a id="__codelineno-83-5" name="__codelineno-83-5" href="#__codelineno-83-5"></a>
</span><span id="__span-83-6"><a id="__codelineno-83-6" name="__codelineno-83-6" href="#__codelineno-83-6"></a><span class="c1"># 2. Add logging to isolate issue</span>
</span><span id="__span-83-7"><a id="__codelineno-83-7" name="__codelineno-83-7" href="#__codelineno-83-7"></a><span class="c1"># api/src/modules/foo/foo.service.ts</span>
</span><span id="__span-83-8"><a id="__codelineno-83-8" name="__codelineno-83-8" href="#__codelineno-83-8"></a>logger.error<span class="o">(</span><span class="s1">&#39;Bug context&#39;</span>,<span class="w"> </span><span class="o">{</span><span class="w"> </span>data<span class="w"> </span><span class="o">})</span><span class="p">;</span>
</span><span id="__span-83-9"><a id="__codelineno-83-9" name="__codelineno-83-9" href="#__codelineno-83-9"></a>
</span><span id="__span-83-10"><a id="__codelineno-83-10" name="__codelineno-83-10" href="#__codelineno-83-10"></a><span class="c1"># 3. Set breakpoints (VSCode debug)</span>
</span><span id="__span-83-11"><a id="__codelineno-83-11" name="__codelineno-83-11" href="#__codelineno-83-11"></a><span class="c1"># - Run &quot;Debug API&quot; configuration</span>
</span><span id="__span-83-12"><a id="__codelineno-83-12" name="__codelineno-83-12" href="#__codelineno-83-12"></a><span class="c1"># - Trigger bug</span>
</span><span id="__span-83-13"><a id="__codelineno-83-13" name="__codelineno-83-13" href="#__codelineno-83-13"></a><span class="c1"># - Step through code</span>
</span><span id="__span-83-14"><a id="__codelineno-83-14" name="__codelineno-83-14" href="#__codelineno-83-14"></a>
</span><span id="__span-83-15"><a id="__codelineno-83-15" name="__codelineno-83-15" href="#__codelineno-83-15"></a><span class="c1"># 4. Fix bug</span>
</span><span id="__span-83-16"><a id="__codelineno-83-16" name="__codelineno-83-16" href="#__codelineno-83-16"></a><span class="c1"># - Make code changes</span>
</span><span id="__span-83-17"><a id="__codelineno-83-17" name="__codelineno-83-17" href="#__codelineno-83-17"></a><span class="c1"># - Hot reload picks up changes</span>
</span><span id="__span-83-18"><a id="__codelineno-83-18" name="__codelineno-83-18" href="#__codelineno-83-18"></a><span class="c1"># - Test fix</span>
</span><span id="__span-83-19"><a id="__codelineno-83-19" name="__codelineno-83-19" href="#__codelineno-83-19"></a>
</span><span id="__span-83-20"><a id="__codelineno-83-20" name="__codelineno-83-20" href="#__codelineno-83-20"></a><span class="c1"># 5. Verify fix</span>
</span><span id="__span-83-21"><a id="__codelineno-83-21" name="__codelineno-83-21" href="#__codelineno-83-21"></a><span class="c1"># - Re-test original bug steps</span>
</span><span id="__span-83-22"><a id="__codelineno-83-22" name="__codelineno-83-22" href="#__codelineno-83-22"></a><span class="c1"># - Check related functionality</span>
</span><span id="__span-83-23"><a id="__codelineno-83-23" name="__codelineno-83-23" href="#__codelineno-83-23"></a><span class="c1"># - Type-check: npx tsc --noEmit</span>
</span><span id="__span-83-24"><a id="__codelineno-83-24" name="__codelineno-83-24" href="#__codelineno-83-24"></a>
</span><span id="__span-83-25"><a id="__codelineno-83-25" name="__codelineno-83-25" href="#__codelineno-83-25"></a><span class="c1"># 6. Commit fix</span>
</span><span id="__span-83-26"><a id="__codelineno-83-26" name="__codelineno-83-26" href="#__codelineno-83-26"></a>git<span class="w"> </span>add<span class="w"> </span>.
</span><span id="__span-83-27"><a id="__codelineno-83-27" name="__codelineno-83-27" href="#__codelineno-83-27"></a>git<span class="w"> </span>commit<span class="w"> </span>-m<span class="w"> </span><span class="s2">&quot;fix: resolve issue with user login&quot;</span>
</span></code></pre></div>
<h3 id="switching-between-docker-and-local">Switching Between Docker and Local<a class="headerlink" href="#switching-between-docker-and-local" title="Permanent link">&para;</a></h3>
<p><strong>From Docker to Local:</strong></p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-84-1"><a id="__codelineno-84-1" name="__codelineno-84-1" href="#__codelineno-84-1"></a><span class="c1"># 1. Stop Docker services</span>
</span><span id="__span-84-2"><a id="__codelineno-84-2" name="__codelineno-84-2" href="#__codelineno-84-2"></a>docker<span class="w"> </span>compose<span class="w"> </span>stop<span class="w"> </span>api<span class="w"> </span>admin
</span><span id="__span-84-3"><a id="__codelineno-84-3" name="__codelineno-84-3" href="#__codelineno-84-3"></a>
</span><span id="__span-84-4"><a id="__codelineno-84-4" name="__codelineno-84-4" href="#__codelineno-84-4"></a><span class="c1"># 2. Keep databases running</span>
</span><span id="__span-84-5"><a id="__codelineno-84-5" name="__codelineno-84-5" href="#__codelineno-84-5"></a>docker<span class="w"> </span>compose<span class="w"> </span>ps<span class="w"> </span>v2-postgres<span class="w"> </span>redis<span class="w"> </span>mailhog
</span><span id="__span-84-6"><a id="__codelineno-84-6" name="__codelineno-84-6" href="#__codelineno-84-6"></a><span class="c1"># Should show running</span>
</span><span id="__span-84-7"><a id="__codelineno-84-7" name="__codelineno-84-7" href="#__codelineno-84-7"></a>
</span><span id="__span-84-8"><a id="__codelineno-84-8" name="__codelineno-84-8" href="#__codelineno-84-8"></a><span class="c1"># 3. Start local dev servers</span>
</span><span id="__span-84-9"><a id="__codelineno-84-9" name="__codelineno-84-9" href="#__codelineno-84-9"></a><span class="nb">cd</span><span class="w"> </span>api<span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span>npm<span class="w"> </span>run<span class="w"> </span>dev<span class="w"> </span><span class="c1"># Terminal 1</span>
</span><span id="__span-84-10"><a id="__codelineno-84-10" name="__codelineno-84-10" href="#__codelineno-84-10"></a><span class="nb">cd</span><span class="w"> </span>admin<span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span>npm<span class="w"> </span>run<span class="w"> </span>dev<span class="w"> </span><span class="c1"># Terminal 2</span>
</span></code></pre></div>
<p><strong>From Local to Docker:</strong></p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-85-1"><a id="__codelineno-85-1" name="__codelineno-85-1" href="#__codelineno-85-1"></a><span class="c1"># 1. Stop local dev servers</span>
</span><span id="__span-85-2"><a id="__codelineno-85-2" name="__codelineno-85-2" href="#__codelineno-85-2"></a><span class="c1"># Press Ctrl+C in both terminals</span>
</span><span id="__span-85-3"><a id="__codelineno-85-3" name="__codelineno-85-3" href="#__codelineno-85-3"></a>
</span><span id="__span-85-4"><a id="__codelineno-85-4" name="__codelineno-85-4" href="#__codelineno-85-4"></a><span class="c1"># 2. Start Docker services</span>
</span><span id="__span-85-5"><a id="__codelineno-85-5" name="__codelineno-85-5" href="#__codelineno-85-5"></a>docker<span class="w"> </span>compose<span class="w"> </span>up<span class="w"> </span>-d<span class="w"> </span>api<span class="w"> </span>admin
</span><span id="__span-85-6"><a id="__codelineno-85-6" name="__codelineno-85-6" href="#__codelineno-85-6"></a>
</span><span id="__span-85-7"><a id="__codelineno-85-7" name="__codelineno-85-7" href="#__codelineno-85-7"></a><span class="c1"># 3. Watch logs</span>
</span><span id="__span-85-8"><a id="__codelineno-85-8" name="__codelineno-85-8" href="#__codelineno-85-8"></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></code></pre></div>
<h2 id="next-steps">Next Steps<a class="headerlink" href="#next-steps" title="Permanent link">&para;</a></h2>
<p>After completing local setup:</p>
<ol>
<li><strong>Read Development Guides:</strong></li>
<li><a href="../npm-commands/">NPM Commands Reference</a> - All package.json scripts</li>
<li><a href="../docker-workflow/">Docker Workflow</a> - Advanced Docker development</li>
<li>
<p><a href="../migrations/">Database Migrations</a> - Schema change workflow</p>
</li>
<li>
<p><strong>Understand Architecture:</strong></p>
</li>
<li><a href="../architecture/api-structure.md">API Architecture</a> - Backend organization</li>
<li><a href="../architecture/frontend-structure.md">Frontend Architecture</a> - React app structure</li>
<li>
<p><a href="../architecture/database-schema.md">Database Schema</a> - Data models</p>
</li>
<li>
<p><strong>Learn Code Patterns:</strong></p>
</li>
<li><a href="../typescript/">TypeScript Guide</a> - TypeScript best practices</li>
<li><a href="../code-style/">Code Style Guide</a> - Coding standards</li>
<li>
<p><a href="../testing/">Testing Guide</a> - Test writing</p>
</li>
<li>
<p><strong>Start Contributing:</strong></p>
</li>
<li><a href="../git-workflow/">Git Workflow</a> - Branching and commits</li>
<li><a href="../contributing.md">Contributing Guide</a> - Contribution process</li>
<li><a href="../V2_PLAN.md">V2 Development Plan</a> - Roadmap and phases</li>
</ol>
<h2 id="related-documentation">Related Documentation<a class="headerlink" href="#related-documentation" title="Permanent link">&para;</a></h2>
<ul>
<li><strong>Deployment:</strong> <a href="../../deployment/docker-compose/">Docker Compose Deployment</a></li>
<li><strong>Configuration:</strong> <a href="../../deployment/environment-variables/">Environment Variables</a></li>
<li><strong>Database:</strong> <a href="../migrations/">Migrations Guide</a></li>
<li><strong>Testing:</strong> <a href="../testing/">Testing Strategy</a></li>
<li><strong>Debugging:</strong> <a href="../debugging/">Debugging Guide</a></li>
</ul>
<h2 id="getting-help">Getting Help<a class="headerlink" href="#getting-help" title="Permanent link">&para;</a></h2>
<p><strong>Documentation:</strong>
- This guide for setup issues
- <a href="../troubleshooting.md">Troubleshooting</a> for common problems
- <a href="../faq.md">FAQ</a> for quick answers</p>
<p><strong>Community:</strong>
- GitHub Issues for bug reports
- GitHub Discussions for questions
- Project README for contact info</p>
<p><strong>Logs:</strong>
- API logs: <code>docker compose logs -f api</code>
- Admin logs: <code>docker compose logs -f admin</code>
- Database logs: <code>docker compose logs -f v2-postgres</code></p>
<h2 id="summary">Summary<a class="headerlink" href="#summary" title="Permanent link">&para;</a></h2>
<p>You now have:
- ✅ Prerequisites installed (Node.js, Docker, Git)
- ✅ Repository cloned and on v2 branch
- ✅ Environment configured (.env file)
- ✅ Database initialized (migrations + seed)
- ✅ Services running (API + Admin + databases)
- ✅ IDE configured (VSCode with extensions)
- ✅ Admin GUI accessible (http://localhost:3000)</p>
<p><strong>Test your setup:</strong>
1. Login to Admin GUI (admin@example.com / Admin123!)
2. Navigate to Users page (/app/users)
3. See yourself in the users table
4. Check MailHog (http://localhost:8025) for welcome email</p>
<p><strong>Ready to develop!</strong> Choose a task from <a href="../V2_PLAN.md">V2_PLAN.md</a> Phase 15 or create a feature branch.</p>
</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="../" class="md-footer__link md-footer__link--prev" aria-label="Previous: Development Guide">
<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">
Development Guide
</div>
</div>
</a>
<a href="../docker-workflow/" class="md-footer__link md-footer__link--next" aria-label="Next: Docker Workflow">
<div class="md-footer__title">
<span class="md-footer__direction">
Next
</span>
<div class="md-ellipsis">
Docker Workflow
</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>