[{"data":1,"prerenderedAt":4},["ShallowReactive",2],{"readme-html:EDM115\u002Fspendly:master":3},"\u003Cdiv align=\"center\">\n\u003Cpicture>\n  \u003Csource width=\"300\" height=\"300\" media=\"(prefers-color-scheme: dark)\" srcset=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fpublic\u002Fimages\u002Flogo.webp\">\n  \u003Csource width=\"300\" height=\"300\" media=\"(prefers-color-scheme: light)\" srcset=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fpublic\u002Fimages\u002Flogo_alt.webp\">\n  \u003Cimg alt=\"Spendly\" width=\"300\" height=\"300\" src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fpublic\u002Fimages\u002Flogo.webp\">\n\u003C\u002Fpicture>\n\u003Ch1 id=\"spendly\" tabindex=\"-1\">\u003Cspan\n                class=\"header-copy-icon\"\n                role=\"button\"\n                data-slug=\"spendly\"\n              >\n                \u003Csvg>\u003Cpath fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.98 2.98 0 0 0 0-4.24a2.98 2.98 0 0 0-4.24 0l-3.53 3.53a2.98 2.98 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.98 2.98 0 0 0 0 4.24a2.98 2.98 0 0 0 4.24 0l3.53-3.53a2.98 2.98 0 0 0 0-4.24a.973.973 0 0 1 0-1.42\"\u002F>\u003C\u002Fsvg>\n              \u003C\u002Fspan>\u003Ca class=\"header-anchor\" href=\"#spendly\">spendly\u003C\u002Fa>\u003C\u002Fh1>\n\u003Cp>Simple, powerful, and free budget tracking for everyone with tables, stats and charts\u003Cbr>\nMade for my gf\u003C\u002Fp>\n\u003C\u002Fdiv>\n\u003Ch2 id=\"what-is-it\" tabindex=\"-1\">\u003Cspan\n                class=\"header-copy-icon\"\n                role=\"button\"\n                data-slug=\"what-is-it\"\n              >\n                \u003Csvg>\u003Cpath fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.98 2.98 0 0 0 0-4.24a2.98 2.98 0 0 0-4.24 0l-3.53 3.53a2.98 2.98 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.98 2.98 0 0 0 0 4.24a2.98 2.98 0 0 0 4.24 0l3.53-3.53a2.98 2.98 0 0 0 0-4.24a.973.973 0 0 1 0-1.42\"\u002F>\u003C\u002Fsvg>\n              \u003C\u002Fspan>\u003Ca class=\"header-anchor\" href=\"#what-is-it\">What is it ?\u003C\u002Fa>\u003C\u002Fh2>\n\u003Cp>Spendly is a web application designed to help you track your budget and expenses in a simple and efficient way. It offers a user-friendly interface where you can easily manage your finances, categorize your spending, and visualize your financial data through charts and statistics. Whether you’re looking to keep a close eye on your daily expenses or plan for long-term financial goals, Spendly provides the tools you need to stay on top of your budget.\u003C\u002Fp>\n\u003Ch2 id=\"user-documentation\" tabindex=\"-1\">\u003Cspan\n                class=\"header-copy-icon\"\n                role=\"button\"\n                data-slug=\"user-documentation\"\n              >\n                \u003Csvg>\u003Cpath fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.98 2.98 0 0 0 0-4.24a2.98 2.98 0 0 0-4.24 0l-3.53 3.53a2.98 2.98 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.98 2.98 0 0 0 0 4.24a2.98 2.98 0 0 0 4.24 0l3.53-3.53a2.98 2.98 0 0 0 0-4.24a.973.973 0 0 1 0-1.42\"\u002F>\u003C\u002Fsvg>\n              \u003C\u002Fspan>\u003Ca class=\"header-anchor\" href=\"#user-documentation\">User documentation\u003C\u002Fa>\u003C\u002Fh2>\n\u003Cp>Hi 👋\u003Cbr>\nYou’ll find here everything you need to know to use Spendly.\u003Cbr>\n\u003Cbr>\n\u003C\u002Fp>\n\u003Cp>The homepage lists all features of Spendly, with some technical details too.\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fhome.png\" alt=\"Home\" loading=\"lazy\">\u003Cbr>\n\u003Cbr>\n\u003C\u002Fp>\n\u003Cp>The best way to understand Spendly is to check the demo.\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fdemo.png\" alt=\"Demo\" loading=\"lazy\">\u003Cbr>\n\u003Cbr>\n\u003C\u002Fp>\n\u003Cp>The app also allows you to install it as a PWA on your device, and access it from anywhere. It is responsive and works on all screen sizes. It will also warn you when a new version is available, so you can always have the latest features and bug fixes.\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fpwa.png\" alt=\"PWA\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fupdate.png\" alt=\"Update\" loading=\"lazy\">\u003Cbr>\n\u003Cbr>\n\u003C\u002Fp>\n\u003Cp>The app is available in English and French, and have both light and dark modes. Use the Menu to switch between them, log in\u002Fout and access your account settings.\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Flight-mode.png\" alt=\"Light mode\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Ffrench.png\" alt=\"French\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fmenu.png\" alt=\"Menu\" loading=\"lazy\">\u003Cbr>\n\u003Cbr>\n\u003C\u002Fp>\n\u003Cp>Go to the login page once you’re ready. You can create an account. Afterwards, simply login with your username\u002Femail and password, or use OAuth once linked in your account settings.\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Flogin.png\" alt=\"Login\" loading=\"lazy\">\u003Cbr>\n\u003Cbr>\n\u003C\u002Fp>\n\u003Cp>Speaking of those accounts settings, they allow you to take multiple account-related actions.\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Faccount-1.png\" alt=\"Account 1\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Faccount-2.png\" alt=\"Account 2\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Faccount-3.png\" alt=\"Account 3\" loading=\"lazy\">\u003Cbr>\n\u003Cbr>\n\u003C\u002Fp>\n\u003Cp>The first step is to create a budget tracker, which is a container for your financial data. You can create as many as you want, and share them with other users with different permissions (viewer\u002Feditor\u002Fadmin).\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fno-budget.png\" alt=\"No budget\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fbudget-list.png\" alt=\"Budget list\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fbudget-share.png\" alt=\"Budget share\" loading=\"lazy\">\u003Cbr>\n\u003Cbr>\n\u003C\u002Fp>\n\u003Cp>Once done, you can start adding categories. It have a built-in icon search.\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fno-categories.png\" alt=\"No categories\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fcategory-icon-1.png\" alt=\"Category icon 1\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fcategory-icon-2.png\" alt=\"Category icon 2\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fcategories.png\" alt=\"Categories\" loading=\"lazy\">\u003Cbr>\n\u003Cbr>\n\u003C\u002Fp>\n\u003Cp>Then, it is time to add transactions. You can specify a name, amount, date, category, and whether it’s an expense or an income. You can also edit or delete them anytime.\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fno-transactions.png\" alt=\"No transactions\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fadd-transaction.png\" alt=\"Add transaction\" loading=\"lazy\">\u003Cbr>\n\u003Cbr>\n\u003C\u002Fp>\n\u003Cp>In the transactions table, you can search for any transaction and sort by any column. You can also export transactions as a CSV or JSON file.\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fsearch.png\" alt=\"Search\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Ftransaction-export.png\" alt=\"Transaction export\" loading=\"lazy\">\u003Cbr>\n\u003Cbr>\n\u003C\u002Fp>\n\u003Cp>The table is replaced by cards on mobile for better readability.\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fmobile.png\" alt=\"Mobile\" loading=\"lazy\">\u003Cbr>\n\u003Cbr>\n\u003C\u002Fp>\n\u003Cp>You can filter the list of displayed transactions by any date range (this also affects the charts). Hovering over the icon will tell you exactly which time range it corresponds to.\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fno-charts.png\" alt=\"No charts\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fperiod-day.png\" alt=\"Period day\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fperiod-week.png\" alt=\"Period week\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fperiod-month-1.png\" alt=\"Period month 1\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fperiod-month-2.png\" alt=\"Period month 2\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fperiod-year.png\" alt=\"Period year\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fperiod-tooltip.png\" alt=\"Period tooltip\" loading=\"lazy\">\u003Cbr>\n\u003Cbr>\n\u003C\u002Fp>\n\u003Cp>Depending on the date range, you’ll see an according summary of your expanses\u002Fincome and your balance. You have options to take also into account past, future, or all transactions.\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fbalance-positive.png\" alt=\"Balance positive\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fbalance-negative.png\" alt=\"Balance negative\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fbalance-options.png\" alt=\"Balance options\" loading=\"lazy\">\u003Cbr>\n\u003Cbr>\n\u003C\u002Fp>\n\u003Cp>Finally, you have 4 types of charts to visualize your financial data in a more digestible way. They all have options to toggle some details on\u002Foff, and you can hover over data points for more details.\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fcharts.png\" alt=\"Charts\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fcharts-evolution.png\" alt=\"Charts evolution\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fcharts-repartition.png\" alt=\"Charts repartition\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fcharts-comparison.png\" alt=\"Charts comparison\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fcharts-distribution.png\" alt=\"Charts distribution\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fcharts-options.png\" alt=\"Charts options\" loading=\"lazy\">\u003Cbr>\n\u003Cbr>\n\u003C\u002Fp>\n\u003Cp>All charts have a simplified mode that turns off all details, and can be downloaded as SVG (infinite resolution), PNG or PDF.\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fcharts-simplified.png\" alt=\"Charts simplified\" loading=\"lazy\">\u003Cbr>\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FEDM115\u002Fspendly\u002Fmaster\u002Fdocs\u002Fcharts-download.png\" alt=\"Charts download\" loading=\"lazy\">\u003C\u002Fp>\n\u003Chr>\n\u003Ch2 id=\"developer-documentation\" tabindex=\"-1\">\u003Cspan\n                class=\"header-copy-icon\"\n                role=\"button\"\n                data-slug=\"developer-documentation\"\n              >\n                \u003Csvg>\u003Cpath fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.98 2.98 0 0 0 0-4.24a2.98 2.98 0 0 0-4.24 0l-3.53 3.53a2.98 2.98 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.98 2.98 0 0 0 0 4.24a2.98 2.98 0 0 0 4.24 0l3.53-3.53a2.98 2.98 0 0 0 0-4.24a.973.973 0 0 1 0-1.42\"\u002F>\u003C\u002Fsvg>\n              \u003C\u002Fspan>\u003Ca class=\"header-anchor\" href=\"#developer-documentation\">Developer documentation\u003C\u002Fa>\u003C\u002Fh2>\n\u003Ch3 id=\"get-started\" tabindex=\"-1\">\u003Cspan\n                class=\"header-copy-icon\"\n                role=\"button\"\n                data-slug=\"get-started\"\n              >\n                \u003Csvg>\u003Cpath fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.98 2.98 0 0 0 0-4.24a2.98 2.98 0 0 0-4.24 0l-3.53 3.53a2.98 2.98 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.98 2.98 0 0 0 0 4.24a2.98 2.98 0 0 0 4.24 0l3.53-3.53a2.98 2.98 0 0 0 0-4.24a.973.973 0 0 1 0-1.42\"\u002F>\u003C\u002Fsvg>\n              \u003C\u002Fspan>\u003Ca class=\"header-anchor\" href=\"#get-started\">Get started\u003C\u002Fa>\u003C\u002Fh3>\n\n          \u003Cdiv class='code-block'>\n            \u003Cdiv class='code-block-header'>\n              \u003Cspan class='code-block-lang'>pwsh\u003C\u002Fspan>\n              \u003Cbutton class='copy-code-button' type='button'>\n                Copy\n              \u003C\u002Fbutton>\n            \u003C\u002Fdiv>\n            \u003Cpre>\u003Ccode class='hljs pwsh'>git clone https:\u002F\u002Fgithub.com\u002FEDM115\u002Fspendly.git\n\u003Cspan class=\"hljs-built_in\">cd\u003C\u002Fspan> spendly\n\u003C\u002Fcode>\u003C\u002Fpre>\n          \u003C\u002Fdiv>\n        \u003Cp>Create a \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-title\">.env\u003C\u002Fspan>\u003C\u002Fcode> file in the root directory and add the following variables :\u003C\u002Fp>\n\n          \u003Cdiv class='code-block'>\n            \u003Cdiv class='code-block-header'>\n              \u003Cspan class='code-block-lang'>env\u003C\u002Fspan>\n              \u003Cbutton class='copy-code-button' type='button'>\n                Copy\n              \u003C\u002Fbutton>\n            \u003C\u002Fdiv>\n            \u003Cpre>\u003Ccode class='hljs env'>\u003Cspan class=\"hljs-attr\">SEED_USERS\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&#x27;[{&quot;email&quot;: &quot;admin@example.test&quot;, &quot;username&quot;: &quot;admin&quot;, &quot;password&quot;: &quot;admin&quot;, &quot;role&quot;: &quot;admin&quot;}, {&quot;email&quot;: &quot;user@example.test&quot;, &quot;username&quot;: &quot;test&quot;, &quot;password&quot;: &quot;test&quot;, &quot;role&quot;: &quot;user&quot;}]&#x27;\u003C\u002Fspan>\n\u003Cspan class=\"hljs-attr\">SEED\u003C\u002Fspan>=\u003Cspan class=\"hljs-literal\">false\u003C\u002Fspan>\n\u003Cspan class=\"hljs-attr\">DEFAULT_UI_LANG\u003C\u002Fspan>=en\n\u003Cspan class=\"hljs-attr\">DB_FILE_NAME\u003C\u002Fspan>=db\u002Fdata.db\n\u003Cspan class=\"hljs-attr\">BETTER_AUTH_SECRET\u003C\u002Fspan>=x0x0x0\n\u003Cspan class=\"hljs-attr\">BETTER_AUTH_URL\u003C\u002Fspan>=http:\u002F\u002Flocalhost:\u003Cspan class=\"hljs-number\">8888\u003C\u002Fspan>\n\u003Cspan class=\"hljs-attr\">RESEND_API_KEY\u003C\u002Fspan>=re_xxxxxxxxx\n\u003Cspan class=\"hljs-attr\">GITHUB_CLIENT_ID\u003C\u002Fspan>=xxxx\n\u003Cspan class=\"hljs-attr\">GITHUB_CLIENT_SECRET\u003C\u002Fspan>=xxxx0000\n\u003Cspan class=\"hljs-attr\">GOOGLE_CLIENT_ID\u003C\u002Fspan>=\u003Cspan class=\"hljs-number\">0000\u003C\u002Fspan>-xxxx.apps.googleusercontent.com\n\u003Cspan class=\"hljs-attr\">GOOGLE_CLIENT_SECRET\u003C\u002Fspan>=xxxx-\u003Cspan class=\"hljs-number\">0000\u003C\u002Fspan>-xxxx-\n\u003Cspan class=\"hljs-attr\">TURNSTILE_SITE_KEY\u003C\u002Fspan>=\u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>x4AAAAAA00\n\u003Cspan class=\"hljs-attr\">TURNSTILE_SECRET_KEY\u003C\u002Fspan>=\u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>x4AAAAAA00-XX\n\n\u003Cspan class=\"hljs-comment\"># you can skip those\u003C\u002Fspan>\n\u003Cspan class=\"hljs-attr\">SERVICE_NAME\u003C\u002Fspan>=spendly\n\u003Cspan class=\"hljs-attr\">SERVICE_VERSION\u003C\u002Fspan>=\u003Cspan class=\"hljs-number\">1.3\u003C\u002Fspan>.\u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>\n\u003Cspan class=\"hljs-attr\">LOG_LEVEL\u003C\u002Fspan>=info\n\u003Cspan class=\"hljs-attr\">LOG_INCLUDE_UA\u003C\u002Fspan>=\u003Cspan class=\"hljs-literal\">true\u003C\u002Fspan>\n\u003Cspan class=\"hljs-attr\">LOG_INCLUDE_IP\u003C\u002Fspan>=\u003Cspan class=\"hljs-literal\">true\u003C\u002Fspan>\n\u003Cspan class=\"hljs-attr\">STAGE\u003C\u002Fspan>=development\n\u003Cspan class=\"hljs-attr\">ALERT_API\u003C\u002Fspan>=https:\u002F\u002Falert.service\u002Fsend?token=xxxx&amp;message=\n\u003Cspan class=\"hljs-attr\">DISABLED_FEATURES\u003C\u002Fspan>=magic-link,turnstile\n\u003C\u002Fcode>\u003C\u002Fpre>\n          \u003C\u002Fdiv>\n        \u003Cp>\u003Cstrong>Required\u003C\u002Fstrong> :\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">SEED_USERS\u003C\u002Fspan>\u003C\u002Fcode> : if any value should contain a quote, write instead \u003Ccode class=\"hljs\">\\&#x27;\u003C\u002Fcode> (or \u003Ccode class=\"hljs\">\\\u003Cspan class=\"hljs-string\">&quot;\u003C\u002Fspan>\u003C\u002Fcode>)\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">SEED\u003C\u002Fspan>\u003C\u002Fcode> : enables database seeding when the app boots. In Docker, leave this to \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-literal\">true\u003C\u002Fspan>\u003C\u002Fcode> so the first run seeds an empty volume (seeding is skipped if data already exists)\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">DEFAULT_UI_LANG\u003C\u002Fspan>\u003C\u002Fcode> : the default language of the UI (either \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">en\u003C\u002Fspan>\u003C\u002Fcode> or \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">fr\u003C\u002Fspan>\u003C\u002Fcode>)\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">DB_FILE_NAME\u003C\u002Fspan>\u003C\u002Fcode> : the path to the SQLite database file, please keep as-is\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">BETTER_AUTH_SECRET\u003C\u002Fspan>\u003C\u002Fcode> : generate with \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-keyword\">node\u003C\u002Fspan> \u003Cspan class=\"hljs-title\">-e\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">&quot;import(&#x27;crypto&#x27;).then(crypto =&gt; console.log(crypto.randomBytes(64).toString(&#x27;hex&#x27;)))&quot;\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">BETTER_AUTH_URL\u003C\u002Fspan>\u003C\u002Fcode> : the base URL of Spendly, port 8888 in dev and 60000 by default in prod, change with the proper URL\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">RESEND_API_KEY\u003C\u002Fspan>\u003C\u002Fcode> : to send emails\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">GITHUB_CLIENT_ID\u003C\u002Fspan>\u003C\u002Fcode> &amp; \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">GITHUB_CLIENT_SECRET\u003C\u002Fspan>\u003C\u002Fcode> : for GitHub OAuth\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">GOOGLE_CLIENT_ID\u003C\u002Fspan>\u003C\u002Fcode> &amp; \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">GOOGLE_CLIENT_SECRET\u003C\u002Fspan>\u003C\u002Fcode> : for Google OAuth\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">TURNSTILE_SITE_KEY\u003C\u002Fspan>\u003C\u002Fcode> &amp; \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">TURNSTILE_SECRET_KEY\u003C\u002Fspan>\u003C\u002Fcode> : for Cloudflare Turnstile CAPTCHA\u003Cbr>\n\u003Cstrong>Optional\u003C\u002Fstrong> :\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">SERVICE_NAME\u003C\u002Fspan>\u003C\u002Fcode> : service identifier in logs (defaults to \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">spendly\u003C\u002Fspan>\u003C\u002Fcode>)\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">SERVICE_VERSION\u003C\u002Fspan>\u003C\u002Fcode> : release\u002Fversion tag to include in logs, defaults to the version in \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-keyword\">package\u003C\u002Fspan>.json\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">LOG_LEVEL\u003C\u002Fspan>\u003C\u002Fcode> : pino log level (ex \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-keyword\">debug\u003C\u002Fspan>\u003C\u002Fcode>, \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">info\u003C\u002Fspan>\u003C\u002Fcode>, \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">warn\u003C\u002Fspan>\u003C\u002Fcode>, \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-keyword\">error\u003C\u002Fspan>\u003C\u002Fcode>)\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">LOG_INCLUDE_UA\u003C\u002Fspan>\u003C\u002Fcode> : set to \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-literal\">true\u003C\u002Fspan>\u003C\u002Fcode> to include user-agent in request logs (default \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-literal\">false\u003C\u002Fspan>\u003C\u002Fcode>)\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">LOG_INCLUDE_IP\u003C\u002Fspan>\u003C\u002Fcode> : set to \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-literal\">true\u003C\u002Fspan>\u003C\u002Fcode> to include client IP in request logs (default \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-literal\">false\u003C\u002Fspan>\u003C\u002Fcode>)\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">STAGE\u003C\u002Fspan>\u003C\u002Fcode> : override log environment (\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">production\u003C\u002Fspan>\u003C\u002Fcode>, \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">staging\u003C\u002Fspan>\u003C\u002Fcode>, \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">development\u003C\u002Fspan>\u003C\u002Fcode>), defaults to \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">NODE_ENV\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">ALERT_API\u003C\u002Fspan>\u003C\u002Fcode> : an API endpoint to send alerts to when an email have been sent (to monitor its usage as it ain’t free), the message will be appended to the URL\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">DISABLED_FEATURES\u003C\u002Fspan>\u003C\u002Fcode> : a list of comma-separated values of stuff you wanna disable in Spendly. useful for self-hosting and not creating accounts everywhere. possible values : \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">email\u003C\u002Fspan>\u003C\u002Fcode>, \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">logs\u003C\u002Fspan>\u003C\u002Fcode>, \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">magic-link\u003C\u002Fspan>\u003C\u002Fcode>, \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">oauth-github\u003C\u002Fspan>\u003C\u002Fcode>, \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">oauth-google\u003C\u002Fspan>\u003C\u002Fcode>, \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">turnstile\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cdiv class=\"markdown-alert markdown-alert-tip\">\n\u003Cp class=\"markdown-alert-title\">Tip\u003C\u002Fp>\n\u003Cp>to keep turnstile active but do no actual validation, use \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>\u003Cspan class=\"hljs-keyword\">x\u003C\u002Fspan>\u003Cspan class=\"hljs-number\">00000000000000000000\u003C\u002Fspan>AA\u003C\u002Fcode> as \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">TURNSTILE_SITE_KEY\u003C\u002Fspan>\u003C\u002Fcode> &amp; \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>\u003Cspan class=\"hljs-keyword\">x\u003C\u002Fspan>\u003Cspan class=\"hljs-number\">0000000000000000000000000000000\u003C\u002Fspan>AA\u003C\u002Fcode> as \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">TURNSTILE_SECRET_KEY\u003C\u002Fspan>\u003C\u002Fcode>\u003Cbr>\nto disable other features, fork the repo and edit the code yourself\u003Cbr>\n\u003Cstrong>other notes for self-hosters\u003C\u002Fstrong> that requires forking the repo :\u003C\u002Fp>\n\u003Cul>\n\u003Cli>disableable login features will unfortunately stay backend-wise because it would be a db migrations pain, but also because I can’t do it, see \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fbetter-auth\u002Fbetter-auth\u002Fpull\u002F6064#pullrequestreview-3612931730\" target=\"_blank\" rel=\"noopener noreferrer\">https:\u002F\u002Fgithub.com\u002Fbetter-auth\u002Fbetter-auth\u002Fpull\u002F6064#pullrequestreview-3612931730\u003C\u002Fa>\u003C\u002Fli>\n\u003Cli>there are some occurences that hard-code either the domain or the email, search for \u003Ccode class=\"hljs\">edm115.\u003Cspan class=\"hljs-built_in\">dev\u003C\u002Fspan>\u003C\u002Fcode> in any \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-title\">.ts\u003C\u002Fspan>\u003C\u002Fcode>\u002F\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-title\">.vue\u003C\u002Fspan>\u003C\u002Fcode>\u002F\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-title\">.json\u003C\u002Fspan>\u003C\u002Fcode> file and edit accordingly\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fdiv>\n\n          \u003Cdiv class='code-block'>\n            \u003Cdiv class='code-block-header'>\n              \u003Cspan class='code-block-lang'>pwsh\u003C\u002Fspan>\n              \u003Cbutton class='copy-code-button' type='button'>\n                Copy\n              \u003C\u002Fbutton>\n            \u003C\u002Fdiv>\n            \u003Cpre>\u003Ccode class='hljs pwsh'>pnpm i \u003Cspan class=\"hljs-literal\">--frozen-lockfile\u003C\u002Fspan>\npnpm db:migrate\npnpm db:seed\npnpm dev\n\u003C\u002Fcode>\u003C\u002Fpre>\n          \u003C\u002Fdiv>\n        \u003Cp>Congrats ! Go now to \u003Ca href=\"http:\u002F\u002Flocalhost:8888\" target=\"_blank\" rel=\"noopener noreferrer\">\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">localhost\u003C\u002Fspan>:\u003Cspan class=\"hljs-number\">8888\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fa> to check the UI.\u003C\u002Fp>\n\u003Ch3 id=\"third-party-setup\" tabindex=\"-1\">\u003Cspan\n                class=\"header-copy-icon\"\n                role=\"button\"\n                data-slug=\"third-party-setup\"\n              >\n                \u003Csvg>\u003Cpath fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.98 2.98 0 0 0 0-4.24a2.98 2.98 0 0 0-4.24 0l-3.53 3.53a2.98 2.98 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.98 2.98 0 0 0 0 4.24a2.98 2.98 0 0 0 4.24 0l3.53-3.53a2.98 2.98 0 0 0 0-4.24a.973.973 0 0 1 0-1.42\"\u002F>\u003C\u002Fsvg>\n              \u003C\u002Fspan>\u003Ca class=\"header-anchor\" href=\"#third-party-setup\">Third-party setup\u003C\u002Fa>\u003C\u002Fh3>\n\u003Ch4 id=\"turnstile\" tabindex=\"-1\">\u003Cspan\n                class=\"header-copy-icon\"\n                role=\"button\"\n                data-slug=\"turnstile\"\n              >\n                \u003Csvg>\u003Cpath fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.98 2.98 0 0 0 0-4.24a2.98 2.98 0 0 0-4.24 0l-3.53 3.53a2.98 2.98 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.98 2.98 0 0 0 0 4.24a2.98 2.98 0 0 0 4.24 0l3.53-3.53a2.98 2.98 0 0 0 0-4.24a.973.973 0 0 1 0-1.42\"\u002F>\u003C\u002Fsvg>\n              \u003C\u002Fspan>\u003Ca class=\"header-anchor\" href=\"#turnstile\">Turnstile\u003C\u002Fa>\u003C\u002Fh4>\n\u003Col>\n\u003Cli>Go to \u003Ca href=\"https:\u002F\u002Fdash.cloudflare.com\" target=\"_blank\" rel=\"noopener noreferrer\">https:\u002F\u002Fdash.cloudflare.com\u003C\u002Fa> -&gt; Turnstile (use the sidebar search if needed)\u003C\u002Fli>\n\u003Cli>Click on \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-built_in\">Add\u003C\u002Fspan> widget\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>Give it a name\u003C\u002Fli>\n\u003Cli>Add as Hostnames : \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">localhost\u003C\u002Fspan>\u003C\u002Fcode> and your production domain (ex \u003Ccode class=\"hljs\">spendly.edm115.\u003Cspan class=\"hljs-built_in\">dev\u003C\u002Fspan>\u003C\u002Fcode>), as well as any other one\u003C\u002Fli>\n\u003Cli>Keep Widget mode to \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">Managed\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>Set Pre-clearance to \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">Yes\u003C\u002Fspan>\u003C\u002Fcode>, and set the Pre-clearance level to \u003Ccode class=\"hljs\">Interactive (\u003Cspan class=\"hljs-name\">high\u003C\u002Fspan>)\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>You now have your Site Key and Secret Key\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch4 id=\"resend-emails\" tabindex=\"-1\">\u003Cspan\n                class=\"header-copy-icon\"\n                role=\"button\"\n                data-slug=\"resend-emails\"\n              >\n                \u003Csvg>\u003Cpath fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.98 2.98 0 0 0 0-4.24a2.98 2.98 0 0 0-4.24 0l-3.53 3.53a2.98 2.98 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.98 2.98 0 0 0 0 4.24a2.98 2.98 0 0 0 4.24 0l3.53-3.53a2.98 2.98 0 0 0 0-4.24a.973.973 0 0 1 0-1.42\"\u002F>\u003C\u002Fsvg>\n              \u003C\u002Fspan>\u003Ca class=\"header-anchor\" href=\"#resend-emails\">Resend (emails)\u003C\u002Fa>\u003C\u002Fh4>\n\u003Col>\n\u003Cli>Create an account on \u003Ca href=\"https:\u002F\u002Fresend.com\u002F\" target=\"_blank\" rel=\"noopener noreferrer\">https:\u002F\u002Fresend.com\u002F\u003C\u002Fa> and go through the Onboarding. Do not reuse that API key for this project and delete it once the Onboarding have been completed\u003C\u002Fli>\n\u003Cli>Go to Domains, \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-keyword\">Add\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">domain\u003C\u002Fspan>\u003C\u002Fcode>. It is best to set it up as a subdomain to avoid potentially polluting your main domain score\u003C\u002Fli>\n\u003Cli>Place the region closest to your website hosting\u003C\u002Fli>\n\u003Cli>Go through the steps. Auto-setup with Cloudflare is easiest, tho be careful if you have existing \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-keyword\">MX\u003C\u002Fspan>\u003C\u002Fcode> records !\u003C\u002Fli>\n\u003Cli>Do not enable Receiving if you already have a solution that forwards emails somewhere else, otherwise do it\u003C\u002Fli>\n\u003Cli>Configuration-wise, disable \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">Click Tracking\u003C\u002Fspan>\u003C\u002Fcode> &amp; \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-keyword\">Open\u003C\u002Fspan> Tracking\u003C\u002Fcode>, and set \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">TLS\u003C\u002Fspan>\u003C\u002Fcode> to \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">Opportunistic\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>Once the domain have been verified, create an API Key. \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-keyword\">Full\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">access\u003C\u002Fspan>\u003C\u002Fcode> if Receiving is enabled, \u003Ccode class=\"hljs\">Sending \u003Cspan class=\"hljs-keyword\">access\u003C\u002Fspan>\u003C\u002Fcode> otherwise\u003C\u002Fli>\n\u003Cli>Go in Templates and create 5 of them with the following variables as string (Formatting is up to you. I wish I could share the templates I made, but alas…) :\n\u003Cul>\n\u003Cli>\u003Ccode class=\"hljs\">spendly-\u003Cspan class=\"hljs-keyword\">password\u003C\u002Fspan>-\u003Cspan class=\"hljs-keyword\">reset\u003C\u002Fspan>\u003C\u002Fcode>\n\u003Cul>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">account_name\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">spendly_home\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">reset_link\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">spendly-\u003Cspan class=\"hljs-built_in\">verify\u003C\u002Fspan>-email\u003C\u002Fcode>\n\u003Cul>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">account_name\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">spendly_home\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">verify_link\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">spendly-magic-link\u003C\u002Fspan>\u003C\u002Fcode>\n\u003Cul>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">account_name\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">spendly_home\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">connect_link\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">spendly-account-deletion\u003C\u002Fspan>\u003C\u002Fcode>\n\u003Cul>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">account_name\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">spendly_home\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-title\">spendly\u003C\u002Fspan>-ex\u003Cspan class=\"hljs-keyword\">port\u003C\u002Fspan>-request\u003C\u002Fcode>\n\u003Cul>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">account_name\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">spendly_home\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch4 id=\"git-hub-o-auth\" tabindex=\"-1\">\u003Cspan\n                class=\"header-copy-icon\"\n                role=\"button\"\n                data-slug=\"git-hub-o-auth\"\n              >\n                \u003Csvg>\u003Cpath fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.98 2.98 0 0 0 0-4.24a2.98 2.98 0 0 0-4.24 0l-3.53 3.53a2.98 2.98 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.98 2.98 0 0 0 0 4.24a2.98 2.98 0 0 0 4.24 0l3.53-3.53a2.98 2.98 0 0 0 0-4.24a.973.973 0 0 1 0-1.42\"\u002F>\u003C\u002Fsvg>\n              \u003C\u002Fspan>\u003Ca class=\"header-anchor\" href=\"#git-hub-o-auth\">GitHub OAuth\u003C\u002Fa>\u003C\u002Fh4>\n\u003Col>\n\u003Cli>Go to \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fsettings\u002Fdevelopers\" target=\"_blank\" rel=\"noopener noreferrer\">https:\u002F\u002Fgithub.com\u002Fsettings\u002Fdevelopers\u003C\u002Fa>\u003C\u002Fli>\n\u003Cli>Click on \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-keyword\">New\u003C\u002Fspan> OAuth App\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>Give it any name, homepage\u003C\u002Fli>\n\u003Cli>Authorization callback URL is \u003Ccode class=\"hljs\">http:\u003Cspan class=\"hljs-regexp\">\u002F\u002F\u003C\u002Fspan>localhost:\u003Cspan class=\"hljs-number\">8888\u003C\u002Fspan>\u003Cspan class=\"hljs-regexp\">\u002Fapi\u002F\u003C\u002Fspan>auth\u003Cspan class=\"hljs-regexp\">\u002Fcallback\u002Fgi\u003C\u002Fspan>thub\u003C\u002Fcode> (\u003Cstrong>note\u003C\u002Fstrong> : you cannot setup multiple callback URLs, so you will need to create multiple Apps for dev and prod, which is recommended anyway. make sure to switch to \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">https\u003C\u002Fspan>\u003C\u002Fcode> with the prod URL !)\u003C\u002Fli>\n\u003Cli>Leave \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-built_in\">Enable\u003C\u002Fspan> Device Flow\u003C\u002Fcode> disabled\u003C\u002Fli>\n\u003Cli>Once registered, add a logo\u002Fbadge color if you want\u003C\u002Fli>\n\u003Cli>You now have your \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-built_in\">Client\u003C\u002Fspan> ID\u003C\u002Fcode> and you can create here the \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-built_in\">Client\u003C\u002Fspan> secret\u003C\u002Fcode>\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch4 id=\"google-o-auth\" tabindex=\"-1\">\u003Cspan\n                class=\"header-copy-icon\"\n                role=\"button\"\n                data-slug=\"google-o-auth\"\n              >\n                \u003Csvg>\u003Cpath fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.98 2.98 0 0 0 0-4.24a2.98 2.98 0 0 0-4.24 0l-3.53 3.53a2.98 2.98 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.98 2.98 0 0 0 0 4.24a2.98 2.98 0 0 0 4.24 0l3.53-3.53a2.98 2.98 0 0 0 0-4.24a.973.973 0 0 1 0-1.42\"\u002F>\u003C\u002Fsvg>\n              \u003C\u002Fspan>\u003Ca class=\"header-anchor\" href=\"#google-o-auth\">Google OAuth\u003C\u002Fa>\u003C\u002Fh4>\n\u003Col>\n\u003Cli>Go to \u003Ca href=\"https:\u002F\u002Fconsole.cloud.google.com\u002Fapis\u002Fcredentials\" target=\"_blank\" rel=\"noopener noreferrer\">https:\u002F\u002Fconsole.cloud.google.com\u002Fapis\u002Fcredentials\u003C\u002Fa>\u003C\u002Fli>\n\u003Cli>Check that you selected the right project at the top (or create one if needed)\u003C\u002Fli>\n\u003Cli>Click on \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-bullet\">+\u003C\u002Fspan> Create Credentials\u003C\u002Fcode> -&gt; \u003Ccode class=\"hljs\">OAuth \u003Cspan class=\"hljs-keyword\">client\u003C\u002Fspan> ID\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>Application type is \u003Ccode class=\"hljs\">Web \u003Cspan class=\"hljs-built_in\">application\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>Authorized JavaScript origins is \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-symbol\">http:\u003C\u002Fspan>\u003Cspan class=\"hljs-comment\">\u002F\u002Flocalhost:8888\u003C\u002Fspan>\u003C\u002Fcode> (and your prod domain, ex \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-symbol\">https:\u003C\u002Fspan>\u003Cspan class=\"hljs-comment\">\u002F\u002Fspendly.edm115.dev\u003C\u002Fspan>\u003C\u002Fcode>)\u003C\u002Fli>\n\u003Cli>Authorized redirect URIs is \u003Ccode class=\"hljs\">http:\u003Cspan class=\"hljs-regexp\">\u002F\u002F\u003C\u002Fspan>localhost:\u003Cspan class=\"hljs-number\">8888\u003C\u002Fspan>\u003Cspan class=\"hljs-regexp\">\u002Fapi\u002F\u003C\u002Fspan>auth\u003Cspan class=\"hljs-regexp\">\u002Fcallback\u002Fg\u003C\u002Fspan>oogle\u003C\u002Fcode> (and the prod one, ex \u003Ccode class=\"hljs\">https:\u003Cspan class=\"hljs-regexp\">\u002F\u002F\u003C\u002Fspan>spendly.edm115.dev\u003Cspan class=\"hljs-regexp\">\u002Fapi\u002F\u003C\u002Fspan>auth\u003Cspan class=\"hljs-regexp\">\u002Fcallback\u002Fg\u003C\u002Fspan>oogle\u003C\u002Fcode>)\u003C\u002Fli>\n\u003Cli>Save. You now have the \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-built_in\">Client\u003C\u002Fspan> ID\u003C\u002Fcode> (looking like an URL), and you can generate a \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-built_in\">Client\u003C\u002Fspan> secret\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>Go to Branding and add a logo if you want. You can also add your prod domain, exempt of any subdomain in the Authorized domains field (ex \u003Ccode class=\"hljs\">edm115.\u003Cspan class=\"hljs-built_in\">dev\u003C\u002Fspan>\u003C\u002Fcode> but not \u003Ccode class=\"hljs\">www.edm115.\u003Cspan class=\"hljs-built_in\">dev\u003C\u002Fspan>\u003C\u002Fcode> nor \u003Ccode class=\"hljs\">spendly.edm115.\u003Cspan class=\"hljs-built_in\">dev\u003C\u002Fspan>\u003C\u002Fcode>)\u003C\u002Fli>\n\u003Cli>Go to Data access, and enable the following fields :\n\u003Cul>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">openid\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">...\u003Cspan class=\"hljs-regexp\">\u002Fauth\u002Fu\u003C\u002Fspan>serinfo.email\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">...\u003Cspan class=\"hljs-regexp\">\u002Fauth\u002Fu\u003C\u002Fspan>serinfo.profile\u003C\u002Fcode>\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>For the Audience tab, you can keep it as Test for dev, but for Production you will need to submit for validation. This will require you, among other things, to provide online and ready-to-hit Privacy Policy and Terms of Use pages. Spendly already has those, accessible at \u003Ccode class=\"hljs\">\u002Fprivacy-policy\u003C\u002Fcode> and \u003Ccode class=\"hljs\">\u002Fterms-of-\u003Cspan class=\"hljs-keyword\">use\u003C\u002Fspan>\u003C\u002Fcode>. You can then check where it’s at in the Validation center, and they will send emails if anything needs to be fixed.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch3 id=\"pwa-vite-pwa-nuxt\" tabindex=\"-1\">\u003Cspan\n                class=\"header-copy-icon\"\n                role=\"button\"\n                data-slug=\"pwa-vite-pwa-nuxt\"\n              >\n                \u003Csvg>\u003Cpath fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.98 2.98 0 0 0 0-4.24a2.98 2.98 0 0 0-4.24 0l-3.53 3.53a2.98 2.98 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.98 2.98 0 0 0 0 4.24a2.98 2.98 0 0 0 4.24 0l3.53-3.53a2.98 2.98 0 0 0 0-4.24a.973.973 0 0 1 0-1.42\"\u002F>\u003C\u002Fsvg>\n              \u003C\u002Fspan>\u003Ca class=\"header-anchor\" href=\"#pwa-vite-pwa-nuxt\">PWA (Vite PWA \u002F Nuxt)\u003C\u002Fa>\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>Spendly uses \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-meta\">@vite\u003C\u002Fspan>-pwa\u002Fnuxt\u003C\u002Fcode> with a generated Web App Manifest and service worker\u003C\u002Fli>\n\u003Cli>PWA head entries (\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">manifest\u003C\u002Fspan>\u003C\u002Fcode>, icons, \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-built_in\">theme\u003C\u002Fspan>-\u003Cspan class=\"hljs-built_in\">color\u003C\u002Fspan>\u003C\u002Fcode>) are injected through \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">NuxtPwaAssets\u003C\u002Fspan>\u003C\u002Fcode> in \u003Ccode class=\"hljs\">app\u003Cspan class=\"hljs-regexp\">\u002Flayouts\u002F\u003C\u002Fspan>\u003Cspan class=\"hljs-keyword\">default\u003C\u002Fspan>.vue\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>Icon assets are generated from \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan>\u003Cspan class=\"hljs-regexp\">\u002Fimages\u002F\u003C\u002Fspan>logo.webp\u003C\u002Fcode> via the \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">pwaAssets\u003C\u002Fspan>\u003C\u002Fcode> integration\u003C\u002Fli>\n\u003Cli>Service worker update mode is configured as \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">prompt\u003C\u002Fspan>\u003C\u002Fcode> to avoid forced reload while users are editing data\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3 id=\"log-analysis-cli-tui\" tabindex=\"-1\">\u003Cspan\n                class=\"header-copy-icon\"\n                role=\"button\"\n                data-slug=\"log-analysis-cli-tui\"\n              >\n                \u003Csvg>\u003Cpath fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.98 2.98 0 0 0 0-4.24a2.98 2.98 0 0 0-4.24 0l-3.53 3.53a2.98 2.98 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.98 2.98 0 0 0 0 4.24a2.98 2.98 0 0 0 4.24 0l3.53-3.53a2.98 2.98 0 0 0 0-4.24a.973.973 0 0 1 0-1.42\"\u002F>\u003C\u002Fsvg>\n              \u003C\u002Fspan>\u003Ca class=\"header-anchor\" href=\"#log-analysis-cli-tui\">Log analysis (CLI + TUI)\u003C\u002Fa>\u003C\u002Fh3>\n\u003Cp>Capture logs to a file (Docker example) :\u003C\u002Fp>\n\n          \u003Cdiv class='code-block'>\n            \u003Cdiv class='code-block-header'>\n              \u003Cspan class='code-block-lang'>pwsh\u003C\u002Fspan>\n              \u003Cbutton class='copy-code-button' type='button'>\n                Copy\n              \u003C\u002Fbutton>\n            \u003C\u002Fdiv>\n            \u003Cpre>\u003Ccode class='hljs pwsh'>docker logs spendly &gt; logs\u002Fspendly.log\n\u003C\u002Fcode>\u003C\u002Fpre>\n          \u003C\u002Fdiv>\n        \u003Cp>Follow logs and save them while you reproduce an issue :\u003C\u002Fp>\n\n          \u003Cdiv class='code-block'>\n            \u003Cdiv class='code-block-header'>\n              \u003Cspan class='code-block-lang'>pwsh\u003C\u002Fspan>\n              \u003Cbutton class='copy-code-button' type='button'>\n                Copy\n              \u003C\u002Fbutton>\n            \u003C\u002Fdiv>\n            \u003Cpre>\u003Ccode class='hljs pwsh'>docker logs \u003Cspan class=\"hljs-operator\">-f\u003C\u002Fspan> spendly | \u003Cspan class=\"hljs-built_in\">Tee-Object\u003C\u002Fspan> \u003Cspan class=\"hljs-literal\">-FilePath\u003C\u002Fspan> logs\u002Fspendly.log\n\u003C\u002Fcode>\u003C\u002Fpre>\n          \u003C\u002Fdiv>\n        \n          \u003Cdiv class='code-block'>\n            \u003Cdiv class='code-block-header'>\n              \u003Cspan class='code-block-lang'>bash\u003C\u002Fspan>\n              \u003Cbutton class='copy-code-button' type='button'>\n                Copy\n              \u003C\u002Fbutton>\n            \u003C\u002Fdiv>\n            \u003Cpre>\u003Ccode class='hljs bash'>docker logs -f spendly | \u003Cspan class=\"hljs-built_in\">tee\u003C\u002Fspan> logs\u002Fspendly.log\n\u003C\u002Fcode>\u003C\u002Fpre>\n          \u003C\u002Fdiv>\n        \u003Cp>Capture dev server logs (when you pipe, JSON is emitted because the logger disables pretty output in non-TTY mode) :\u003C\u002Fp>\n\n          \u003Cdiv class='code-block'>\n            \u003Cdiv class='code-block-header'>\n              \u003Cspan class='code-block-lang'>pwsh\u003C\u002Fspan>\n              \u003Cbutton class='copy-code-button' type='button'>\n                Copy\n              \u003C\u002Fbutton>\n            \u003C\u002Fdiv>\n            \u003Cpre>\u003Ccode class='hljs pwsh'>pnpm dev | \u003Cspan class=\"hljs-built_in\">Tee-Object\u003C\u002Fspan> \u003Cspan class=\"hljs-literal\">-FilePath\u003C\u002Fspan> logs\u002Fspendly.log\n\u003C\u002Fcode>\u003C\u002Fpre>\n          \u003C\u002Fdiv>\n        \u003Cp>Analyze logs (CLI summary + JSON report) :\u003C\u002Fp>\n\n          \u003Cdiv class='code-block'>\n            \u003Cdiv class='code-block-header'>\n              \u003Cspan class='code-block-lang'>pwsh\u003C\u002Fspan>\n              \u003Cbutton class='copy-code-button' type='button'>\n                Copy\n              \u003C\u002Fbutton>\n            \u003C\u002Fdiv>\n            \u003Cpre>\u003Ccode class='hljs pwsh'>pnpm log:analyze \u003Cspan class=\"hljs-literal\">--file\u003C\u002Fspan> logs\u002Fspendly.log\n\u003C\u002Fcode>\u003C\u002Fpre>\n          \u003C\u002Fdiv>\n        \u003Cp>Analyze directly from stdin :\u003C\u002Fp>\n\n          \u003Cdiv class='code-block'>\n            \u003Cdiv class='code-block-header'>\n              \u003Cspan class='code-block-lang'>pwsh\u003C\u002Fspan>\n              \u003Cbutton class='copy-code-button' type='button'>\n                Copy\n              \u003C\u002Fbutton>\n            \u003C\u002Fdiv>\n            \u003Cpre>\u003Ccode class='hljs pwsh'>\u003Cspan class=\"hljs-built_in\">cat\u003C\u002Fspan> logs\u002Fspendly.log | pnpm log:analyze\n\u003C\u002Fcode>\u003C\u002Fpre>\n          \u003C\u002Fdiv>\n        \u003Cp>Useful flags :\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Ccode class=\"hljs\">--duration-kind request|\u003Cspan class=\"hljs-type\">ui\u003C\u002Fspan>|\u003Cspan class=\"hljs-type\">system\u003C\u002Fspan>\u003C\u002Fcode> : choose which kind feeds duration stats\u002Fslowest\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-comment\">--no-output\u003C\u002Fspan>\u003C\u002Fcode> : skip writing the JSON report to disk\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">--json logs\u002F\u003Cspan class=\"hljs-keyword\">log\u003C\u002Fspan>-\u003Cspan class=\"hljs-keyword\">report\u003C\u002Fspan>.json\u003C\u002Fcode> : write the JSON report to a custom path\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Interactive TUI (requires a file path and a TTY) :\u003C\u002Fp>\n\n          \u003Cdiv class='code-block'>\n            \u003Cdiv class='code-block-header'>\n              \u003Cspan class='code-block-lang'>pwsh\u003C\u002Fspan>\n              \u003Cbutton class='copy-code-button' type='button'>\n                Copy\n              \u003C\u002Fbutton>\n            \u003C\u002Fdiv>\n            \u003Cpre>\u003Ccode class='hljs pwsh'>pnpm log:tui \u003Cspan class=\"hljs-literal\">--file\u003C\u002Fspan> logs\u002Fspendly.log\n\u003C\u002Fcode>\u003C\u002Fpre>\n          \u003C\u002Fdiv>\n        \u003Cp>TUI controls :\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Tabs : \u003Ccode class=\"hljs\">1\u003C\u002Fcode> Overview, \u003Ccode class=\"hljs\">2\u003C\u002Fcode> Filters, \u003Ccode class=\"hljs\">3\u003C\u002Fcode> Drilldown\u003C\u002Fli>\n\u003Cli>Overview : \u003Ccode class=\"hljs\">↑\u003Cspan class=\"hljs-regexp\">\u002F↓\u002F\u003C\u002Fspan>scroll\u003C\u002Fcode> move\u003C\u002Fli>\n\u003Cli>Filters : \u003Ccode class=\"hljs\">f\u003Cspan class=\"hljs-regexp\">\u002Ft\u002F\u003C\u002Fspan>e\u003Cspan class=\"hljs-regexp\">\u002Fs\u002F\u003C\u002Fspan>k\u003Cspan class=\"hljs-regexp\">\u002Fo\u002F\u003C\u002Fspan>h\u003Cspan class=\"hljs-regexp\">\u002Fa\u002F\u003C\u002Fspan>d\u002Fx\u003C\u002Fcode> edit\u002Fclear filters\u003C\u002Fli>\n\u003Cli>Drilldown : \u003Ccode class=\"hljs\">↑\u003Cspan class=\"hljs-regexp\">\u002F↓\u002F\u003C\u002Fspan>scroll\u003C\u002Fcode> move, \u003Ccode class=\"hljs\">\u002F\u003C\u002Fcode> search, \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">Esc\u003C\u002Fspan>\u003C\u002Fcode> clear search\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attribute\">r\u003C\u002Fspan>\u003C\u002Fcode> refresh, \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-selector-tag\">q\u003C\u002Fspan>\u003C\u002Fcode> quit, \u003Ccode class=\"hljs\">?\u003C\u002Fcode> help\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3 id=\"on-drizzle-db-schema-better-auth-config-changes\" tabindex=\"-1\">\u003Cspan\n                class=\"header-copy-icon\"\n                role=\"button\"\n                data-slug=\"on-drizzle-db-schema-better-auth-config-changes\"\n              >\n                \u003Csvg>\u003Cpath fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.98 2.98 0 0 0 0-4.24a2.98 2.98 0 0 0-4.24 0l-3.53 3.53a2.98 2.98 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.98 2.98 0 0 0 0 4.24a2.98 2.98 0 0 0 4.24 0l3.53-3.53a2.98 2.98 0 0 0 0-4.24a.973.973 0 0 1 0-1.42\"\u002F>\u003C\u002Fsvg>\n              \u003C\u002Fspan>\u003Ca class=\"header-anchor\" href=\"#on-drizzle-db-schema-better-auth-config-changes\">On Drizzle DB schema\u002FBetter Auth config changes\u003C\u002Fa>\u003C\u002Fh3>\n\n          \u003Cdiv class='code-block'>\n            \u003Cdiv class='code-block-header'>\n              \u003Cspan class='code-block-lang'>pwsh\u003C\u002Fspan>\n              \u003Cbutton class='copy-code-button' type='button'>\n                Copy\n              \u003C\u002Fbutton>\n            \u003C\u002Fdiv>\n            \u003Cpre>\u003Ccode class='hljs pwsh'>pnpm better\u003Cspan class=\"hljs-literal\">-auth\u003C\u002Fspan>:generate\n\u003Cspan class=\"hljs-comment\"># diff shared\u002Fdb\u002Fauth.schema.ts with shared\u002Fdb\u002Fschema.ts and update the Better Auth tables accordingly\u003C\u002Fspan>\npnpm db:generate\npnpm db:migrate\n\u003C\u002Fcode>\u003C\u002Fpre>\n          \u003C\u002Fdiv>\n        \u003Ch3 id=\"build-and-run-redeploy\" tabindex=\"-1\">\u003Cspan\n                class=\"header-copy-icon\"\n                role=\"button\"\n                data-slug=\"build-and-run-redeploy\"\n              >\n                \u003Csvg>\u003Cpath fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.98 2.98 0 0 0 0-4.24a2.98 2.98 0 0 0-4.24 0l-3.53 3.53a2.98 2.98 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.98 2.98 0 0 0 0 4.24a2.98 2.98 0 0 0 4.24 0l3.53-3.53a2.98 2.98 0 0 0 0-4.24a.973.973 0 0 1 0-1.42\"\u002F>\u003C\u002Fsvg>\n              \u003C\u002Fspan>\u003Ca class=\"header-anchor\" href=\"#build-and-run-redeploy\">Build and run\u002FRedeploy\u003C\u002Fa>\u003C\u002Fh3>\n\n          \u003Cdiv class='code-block'>\n            \u003Cdiv class='code-block-header'>\n              \u003Cspan class='code-block-lang'>pwsh\u003C\u002Fspan>\n              \u003Cbutton class='copy-code-button' type='button'>\n                Copy\n              \u003C\u002Fbutton>\n            \u003C\u002Fdiv>\n            \u003Cpre>\u003Ccode class='hljs pwsh'>docker compose up \u003Cspan class=\"hljs-literal\">-d\u003C\u002Fspan> \u003Cspan class=\"hljs-literal\">--build\u003C\u002Fspan>\n\u003C\u002Fcode>\u003C\u002Fpre>\n          \u003C\u002Fdiv>\n        \u003Ch4 id=\"remove-container-but-keep-data\" tabindex=\"-1\">\u003Cspan\n                class=\"header-copy-icon\"\n                role=\"button\"\n                data-slug=\"remove-container-but-keep-data\"\n              >\n                \u003Csvg>\u003Cpath fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.98 2.98 0 0 0 0-4.24a2.98 2.98 0 0 0-4.24 0l-3.53 3.53a2.98 2.98 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.98 2.98 0 0 0 0 4.24a2.98 2.98 0 0 0 4.24 0l3.53-3.53a2.98 2.98 0 0 0 0-4.24a.973.973 0 0 1 0-1.42\"\u002F>\u003C\u002Fsvg>\n              \u003C\u002Fspan>\u003Ca class=\"header-anchor\" href=\"#remove-container-but-keep-data\">Remove container but keep data\u003C\u002Fa>\u003C\u002Fh4>\n\n          \u003Cdiv class='code-block'>\n            \u003Cdiv class='code-block-header'>\n              \u003Cspan class='code-block-lang'>pwsh\u003C\u002Fspan>\n              \u003Cbutton class='copy-code-button' type='button'>\n                Copy\n              \u003C\u002Fbutton>\n            \u003C\u002Fdiv>\n            \u003Cpre>\u003Ccode class='hljs pwsh'>docker compose down\n\u003C\u002Fcode>\u003C\u002Fpre>\n          \u003C\u002Fdiv>\n        \u003Cdetails>\u003Csummary>\u003Ch4>Older method\u003C\u002Fsummary>\n\u003Ch3 id=\"build-and-run-docker-cli\" tabindex=\"-1\">\u003Cspan\n                class=\"header-copy-icon\"\n                role=\"button\"\n                data-slug=\"build-and-run-docker-cli\"\n              >\n                \u003Csvg>\u003Cpath fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.98 2.98 0 0 0 0-4.24a2.98 2.98 0 0 0-4.24 0l-3.53 3.53a2.98 2.98 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.98 2.98 0 0 0 0 4.24a2.98 2.98 0 0 0 4.24 0l3.53-3.53a2.98 2.98 0 0 0 0-4.24a.973.973 0 0 1 0-1.42\"\u002F>\u003C\u002Fsvg>\n              \u003C\u002Fspan>\u003Ca class=\"header-anchor\" href=\"#build-and-run-docker-cli\">Build and run (Docker CLI)\u003C\u002Fa>\u003C\u002Fh3>\n\n          \u003Cdiv class='code-block'>\n            \u003Cdiv class='code-block-header'>\n              \u003Cspan class='code-block-lang'>pwsh\u003C\u002Fspan>\n              \u003Cbutton class='copy-code-button' type='button'>\n                Copy\n              \u003C\u002Fbutton>\n            \u003C\u002Fdiv>\n            \u003Cpre>\u003Ccode class='hljs pwsh'>docker build \u003Cspan class=\"hljs-literal\">-t\u003C\u002Fspan> edm115\u002Fspendly .\ndocker run \u003Cspan class=\"hljs-literal\">-d\u003C\u002Fspan> \u003Cspan class=\"hljs-literal\">-p\u003C\u002Fspan> \u003Cspan class=\"hljs-number\">60000\u003C\u002Fspan>:\u003Cspan class=\"hljs-number\">60000\u003C\u002Fspan> \u003Cspan class=\"hljs-literal\">--env-file\u003C\u002Fspan> .env \u003Cspan class=\"hljs-literal\">-v\u003C\u002Fspan> spendly_db:\u002Fapp\u002Fdb \u003Cspan class=\"hljs-literal\">--name\u003C\u002Fspan> spendly edm115\u002Fspendly\n\u003C\u002Fcode>\u003C\u002Fpre>\n          \u003C\u002Fdiv>\n        \u003Ch4 id=\"redeploy-rebuild-without-data-loss\" tabindex=\"-1\">\u003Cspan\n                class=\"header-copy-icon\"\n                role=\"button\"\n                data-slug=\"redeploy-rebuild-without-data-loss\"\n              >\n                \u003Csvg>\u003Cpath fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.98 2.98 0 0 0 0-4.24a2.98 2.98 0 0 0-4.24 0l-3.53 3.53a2.98 2.98 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.98 2.98 0 0 0 0 4.24a2.98 2.98 0 0 0 4.24 0l3.53-3.53a2.98 2.98 0 0 0 0-4.24a.973.973 0 0 1 0-1.42\"\u002F>\u003C\u002Fsvg>\n              \u003C\u002Fspan>\u003Ca class=\"header-anchor\" href=\"#redeploy-rebuild-without-data-loss\">Redeploy (rebuild without data loss)\u003C\u002Fa>\u003C\u002Fh4>\n\n          \u003Cdiv class='code-block'>\n            \u003Cdiv class='code-block-header'>\n              \u003Cspan class='code-block-lang'>pwsh\u003C\u002Fspan>\n              \u003Cbutton class='copy-code-button' type='button'>\n                Copy\n              \u003C\u002Fbutton>\n            \u003C\u002Fdiv>\n            \u003Cpre>\u003Ccode class='hljs pwsh'>docker stop spendly &amp;&amp; docker \u003Cspan class=\"hljs-built_in\">rm\u003C\u002Fspan> spendly\ndocker build \u003Cspan class=\"hljs-literal\">-t\u003C\u002Fspan> edm115\u002Fspendly .\ndocker run \u003Cspan class=\"hljs-literal\">-d\u003C\u002Fspan> \u003Cspan class=\"hljs-literal\">-p\u003C\u002Fspan> \u003Cspan class=\"hljs-number\">60000\u003C\u002Fspan>:\u003Cspan class=\"hljs-number\">60000\u003C\u002Fspan> \u003Cspan class=\"hljs-literal\">--env-file\u003C\u002Fspan> .env \u003Cspan class=\"hljs-literal\">-v\u003C\u002Fspan> spendly_db:\u002Fapp\u002Fdb \u003Cspan class=\"hljs-literal\">--name\u003C\u002Fspan> spendly edm115\u002Fspendly\n\u003C\u002Fcode>\u003C\u002Fpre>\n          \u003C\u002Fdiv>\n        \u003C\u002Fdetails>\n\u003Ch4 id=\"notes\" tabindex=\"-1\">\u003Cspan\n                class=\"header-copy-icon\"\n                role=\"button\"\n                data-slug=\"notes\"\n              >\n                \u003Csvg>\u003Cpath fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.98 2.98 0 0 0 0-4.24a2.98 2.98 0 0 0-4.24 0l-3.53 3.53a2.98 2.98 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.98 2.98 0 0 0 0 4.24a2.98 2.98 0 0 0 4.24 0l3.53-3.53a2.98 2.98 0 0 0 0-4.24a.973.973 0 0 1 0-1.42\"\u002F>\u003C\u002Fsvg>\n              \u003C\u002Fspan>\u003Ca class=\"header-anchor\" href=\"#notes\">Notes\u003C\u002Fa>\u003C\u002Fh4>\n\u003Cul>\n\u003Cli>Database migrations run automatically at container startup\u003C\u002Fli>\n\u003Cli>Seeding runs only when \u003Ccode class=\"hljs\">\u003Cspan class=\"hljs-attr\">SEED\u003C\u002Fspan>=\u003Cspan class=\"hljs-literal\">true\u003C\u002Fspan>\u003C\u002Fcode> \u003Cstrong>and\u003C\u002Fstrong> the database is empty\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cdetails>\u003Csummary>\u003Ch3>DB Schema\u003C\u002Fh3>\u003C\u002Fsummary>\n\u003Ch4 id=\"budget-tracker\" tabindex=\"-1\">\u003Cspan\n                class=\"header-copy-icon\"\n                role=\"button\"\n                data-slug=\"budget-tracker\"\n              >\n                \u003Csvg>\u003Cpath fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.98 2.98 0 0 0 0-4.24a2.98 2.98 0 0 0-4.24 0l-3.53 3.53a2.98 2.98 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.98 2.98 0 0 0 0 4.24a2.98 2.98 0 0 0 4.24 0l3.53-3.53a2.98 2.98 0 0 0 0-4.24a.973.973 0 0 1 0-1.42\"\u002F>\u003C\u002Fsvg>\n              \u003C\u002Fspan>\u003Ca class=\"header-anchor\" href=\"#budget-tracker\">budget_tracker\u003C\u002Fa>\u003C\u002Fh4>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth style=\"text-align:left\">Column\u003C\u002Fth>\n\u003Cth style=\"text-align:left\">Type\u003C\u002Fth>\n\u003Cth style=\"text-align:left\">Extra\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd style=\"text-align:left\">id\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">string\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">Primary Key, UUIDv4\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd style=\"text-align:left\">name\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">string\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">Not Null\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Ch4 id=\"user-budget-tracker\" tabindex=\"-1\">\u003Cspan\n                class=\"header-copy-icon\"\n                role=\"button\"\n                data-slug=\"user-budget-tracker\"\n              >\n                \u003Csvg>\u003Cpath fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.98 2.98 0 0 0 0-4.24a2.98 2.98 0 0 0-4.24 0l-3.53 3.53a2.98 2.98 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.98 2.98 0 0 0 0 4.24a2.98 2.98 0 0 0 4.24 0l3.53-3.53a2.98 2.98 0 0 0 0-4.24a.973.973 0 0 1 0-1.42\"\u002F>\u003C\u002Fsvg>\n              \u003C\u002Fspan>\u003Ca class=\"header-anchor\" href=\"#user-budget-tracker\">user_budget_tracker\u003C\u002Fa>\u003C\u002Fh4>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth style=\"text-align:left\">Column\u003C\u002Fth>\n\u003Cth style=\"text-align:left\">Type\u003C\u002Fth>\n\u003Cth style=\"text-align:left\">Extra\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd style=\"text-align:left\">user_id\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">string\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">Primary Key, Foreign Key, UUIDv4\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd style=\"text-align:left\">budget_tracker_id\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">string\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">Primary Key, Foreign Key, UUIDv4\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd style=\"text-align:left\">role\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">string\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">Not Null, default “viewer”\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Ch4 id=\"category\" tabindex=\"-1\">\u003Cspan\n                class=\"header-copy-icon\"\n                role=\"button\"\n                data-slug=\"category\"\n              >\n                \u003Csvg>\u003Cpath fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.98 2.98 0 0 0 0-4.24a2.98 2.98 0 0 0-4.24 0l-3.53 3.53a2.98 2.98 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.98 2.98 0 0 0 0 4.24a2.98 2.98 0 0 0 4.24 0l3.53-3.53a2.98 2.98 0 0 0 0-4.24a.973.973 0 0 1 0-1.42\"\u002F>\u003C\u002Fsvg>\n              \u003C\u002Fspan>\u003Ca class=\"header-anchor\" href=\"#category\">category\u003C\u002Fa>\u003C\u002Fh4>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth style=\"text-align:left\">Column\u003C\u002Fth>\n\u003Cth style=\"text-align:left\">Type\u003C\u002Fth>\n\u003Cth style=\"text-align:left\">Extra\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd style=\"text-align:left\">id\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">string\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">Primary Key, UUIDv4\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd style=\"text-align:left\">name\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">string\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">Not Null\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd style=\"text-align:left\">icon\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">string\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">Not Null\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd style=\"text-align:left\">color\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">string\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">Not Null\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd style=\"text-align:left\">budget_tracker_id\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">string\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">Not Null, Foreign Key\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Ch4 id=\"spending\" tabindex=\"-1\">\u003Cspan\n                class=\"header-copy-icon\"\n                role=\"button\"\n                data-slug=\"spending\"\n              >\n                \u003Csvg>\u003Cpath fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.98 2.98 0 0 0 0-4.24a2.98 2.98 0 0 0-4.24 0l-3.53 3.53a2.98 2.98 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.98 2.98 0 0 0 0 4.24a2.98 2.98 0 0 0 4.24 0l3.53-3.53a2.98 2.98 0 0 0 0-4.24a.973.973 0 0 1 0-1.42\"\u002F>\u003C\u002Fsvg>\n              \u003C\u002Fspan>\u003Ca class=\"header-anchor\" href=\"#spending\">spending\u003C\u002Fa>\u003C\u002Fh4>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth style=\"text-align:left\">Column\u003C\u002Fth>\n\u003Cth style=\"text-align:left\">Type\u003C\u002Fth>\n\u003Cth style=\"text-align:left\">Extra\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd style=\"text-align:left\">id\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">string\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">Primary Key, UUIDv4\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd style=\"text-align:left\">name\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">string\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">Not Null\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd style=\"text-align:left\">budget_tracker_id\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">string\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">Not Null, Foreign Key\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd style=\"text-align:left\">value\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">float\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">Not Null\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd style=\"text-align:left\">is_spending\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">boolean\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">Not Null, default true\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd style=\"text-align:left\">category_id\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">string\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">Foreign Key, Not Null\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd style=\"text-align:left\">date\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">date\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">Not Null\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Ch4 id=\"mermaid-diagram\" tabindex=\"-1\">\u003Cspan\n                class=\"header-copy-icon\"\n                role=\"button\"\n                data-slug=\"mermaid-diagram\"\n              >\n                \u003Csvg>\u003Cpath fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.98 2.98 0 0 0 0-4.24a2.98 2.98 0 0 0-4.24 0l-3.53 3.53a2.98 2.98 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.98 2.98 0 0 0 0 4.24a2.98 2.98 0 0 0 4.24 0l3.53-3.53a2.98 2.98 0 0 0 0-4.24a.973.973 0 0 1 0-1.42\"\u002F>\u003C\u002Fsvg>\n              \u003C\u002Fspan>\u003Ca class=\"header-anchor\" href=\"#mermaid-diagram\">Mermaid diagram\u003C\u002Fa>\u003C\u002Fh4>\n\n          \u003Cdiv class='code-block'>\n            \u003Cdiv class='code-block-header'>\n              \u003Cspan class='code-block-lang'>mermaid\u003C\u002Fspan>\n              \u003Cbutton class='copy-code-button' type='button'>\n                Copy\n              \u003C\u002Fbutton>\n            \u003C\u002Fdiv>\n            \u003Cpre>\u003Ccode class='hljs mermaid'>erDiagram\n  budget_tracker {\n    \u003Cspan class=\"hljs-type\">TEXT\u003C\u002Fspan> id PK\n    \u003Cspan class=\"hljs-type\">TEXT\u003C\u002Fspan> \u003Cspan class=\"hljs-type\">name\u003C\u002Fspan>\n  }\n\n  user_budget_tracker {\n    \u003Cspan class=\"hljs-type\">TEXT\u003C\u002Fspan> user_id PK FK\n    \u003Cspan class=\"hljs-type\">TEXT\u003C\u002Fspan> budget_tracker_id PK FK\n    \u003Cspan class=\"hljs-type\">TEXT\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">role\u003C\u002Fspan>\n  }\n\n  category {\n    \u003Cspan class=\"hljs-type\">TEXT\u003C\u002Fspan> id PK\n    \u003Cspan class=\"hljs-type\">TEXT\u003C\u002Fspan> \u003Cspan class=\"hljs-type\">name\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-type\">TEXT\u003C\u002Fspan> icon\n    \u003Cspan class=\"hljs-type\">TEXT\u003C\u002Fspan> color\n    \u003Cspan class=\"hljs-type\">TEXT\u003C\u002Fspan> budget_tracker_id FK\n  }\n\n  spending {\n    \u003Cspan class=\"hljs-type\">TEXT\u003C\u002Fspan> id PK\n    \u003Cspan class=\"hljs-type\">TEXT\u003C\u002Fspan> \u003Cspan class=\"hljs-type\">name\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-type\">TEXT\u003C\u002Fspan> budget_tracker_id FK\n    \u003Cspan class=\"hljs-type\">REAL\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">value\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-type\">BOOLEAN\u003C\u002Fspan> is_spending\n    \u003Cspan class=\"hljs-type\">TEXT\u003C\u002Fspan> category_id FK\n    DATETIME \u003Cspan class=\"hljs-type\">date\u003C\u002Fspan>\n  }\n\n  budget_tracker ||\u003Cspan class=\"hljs-comment\">--o{ user_budget_tracker : has\u003C\u002Fspan>\n  budget_tracker ||\u003Cspan class=\"hljs-comment\">--o{ spending : contains\u003C\u002Fspan>\n  budget_tracker ||\u003Cspan class=\"hljs-comment\">--o{ category : has\u003C\u002Fspan>\n  category ||\u003Cspan class=\"hljs-comment\">--o{ spending : classifies\u003C\u002Fspan>\n\u003C\u002Fcode>\u003C\u002Fpre>\n          \u003C\u002Fdiv>\n        \u003C\u002Fdetails>\n",1779532541255]