Compare commits

..
Author SHA1 Message Date
Justin Lin 0467bf8d3f chore(deps): bump the npm_and_yarn group across 2 directories with 2 updates (#223)
Bumps the npm_and_yarn group with 1 update in the /app directory:
[vue-i18n](https://github.com/intlify/vue-i18n/tree/HEAD/packages/vue-i18n).
Bumps the npm_and_yarn group with 1 update in the /packages/constants
directory: [zod](https://github.com/colinhacks/zod).

Updates `vue-i18n` from 11.3.0 to 11.4.14
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/intlify/vue-i18n/releases">vue-i18n's
releases</a>.</em></p>
<blockquote>
<h2>v11.4.14</h2>
<!-- raw HTML omitted -->
<h2>What's Changed</h2>
<h3>🔒 Security Fixes</h3>
<ul>
<li>fix(shared): neutralize srcdoc attribute in sanitizeTranslatedHtml
by <a href="https://github.com/kazupon"><code>@​kazupon</code></a> in <a
href="https://redirect.github.com/intlify/vue-i18n/pull/2679">intlify/vue-i18n#2679</a></li>
<li>fix(core-base): escape both list and named values with
escapeParameter by <a
href="https://github.com/kazupon"><code>@​kazupon</code></a> in <a
href="https://redirect.github.com/intlify/vue-i18n/pull/2680">intlify/vue-i18n#2680</a></li>
<li>fix: ignore locales named after Object.prototype properties by <a
href="https://github.com/kazupon"><code>@​kazupon</code></a> in <a
href="https://redirect.github.com/intlify/vue-i18n/pull/2681">intlify/vue-i18n#2681</a></li>
</ul>
<h3>👕 Refactoring</h3>
<ul>
<li>refactor(core-base): extract the resolved message format check by <a
href="https://github.com/kazupon"><code>@​kazupon</code></a> in <a
href="https://redirect.github.com/intlify/vue-i18n/pull/2674">intlify/vue-i18n#2674</a></li>
<li>refactor(vue-i18n-core): remove dead branches in getCoreContext() by
<a href="https://github.com/kazupon"><code>@​kazupon</code></a> in <a
href="https://redirect.github.com/intlify/vue-i18n/pull/2675">intlify/vue-i18n#2675</a></li>
<li>refactor(vue-i18n-core): share the fallbackContext handling in the
composer by <a
href="https://github.com/kazupon"><code>@​kazupon</code></a> in <a
href="https://redirect.github.com/intlify/vue-i18n/pull/2676">intlify/vue-i18n#2676</a></li>
<li>refactor(vue-i18n-core): simplify flat JSON handling in
setLocaleMessage / mergeLocaleMessage by <a
href="https://github.com/kazupon"><code>@​kazupon</code></a> in <a
href="https://redirect.github.com/intlify/vue-i18n/pull/2677">intlify/vue-i18n#2677</a></li>
<li>refactor(core-base): give the compile cache a proper type by <a
href="https://github.com/kazupon"><code>@​kazupon</code></a> in <a
href="https://redirect.github.com/intlify/vue-i18n/pull/2678">intlify/vue-i18n#2678</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/intlify/vue-i18n/compare/v11.4.13...v11.4.14">https://github.com/intlify/vue-i18n/compare/v11.4.13...v11.4.14</a></p>
<h2>v11.4.13</h2>
<!-- raw HTML omitted -->
<h2>What's Changed</h2>
<h3>⚡ Improvement Features</h3>
<ul>
<li>fix(core-base): map locale unions without the UnionToTuple
round-trip by <a
href="https://github.com/kazupon"><code>@​kazupon</code></a> in <a
href="https://redirect.github.com/intlify/vue-i18n/pull/2651">intlify/vue-i18n#2651</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/intlify/vue-i18n/compare/v11.4.12...v11.4.13">https://github.com/intlify/vue-i18n/compare/v11.4.12...v11.4.13</a></p>
<h2>v11.4.12</h2>
<!-- raw HTML omitted -->
<h2>What's Changed</h2>
<h3>🐛 Bug Fixes</h3>
<ul>
<li>fix(core-base): cache locale chains by fallback identity by <a
href="https://github.com/kazupon"><code>@​kazupon</code></a> in <a
href="https://redirect.github.com/intlify/vue-i18n/pull/2635">intlify/vue-i18n#2635</a></li>
<li>fix(vue-i18n-core): sync the core context when inheritLocale is
enabled by <a
href="https://github.com/kazupon"><code>@​kazupon</code></a> in <a
href="https://redirect.github.com/intlify/vue-i18n/pull/2636">intlify/vue-i18n#2636</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/intlify/vue-i18n/compare/v11.4.10...v11.4.12">https://github.com/intlify/vue-i18n/compare/v11.4.10...v11.4.12</a></p>
<h2>v11.4.10</h2>
<!-- raw HTML omitted -->
<h2>What's Changed</h2>
<h3>🐛 Bug Fixes</h3>
<ul>
<li>fix(core-base): avoid throwing on AST keys in devtools groupId by <a
href="https://github.com/kazupon"><code>@​kazupon</code></a> in <a
href="https://redirect.github.com/intlify/vue-i18n/pull/2603">intlify/vue-i18n#2603</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/intlify/vue-i18n/compare/v11.4.9...v11.4.10">https://github.com/intlify/vue-i18n/compare/v11.4.9...v11.4.10</a></p>
<h2>v11.4.9</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/intlify/vue-i18n/blob/v11.4.14/CHANGELOG.md">vue-i18n's
changelog</a>.</em></p>
<blockquote>
<h1>v11.4.14 (2026-10-10T11:06:13.502Z)</h1>
<p>This changelog is generated by <a
href="https://github.com/intlify/vue-i18n/releases/tag/v11.4.14">GitHub
Releases</a></p>
<!-- raw HTML omitted -->
<h2>What's Changed</h2>
<h3>🔒 Security Fixes</h3>
<ul>
<li>fix(shared): neutralize srcdoc attribute in sanitizeTranslatedHtml
by <a href="https://github.com/kazupon"><code>@​kazupon</code></a> in <a
href="https://redirect.github.com/intlify/vue-i18n/pull/2679">intlify/vue-i18n#2679</a></li>
<li>fix(core-base): escape both list and named values with
escapeParameter by <a
href="https://github.com/kazupon"><code>@​kazupon</code></a> in <a
href="https://redirect.github.com/intlify/vue-i18n/pull/2680">intlify/vue-i18n#2680</a></li>
<li>fix: ignore locales named after Object.prototype properties by <a
href="https://github.com/kazupon"><code>@​kazupon</code></a> in <a
href="https://redirect.github.com/intlify/vue-i18n/pull/2681">intlify/vue-i18n#2681</a></li>
</ul>
<h3>👕 Refactoring</h3>
<ul>
<li>refactor(core-base): extract the resolved message format check by <a
href="https://github.com/kazupon"><code>@​kazupon</code></a> in <a
href="https://redirect.github.com/intlify/vue-i18n/pull/2674">intlify/vue-i18n#2674</a></li>
<li>refactor(vue-i18n-core): remove dead branches in getCoreContext() by
<a href="https://github.com/kazupon"><code>@​kazupon</code></a> in <a
href="https://redirect.github.com/intlify/vue-i18n/pull/2675">intlify/vue-i18n#2675</a></li>
<li>refactor(vue-i18n-core): share the fallbackContext handling in the
composer by <a
href="https://github.com/kazupon"><code>@​kazupon</code></a> in <a
href="https://redirect.github.com/intlify/vue-i18n/pull/2676">intlify/vue-i18n#2676</a></li>
<li>refactor(vue-i18n-core): simplify flat JSON handling in
setLocaleMessage / mergeLocaleMessage by <a
href="https://github.com/kazupon"><code>@​kazupon</code></a> in <a
href="https://redirect.github.com/intlify/vue-i18n/pull/2677">intlify/vue-i18n#2677</a></li>
<li>refactor(core-base): give the compile cache a proper type by <a
href="https://github.com/kazupon"><code>@​kazupon</code></a> in <a
href="https://redirect.github.com/intlify/vue-i18n/pull/2678">intlify/vue-i18n#2678</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/intlify/vue-i18n/compare/v11.4.13...v11.4.14">https://github.com/intlify/vue-i18n/compare/v11.4.13...v11.4.14</a></p>
<h1>v11.4.13 (2026-10-02T11:01:59.781Z)</h1>
<p>This changelog is generated by <a
href="https://github.com/intlify/vue-i18n/releases/tag/v11.4.13">GitHub
Releases</a></p>
<!-- raw HTML omitted -->
<h2>What's Changed</h2>
<h3>⚡ Improvement Features</h3>
<ul>
<li>fix(core-base): map locale unions without the UnionToTuple
round-trip by <a
href="https://github.com/kazupon"><code>@​kazupon</code></a> in <a
href="https://redirect.github.com/intlify/vue-i18n/pull/2651">intlify/vue-i18n#2651</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/intlify/vue-i18n/compare/v11.4.12...v11.4.13">https://github.com/intlify/vue-i18n/compare/v11.4.12...v11.4.13</a></p>
<h1>v11.4.12 (2026-09-16T14:30:30.396Z)</h1>
<p>This changelog is generated by <a
href="https://github.com/intlify/vue-i18n/releases/tag/v11.4.12">GitHub
Releases</a></p>
<!-- raw HTML omitted -->
<h2>What's Changed</h2>
<h3>🐛 Bug Fixes</h3>
<ul>
<li>fix(core-base): cache locale chains by fallback identity by <a
href="https://github.com/kazupon"><code>@​kazupon</code></a> in <a
href="https://redirect.github.com/intlify/vue-i18n/pull/2635">intlify/vue-i18n#2635</a></li>
<li>fix(vue-i18n-core): sync the core context when inheritLocale is
enabled by <a
href="https://github.com/kazupon"><code>@​kazupon</code></a> in <a
href="https://redirect.github.com/intlify/vue-i18n/pull/2636">intlify/vue-i18n#2636</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/intlify/vue-i18n/commit/6bb9ebdeae4955cafb53e82bc461199f6186f573"><code>6bb9ebd</code></a>
release: v11.4.14</li>
<li><a
href="https://github.com/intlify/vue-i18n/commit/f6b3e75a16f43f4d9a7a567b1b36cd638e880831"><code>f6b3e75</code></a>
release: v11.4.13</li>
<li><a
href="https://github.com/intlify/vue-i18n/commit/87f3745072ca15cbd2d1eb8d030aed10639b0e3e"><code>87f3745</code></a>
release: v11.4.12</li>
<li><a
href="https://github.com/intlify/vue-i18n/commit/87510e2f9344e0734bfe9fbc7e0a2c509042e159"><code>87510e2</code></a>
release: v11.4.10</li>
<li><a
href="https://github.com/intlify/vue-i18n/commit/5128bf00a1e23fb1defb01ea80622dd6fedd1e21"><code>5128bf0</code></a>
release: v11.4.9</li>
<li><a
href="https://github.com/intlify/vue-i18n/commit/de2393bae16676a8a7c293bb493415e3085a521a"><code>de2393b</code></a>
release: v11.4.8</li>
<li><a
href="https://github.com/intlify/vue-i18n/commit/ceb0548cb421b8aaf237fecda0394d8fd3d75da7"><code>ceb0548</code></a>
release: v11.4.7</li>
<li><a
href="https://github.com/intlify/vue-i18n/commit/913bbfee3d0fe1b0ff321f9670ee7de5c0a5ae51"><code>913bbfe</code></a>
release: v11.4.6</li>
<li><a
href="https://github.com/intlify/vue-i18n/commit/6b97bfd57387b440d31e76af665cea4a5cdbd8d3"><code>6b97bfd</code></a>
release: v11.4.5</li>
<li><a
href="https://github.com/intlify/vue-i18n/commit/99fd4bcaf9e38b39857653eecf53b5038a6e8a49"><code>99fd4bc</code></a>
release: v11.4.4</li>
<li>Additional commits viewable in <a
href="https://github.com/intlify/vue-i18n/commits/v11.4.14/packages/vue-i18n">compare
view</a></li>
</ul>
</details>
<br />

Updates `zod` from 3.25.76 to 4.6.7
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/colinhacks/zod/releases">zod's
releases</a>.</em></p>
<blockquote>
<h2>v4.6.7</h2>
<h2>Commits:</h2>
<ul>
<li>b500a2ee95134f81d74da7911d7378dd2853d042 4.6.7</li>
</ul>
<h2>v4.6.6</h2>
<blockquote>
<p>Six malicious commits pushed to <code>main</code> on September 21
with a compromised git credential were reverted the same day in <a
href="https://redirect.github.com/colinhacks/zod/issues/6620">#6620</a>
and have since been removed from the history of <code>main</code>. None
were ever published to npm. The npm provenance for 4.6.6 names 284243fb,
the pre-rewrite copy of 9c219833 with identical contents.</p>
</blockquote>
<h2>Commits:</h2>
<ul>
<li>0cd3af5e9f50ad11bbcd4407a46a21b8848ba574 docs: load the en locale
directly in Zod Mini</li>
<li>9d5e60f162997b764b40d45aa02481888db50dea docs: import locales from
zod/locales, not z.locales</li>
<li>34a626f307871ecbb0123947559328dc52ed5e3e docs: tighten the z.locales
tree-shaking note</li>
<li>74304ce5b5796b5edb9a88ceb8e170b6ea3e82df release: stage npm versions
instead of publishing them (<a
href="https://redirect.github.com/colinhacks/zod/issues/6622">#6622</a>)</li>
<li>b2ef983e0711ff6517d6020b021071e2e5653efa docs: add Code for Japan to
bronze sponsors</li>
<li>8fb2faf0584828a6a027d8279db36f6f52784462 docs: crop the Code for
Japan logo and list it first in bronze</li>
<li>bacad518c41610d654658172d325874bf0ee6290 docs: square the Code for
Japan logo to match the other bronze tiles</li>
<li>c42596bd1c87e941d7241d10e012c5c196adcc10 release: hold the run on
the release-approval environment until the staged versions are approved
(<a
href="https://redirect.github.com/colinhacks/zod/issues/6638">#6638</a>)</li>
<li>a2ba4a67986a3e6b2b131be30084a2f460d9ea67 fix(types): declare record
output and input as members, not base type arguments (<a
href="https://redirect.github.com/colinhacks/zod/issues/6645">#6645</a>)</li>
<li>a0ebf970f2d752e5f58527cd2942ff48be4f46bd fix(v4): check array, set
and map size bounds before parsing elements (<a
href="https://redirect.github.com/colinhacks/zod/issues/6652">#6652</a>)</li>
<li>529f78dd9431b07f49647fee3aa0819b3f8b66b7 4.6.6</li>
<li>9c219833c3a3764fa1f1ea9c5ecec8254a63c0b1 ci: stage at verbose so a
failed OIDC token exchange says why</li>
</ul>
<h2>v4.6.5</h2>
<h2>Commits:</h2>
<ul>
<li>d2b135cfb7a3582b9eb515756b9166bcb9521f4a docs: add the 4.6.x patch
highlights to the 4.6 post</li>
<li>f1448f7cee00df9fe1e9ad84a000aa1828cc8bc1 docs: fold the 4.6.x patch
highlights into the 4.6 post's own sections</li>
<li>de65a5cb39ed22a507fac935788f718fa88d104f docs: lead the properties
section with the check and add a Zod Mini tab (<a
href="https://redirect.github.com/colinhacks/zod/issues/6598">#6598</a>)</li>
<li>56222cd1532c07bcb91b67df529cab4c0a215330 feat(instanceof): key the
.properties() shape off the instance type (<a
href="https://redirect.github.com/colinhacks/zod/issues/6600">#6600</a>)</li>
<li>ca0229a404818290e6cdcfefcd7eb2d04bcbb543 Revert &quot;feat: add
z.currencyCode() over a vendored ISO 4217 list, refreshed weekly by CI
(<a
href="https://redirect.github.com/colinhacks/zod/issues/6595">#6595</a>)&quot;</li>
<li>cc4cd4ee9c52fcaa10964e48cc144541e41a5ed9 Revert &quot;Revert
&quot;feat: add z.currencyCode() over a vendored ISO 4217 list,
refreshed weekly by CI (<a
href="https://redirect.github.com/colinhacks/zod/issues/6595">#6595</a>)&quot;&quot;</li>
<li>0f3f5ee3ca56c7574bf849e54f79e9a6e02562ee 4.6.5</li>
<li>59bbc03e10c636b9eb3c393dfeb552819774ec21 chore: re-pin the
integration peers to the workspace zod after the 4.6.5 bump</li>
</ul>
<h2>v4.6.4</h2>
<p>A patch on top of <a
href="https://github.com/colinhacks/zod/releases/tag/v4.6.3">4.6.3</a>.</p>
<ul>
<li><a
href="https://github.com/colinhacks/zod/commit/d6bc1e30"><code>d6bc1e30</code></a>
feat: add <code>z.currencyCode()</code> over a vendored ISO 4217 list,
refreshed weekly by CI (<a
href="https://redirect.github.com/colinhacks/zod/pull/6595">#6595</a>)</li>
<li><a
href="https://github.com/colinhacks/zod/commit/ad32d751"><code>ad32d751</code></a>
perf: <code>z.url()</code> rejects an invalid URL with
<code>URL.canParse()</code> instead of a throwing constructor, about 50x
faster; fewer allocations on the validation path (<a
href="https://redirect.github.com/colinhacks/zod/pull/6588">#6588</a>)</li>
<li><a
href="https://github.com/colinhacks/zod/commit/2bb08717"><code>2bb08717</code></a>
chore: re-pin the integration peers to the workspace zod after the 4.6.4
bump</li>
<li><a
href="https://github.com/colinhacks/zod/commit/f6e1701a"><code>f6e1701a</code></a>
chore(deps): bump next to 15.5.25 and vite to 7.3.6 (<a
href="https://redirect.github.com/colinhacks/zod/pull/6153">#6153</a>)</li>
</ul>
<h2>v4.6.3</h2>
<p>A patch on top of <a
href="https://github.com/colinhacks/zod/releases/tag/v4.6.2">4.6.2</a>.</p>
<ul>
<li><a
href="https://github.com/colinhacks/zod/commit/413cce9a"><code>413cce9a</code></a>
fix(v4): make z.properties() a check again (<a
href="https://redirect.github.com/colinhacks/zod/pull/6594">#6594</a>) —
removes the standalone <code>z.properties()</code> schema from 4.6.0;
<code>z.instanceof().properties()</code> and
<code>.check(...z.properties())</code> are unchanged</li>
<li><a
href="https://github.com/colinhacks/zod/commit/75d63ee1"><code>75d63ee1</code></a>
docs: show only the <code>.properties()</code> method form in the 4.6
post</li>
<li><a
href="https://github.com/colinhacks/zod/commit/46da9572"><code>46da9572</code></a>
docs: match the error-message examples to what the parsers emit</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/colinhacks/zod/commit/b500a2ee95134f81d74da7911d7378dd2853d042"><code>b500a2e</code></a>
4.6.7</li>
<li><a
href="https://github.com/colinhacks/zod/commit/9c219833c3a3764fa1f1ea9c5ecec8254a63c0b1"><code>9c21983</code></a>
ci: stage at verbose so a failed OIDC token exchange says why</li>
<li><a
href="https://github.com/colinhacks/zod/commit/529f78dd9431b07f49647fee3aa0819b3f8b66b7"><code>529f78d</code></a>
4.6.6</li>
<li><a
href="https://github.com/colinhacks/zod/commit/a0ebf970f2d752e5f58527cd2942ff48be4f46bd"><code>a0ebf97</code></a>
fix(v4): check array, set and map size bounds before parsing elements
(<a
href="https://redirect.github.com/colinhacks/zod/issues/6652">#6652</a>)</li>
<li><a
href="https://github.com/colinhacks/zod/commit/a2ba4a67986a3e6b2b131be30084a2f460d9ea67"><code>a2ba4a6</code></a>
fix(types): declare record output and input as members, not base type
argumen...</li>
<li><a
href="https://github.com/colinhacks/zod/commit/c42596bd1c87e941d7241d10e012c5c196adcc10"><code>c42596b</code></a>
release: hold the run on the release-approval environment until the
staged ve...</li>
<li><a
href="https://github.com/colinhacks/zod/commit/bacad518c41610d654658172d325874bf0ee6290"><code>bacad51</code></a>
docs: square the Code for Japan logo to match the other bronze
tiles</li>
<li><a
href="https://github.com/colinhacks/zod/commit/8fb2faf0584828a6a027d8279db36f6f52784462"><code>8fb2faf</code></a>
docs: crop the Code for Japan logo and list it first in bronze</li>
<li><a
href="https://github.com/colinhacks/zod/commit/b2ef983e0711ff6517d6020b021071e2e5653efa"><code>b2ef983</code></a>
docs: add Code for Japan to bronze sponsors</li>
<li><a
href="https://github.com/colinhacks/zod/commit/74304ce5b5796b5edb9a88ceb8e170b6ea3e82df"><code>74304ce</code></a>
release: stage npm versions instead of publishing them (<a
href="https://redirect.github.com/colinhacks/zod/issues/6622">#6622</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/colinhacks/zod/compare/v3.25.76...v4.6.7">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for zod since your current version.</p>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/linux-odyssey/linux-odyssey/network/alerts).

</details>
2026-11-03 20:43:29 +11:00
Justin Lin 7caadd6695 deps: update zod and vue-i18n 2026-11-03 20:42:28 +11:00
dependabot[bot] 7ab6a828ca chore(deps): bump the npm_and_yarn group across 2 directories with 2 updates
Bumps the npm_and_yarn group with 1 update in the /app directory: [vue-i18n](https://github.com/intlify/vue-i18n/tree/HEAD/packages/vue-i18n).
Bumps the npm_and_yarn group with 1 update in the /packages/constants directory: [zod](https://github.com/colinhacks/zod).


Updates `vue-i18n` from 11.3.0 to 11.4.14
- [Release notes](https://github.com/intlify/vue-i18n/releases)
- [Changelog](https://github.com/intlify/vue-i18n/blob/v11.4.14/CHANGELOG.md)
- [Commits](https://github.com/intlify/vue-i18n/commits/v11.4.14/packages/vue-i18n)

Updates `zod` from 3.25.76 to 4.6.7
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v3.25.76...v4.6.7)

---
updated-dependencies:
- dependency-name: vue-i18n
  dependency-version: 11.4.14
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: zod
  dependency-version: 4.6.7
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-11-03 09:39:04 +00:00
Justin Lin acbe310a4c chore(deps): bump the npm_and_yarn group across 2 directories with 1 update (#221)
Bumps the npm_and_yarn group with 1 update in the /server directory:
[zod](https://github.com/colinhacks/zod).
Bumps the npm_and_yarn group with 1 update in the /services/analytics
directory: [zod](https://github.com/colinhacks/zod).

Updates `zod` from 3.25.76 to 4.6.7
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/colinhacks/zod/releases">zod's
releases</a>.</em></p>
<blockquote>
<h2>v4.6.7</h2>
<h2>Commits:</h2>
<ul>
<li>b500a2ee95134f81d74da7911d7378dd2853d042 4.6.7</li>
</ul>
<h2>v4.6.6</h2>
<blockquote>
<p>Six malicious commits pushed to <code>main</code> on September 21
with a compromised git credential were reverted the same day in <a
href="https://redirect.github.com/colinhacks/zod/issues/6620">#6620</a>
and have since been removed from the history of <code>main</code>. None
were ever published to npm. The npm provenance for 4.6.6 names 284243fb,
the pre-rewrite copy of 9c219833 with identical contents.</p>
</blockquote>
<h2>Commits:</h2>
<ul>
<li>0cd3af5e9f50ad11bbcd4407a46a21b8848ba574 docs: load the en locale
directly in Zod Mini</li>
<li>9d5e60f162997b764b40d45aa02481888db50dea docs: import locales from
zod/locales, not z.locales</li>
<li>34a626f307871ecbb0123947559328dc52ed5e3e docs: tighten the z.locales
tree-shaking note</li>
<li>74304ce5b5796b5edb9a88ceb8e170b6ea3e82df release: stage npm versions
instead of publishing them (<a
href="https://redirect.github.com/colinhacks/zod/issues/6622">#6622</a>)</li>
<li>b2ef983e0711ff6517d6020b021071e2e5653efa docs: add Code for Japan to
bronze sponsors</li>
<li>8fb2faf0584828a6a027d8279db36f6f52784462 docs: crop the Code for
Japan logo and list it first in bronze</li>
<li>bacad518c41610d654658172d325874bf0ee6290 docs: square the Code for
Japan logo to match the other bronze tiles</li>
<li>c42596bd1c87e941d7241d10e012c5c196adcc10 release: hold the run on
the release-approval environment until the staged versions are approved
(<a
href="https://redirect.github.com/colinhacks/zod/issues/6638">#6638</a>)</li>
<li>a2ba4a67986a3e6b2b131be30084a2f460d9ea67 fix(types): declare record
output and input as members, not base type arguments (<a
href="https://redirect.github.com/colinhacks/zod/issues/6645">#6645</a>)</li>
<li>a0ebf970f2d752e5f58527cd2942ff48be4f46bd fix(v4): check array, set
and map size bounds before parsing elements (<a
href="https://redirect.github.com/colinhacks/zod/issues/6652">#6652</a>)</li>
<li>529f78dd9431b07f49647fee3aa0819b3f8b66b7 4.6.6</li>
<li>9c219833c3a3764fa1f1ea9c5ecec8254a63c0b1 ci: stage at verbose so a
failed OIDC token exchange says why</li>
</ul>
<h2>v4.6.5</h2>
<h2>Commits:</h2>
<ul>
<li>d2b135cfb7a3582b9eb515756b9166bcb9521f4a docs: add the 4.6.x patch
highlights to the 4.6 post</li>
<li>f1448f7cee00df9fe1e9ad84a000aa1828cc8bc1 docs: fold the 4.6.x patch
highlights into the 4.6 post's own sections</li>
<li>de65a5cb39ed22a507fac935788f718fa88d104f docs: lead the properties
section with the check and add a Zod Mini tab (<a
href="https://redirect.github.com/colinhacks/zod/issues/6598">#6598</a>)</li>
<li>56222cd1532c07bcb91b67df529cab4c0a215330 feat(instanceof): key the
.properties() shape off the instance type (<a
href="https://redirect.github.com/colinhacks/zod/issues/6600">#6600</a>)</li>
<li>ca0229a404818290e6cdcfefcd7eb2d04bcbb543 Revert &quot;feat: add
z.currencyCode() over a vendored ISO 4217 list, refreshed weekly by CI
(<a
href="https://redirect.github.com/colinhacks/zod/issues/6595">#6595</a>)&quot;</li>
<li>cc4cd4ee9c52fcaa10964e48cc144541e41a5ed9 Revert &quot;Revert
&quot;feat: add z.currencyCode() over a vendored ISO 4217 list,
refreshed weekly by CI (<a
href="https://redirect.github.com/colinhacks/zod/issues/6595">#6595</a>)&quot;&quot;</li>
<li>0f3f5ee3ca56c7574bf849e54f79e9a6e02562ee 4.6.5</li>
<li>59bbc03e10c636b9eb3c393dfeb552819774ec21 chore: re-pin the
integration peers to the workspace zod after the 4.6.5 bump</li>
</ul>
<h2>v4.6.4</h2>
<p>A patch on top of <a
href="https://github.com/colinhacks/zod/releases/tag/v4.6.3">4.6.3</a>.</p>
<ul>
<li><a
href="https://github.com/colinhacks/zod/commit/d6bc1e30"><code>d6bc1e30</code></a>
feat: add <code>z.currencyCode()</code> over a vendored ISO 4217 list,
refreshed weekly by CI (<a
href="https://redirect.github.com/colinhacks/zod/pull/6595">#6595</a>)</li>
<li><a
href="https://github.com/colinhacks/zod/commit/ad32d751"><code>ad32d751</code></a>
perf: <code>z.url()</code> rejects an invalid URL with
<code>URL.canParse()</code> instead of a throwing constructor, about 50x
faster; fewer allocations on the validation path (<a
href="https://redirect.github.com/colinhacks/zod/pull/6588">#6588</a>)</li>
<li><a
href="https://github.com/colinhacks/zod/commit/2bb08717"><code>2bb08717</code></a>
chore: re-pin the integration peers to the workspace zod after the 4.6.4
bump</li>
<li><a
href="https://github.com/colinhacks/zod/commit/f6e1701a"><code>f6e1701a</code></a>
chore(deps): bump next to 15.5.25 and vite to 7.3.6 (<a
href="https://redirect.github.com/colinhacks/zod/pull/6153">#6153</a>)</li>
</ul>
<h2>v4.6.3</h2>
<p>A patch on top of <a
href="https://github.com/colinhacks/zod/releases/tag/v4.6.2">4.6.2</a>.</p>
<ul>
<li><a
href="https://github.com/colinhacks/zod/commit/413cce9a"><code>413cce9a</code></a>
fix(v4): make z.properties() a check again (<a
href="https://redirect.github.com/colinhacks/zod/pull/6594">#6594</a>) —
removes the standalone <code>z.properties()</code> schema from 4.6.0;
<code>z.instanceof().properties()</code> and
<code>.check(...z.properties())</code> are unchanged</li>
<li><a
href="https://github.com/colinhacks/zod/commit/75d63ee1"><code>75d63ee1</code></a>
docs: show only the <code>.properties()</code> method form in the 4.6
post</li>
<li><a
href="https://github.com/colinhacks/zod/commit/46da9572"><code>46da9572</code></a>
docs: match the error-message examples to what the parsers emit</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/colinhacks/zod/commit/b500a2ee95134f81d74da7911d7378dd2853d042"><code>b500a2e</code></a>
4.6.7</li>
<li><a
href="https://github.com/colinhacks/zod/commit/9c219833c3a3764fa1f1ea9c5ecec8254a63c0b1"><code>9c21983</code></a>
ci: stage at verbose so a failed OIDC token exchange says why</li>
<li><a
href="https://github.com/colinhacks/zod/commit/529f78dd9431b07f49647fee3aa0819b3f8b66b7"><code>529f78d</code></a>
4.6.6</li>
<li><a
href="https://github.com/colinhacks/zod/commit/a0ebf970f2d752e5f58527cd2942ff48be4f46bd"><code>a0ebf97</code></a>
fix(v4): check array, set and map size bounds before parsing elements
(<a
href="https://redirect.github.com/colinhacks/zod/issues/6652">#6652</a>)</li>
<li><a
href="https://github.com/colinhacks/zod/commit/a2ba4a67986a3e6b2b131be30084a2f460d9ea67"><code>a2ba4a6</code></a>
fix(types): declare record output and input as members, not base type
argumen...</li>
<li><a
href="https://github.com/colinhacks/zod/commit/c42596bd1c87e941d7241d10e012c5c196adcc10"><code>c42596b</code></a>
release: hold the run on the release-approval environment until the
staged ve...</li>
<li><a
href="https://github.com/colinhacks/zod/commit/bacad518c41610d654658172d325874bf0ee6290"><code>bacad51</code></a>
docs: square the Code for Japan logo to match the other bronze
tiles</li>
<li><a
href="https://github.com/colinhacks/zod/commit/8fb2faf0584828a6a027d8279db36f6f52784462"><code>8fb2faf</code></a>
docs: crop the Code for Japan logo and list it first in bronze</li>
<li><a
href="https://github.com/colinhacks/zod/commit/b2ef983e0711ff6517d6020b021071e2e5653efa"><code>b2ef983</code></a>
docs: add Code for Japan to bronze sponsors</li>
<li><a
href="https://github.com/colinhacks/zod/commit/74304ce5b5796b5edb9a88ceb8e170b6ea3e82df"><code>74304ce</code></a>
release: stage npm versions instead of publishing them (<a
href="https://redirect.github.com/colinhacks/zod/issues/6622">#6622</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/colinhacks/zod/compare/v3.25.76...v4.6.7">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for zod since your current version.</p>
</details>
<br />

Updates `zod` from 3.25.76 to 4.6.7
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/colinhacks/zod/releases">zod's
releases</a>.</em></p>
<blockquote>
<h2>v4.6.7</h2>
<h2>Commits:</h2>
<ul>
<li>b500a2ee95134f81d74da7911d7378dd2853d042 4.6.7</li>
</ul>
<h2>v4.6.6</h2>
<blockquote>
<p>Six malicious commits pushed to <code>main</code> on September 21
with a compromised git credential were reverted the same day in <a
href="https://redirect.github.com/colinhacks/zod/issues/6620">#6620</a>
and have since been removed from the history of <code>main</code>. None
were ever published to npm. The npm provenance for 4.6.6 names 284243fb,
the pre-rewrite copy of 9c219833 with identical contents.</p>
</blockquote>
<h2>Commits:</h2>
<ul>
<li>0cd3af5e9f50ad11bbcd4407a46a21b8848ba574 docs: load the en locale
directly in Zod Mini</li>
<li>9d5e60f162997b764b40d45aa02481888db50dea docs: import locales from
zod/locales, not z.locales</li>
<li>34a626f307871ecbb0123947559328dc52ed5e3e docs: tighten the z.locales
tree-shaking note</li>
<li>74304ce5b5796b5edb9a88ceb8e170b6ea3e82df release: stage npm versions
instead of publishing them (<a
href="https://redirect.github.com/colinhacks/zod/issues/6622">#6622</a>)</li>
<li>b2ef983e0711ff6517d6020b021071e2e5653efa docs: add Code for Japan to
bronze sponsors</li>
<li>8fb2faf0584828a6a027d8279db36f6f52784462 docs: crop the Code for
Japan logo and list it first in bronze</li>
<li>bacad518c41610d654658172d325874bf0ee6290 docs: square the Code for
Japan logo to match the other bronze tiles</li>
<li>c42596bd1c87e941d7241d10e012c5c196adcc10 release: hold the run on
the release-approval environment until the staged versions are approved
(<a
href="https://redirect.github.com/colinhacks/zod/issues/6638">#6638</a>)</li>
<li>a2ba4a67986a3e6b2b131be30084a2f460d9ea67 fix(types): declare record
output and input as members, not base type arguments (<a
href="https://redirect.github.com/colinhacks/zod/issues/6645">#6645</a>)</li>
<li>a0ebf970f2d752e5f58527cd2942ff48be4f46bd fix(v4): check array, set
and map size bounds before parsing elements (<a
href="https://redirect.github.com/colinhacks/zod/issues/6652">#6652</a>)</li>
<li>529f78dd9431b07f49647fee3aa0819b3f8b66b7 4.6.6</li>
<li>9c219833c3a3764fa1f1ea9c5ecec8254a63c0b1 ci: stage at verbose so a
failed OIDC token exchange says why</li>
</ul>
<h2>v4.6.5</h2>
<h2>Commits:</h2>
<ul>
<li>d2b135cfb7a3582b9eb515756b9166bcb9521f4a docs: add the 4.6.x patch
highlights to the 4.6 post</li>
<li>f1448f7cee00df9fe1e9ad84a000aa1828cc8bc1 docs: fold the 4.6.x patch
highlights into the 4.6 post's own sections</li>
<li>de65a5cb39ed22a507fac935788f718fa88d104f docs: lead the properties
section with the check and add a Zod Mini tab (<a
href="https://redirect.github.com/colinhacks/zod/issues/6598">#6598</a>)</li>
<li>56222cd1532c07bcb91b67df529cab4c0a215330 feat(instanceof): key the
.properties() shape off the instance type (<a
href="https://redirect.github.com/colinhacks/zod/issues/6600">#6600</a>)</li>
<li>ca0229a404818290e6cdcfefcd7eb2d04bcbb543 Revert &quot;feat: add
z.currencyCode() over a vendored ISO 4217 list, refreshed weekly by CI
(<a
href="https://redirect.github.com/colinhacks/zod/issues/6595">#6595</a>)&quot;</li>
<li>cc4cd4ee9c52fcaa10964e48cc144541e41a5ed9 Revert &quot;Revert
&quot;feat: add z.currencyCode() over a vendored ISO 4217 list,
refreshed weekly by CI (<a
href="https://redirect.github.com/colinhacks/zod/issues/6595">#6595</a>)&quot;&quot;</li>
<li>0f3f5ee3ca56c7574bf849e54f79e9a6e02562ee 4.6.5</li>
<li>59bbc03e10c636b9eb3c393dfeb552819774ec21 chore: re-pin the
integration peers to the workspace zod after the 4.6.5 bump</li>
</ul>
<h2>v4.6.4</h2>
<p>A patch on top of <a
href="https://github.com/colinhacks/zod/releases/tag/v4.6.3">4.6.3</a>.</p>
<ul>
<li><a
href="https://github.com/colinhacks/zod/commit/d6bc1e30"><code>d6bc1e30</code></a>
feat: add <code>z.currencyCode()</code> over a vendored ISO 4217 list,
refreshed weekly by CI (<a
href="https://redirect.github.com/colinhacks/zod/pull/6595">#6595</a>)</li>
<li><a
href="https://github.com/colinhacks/zod/commit/ad32d751"><code>ad32d751</code></a>
perf: <code>z.url()</code> rejects an invalid URL with
<code>URL.canParse()</code> instead of a throwing constructor, about 50x
faster; fewer allocations on the validation path (<a
href="https://redirect.github.com/colinhacks/zod/pull/6588">#6588</a>)</li>
<li><a
href="https://github.com/colinhacks/zod/commit/2bb08717"><code>2bb08717</code></a>
chore: re-pin the integration peers to the workspace zod after the 4.6.4
bump</li>
<li><a
href="https://github.com/colinhacks/zod/commit/f6e1701a"><code>f6e1701a</code></a>
chore(deps): bump next to 15.5.25 and vite to 7.3.6 (<a
href="https://redirect.github.com/colinhacks/zod/pull/6153">#6153</a>)</li>
</ul>
<h2>v4.6.3</h2>
<p>A patch on top of <a
href="https://github.com/colinhacks/zod/releases/tag/v4.6.2">4.6.2</a>.</p>
<ul>
<li><a
href="https://github.com/colinhacks/zod/commit/413cce9a"><code>413cce9a</code></a>
fix(v4): make z.properties() a check again (<a
href="https://redirect.github.com/colinhacks/zod/pull/6594">#6594</a>) —
removes the standalone <code>z.properties()</code> schema from 4.6.0;
<code>z.instanceof().properties()</code> and
<code>.check(...z.properties())</code> are unchanged</li>
<li><a
href="https://github.com/colinhacks/zod/commit/75d63ee1"><code>75d63ee1</code></a>
docs: show only the <code>.properties()</code> method form in the 4.6
post</li>
<li><a
href="https://github.com/colinhacks/zod/commit/46da9572"><code>46da9572</code></a>
docs: match the error-message examples to what the parsers emit</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/colinhacks/zod/commit/b500a2ee95134f81d74da7911d7378dd2853d042"><code>b500a2e</code></a>
4.6.7</li>
<li><a
href="https://github.com/colinhacks/zod/commit/9c219833c3a3764fa1f1ea9c5ecec8254a63c0b1"><code>9c21983</code></a>
ci: stage at verbose so a failed OIDC token exchange says why</li>
<li><a
href="https://github.com/colinhacks/zod/commit/529f78dd9431b07f49647fee3aa0819b3f8b66b7"><code>529f78d</code></a>
4.6.6</li>
<li><a
href="https://github.com/colinhacks/zod/commit/a0ebf970f2d752e5f58527cd2942ff48be4f46bd"><code>a0ebf97</code></a>
fix(v4): check array, set and map size bounds before parsing elements
(<a
href="https://redirect.github.com/colinhacks/zod/issues/6652">#6652</a>)</li>
<li><a
href="https://github.com/colinhacks/zod/commit/a2ba4a67986a3e6b2b131be30084a2f460d9ea67"><code>a2ba4a6</code></a>
fix(types): declare record output and input as members, not base type
argumen...</li>
<li><a
href="https://github.com/colinhacks/zod/commit/c42596bd1c87e941d7241d10e012c5c196adcc10"><code>c42596b</code></a>
release: hold the run on the release-approval environment until the
staged ve...</li>
<li><a
href="https://github.com/colinhacks/zod/commit/bacad518c41610d654658172d325874bf0ee6290"><code>bacad51</code></a>
docs: square the Code for Japan logo to match the other bronze
tiles</li>
<li><a
href="https://github.com/colinhacks/zod/commit/8fb2faf0584828a6a027d8279db36f6f52784462"><code>8fb2faf</code></a>
docs: crop the Code for Japan logo and list it first in bronze</li>
<li><a
href="https://github.com/colinhacks/zod/commit/b2ef983e0711ff6517d6020b021071e2e5653efa"><code>b2ef983</code></a>
docs: add Code for Japan to bronze sponsors</li>
<li><a
href="https://github.com/colinhacks/zod/commit/74304ce5b5796b5edb9a88ceb8e170b6ea3e82df"><code>74304ce</code></a>
release: stage npm versions instead of publishing them (<a
href="https://redirect.github.com/colinhacks/zod/issues/6622">#6622</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/colinhacks/zod/compare/v3.25.76...v4.6.7">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for zod since your current version.</p>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/linux-odyssey/linux-odyssey/network/alerts).

</details>
2026-11-03 20:37:50 +11:00
Justin Lin 045941a05c deps: upgrade zod 2026-11-03 20:36:34 +11:00
Justin Lin 676c12de77 Fix: add rate limit to session and disable guest account (#222)
- Disable guest accounts in frontend and backend
- Apply in memory rate limit to new session creation (3 times per
minute)
- Add restart: always to traefik in docker compose production file
2026-11-03 20:33:22 +11:00
Justin Lin b510d18758 feat: add rate limit to session creation 2026-11-03 20:28:04 +11:00
Justin Lin 9ffcd9fa90 fix: disable guest login 2026-11-03 20:18:15 +11:00
Justin Lin 3106ae00d0 feat: set traefik always restart 2026-11-03 20:10:04 +11:00
dependabot[bot] 9ce161e4f3 chore(deps): bump the npm_and_yarn group across 2 directories with 1 update
Bumps the npm_and_yarn group with 1 update in the /server directory: [zod](https://github.com/colinhacks/zod).
Bumps the npm_and_yarn group with 1 update in the /services/analytics directory: [zod](https://github.com/colinhacks/zod).


Updates `zod` from 3.25.76 to 4.6.7
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v3.25.76...v4.6.7)

Updates `zod` from 3.25.76 to 4.6.7
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v3.25.76...v4.6.7)

---
updated-dependencies:
- dependency-name: zod
  dependency-version: 4.6.7
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: zod
  dependency-version: 4.6.7
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 22:06:42 +00:00
34 changed files with 79 additions and 7600 deletions

No files matched your search

-1
View File
@@ -25,7 +25,6 @@ pids
*.seed *.seed
*.pid.lock *.pid.lock
data data
postgres-data
# Directory for instrumented libs generated by jscoverage/JSCover # Directory for instrumented libs generated by jscoverage/JSCover
lib-cov lib-cov
+1 -1
View File
@@ -30,7 +30,7 @@
"socket.io-client": "^4.7.1", "socket.io-client": "^4.7.1",
"validator": "^13.15.22", "validator": "^13.15.22",
"vue": "^3.3.4", "vue": "^3.3.4",
"vue-i18n": "11.3.0", "vue-i18n": "11.4.14",
"vue-router": "^4.2.4", "vue-router": "^4.2.4",
"vue-toastification": "^2.0.0-rc.5", "vue-toastification": "^2.0.0-rc.5",
"vue3-lottie": "^3.2.0", "vue3-lottie": "^3.2.0",
+12 -11
View File
@@ -87,15 +87,16 @@ const handleChange = () => {
} }
} }
const registerGuest = async () => { // Guest account currently disabled
try { // const registerGuest = async () => {
await api.post('/auth/register-guest') // try {
router.push({ name: 'map' }) // await api.post('/auth/register-guest')
} catch (err) { // router.push({ name: 'map' })
console.error(err) // } catch (err) {
toast.error('Something went wrong.') // console.error(err)
} // toast.error('Something went wrong.')
} // }
// }
</script> </script>
<template> <template>
<!-- login page --> <!-- login page -->
@@ -241,13 +242,13 @@ const registerGuest = async () => {
</RouterLink></span </RouterLink></span
> >
</p> </p>
<button <!-- <button
id="register-guest" id="register-guest"
class="rounded-lg py-2 mt-3 bg-bg-primary text-text-secondary w-full border-text-secondary border-2" class="rounded-lg py-2 mt-3 bg-bg-primary text-text-secondary w-full border-text-secondary border-2"
@click="registerGuest()" @click="registerGuest()"
> >
{{ t('authform.continue_as_guest') }} {{ t('authform.continue_as_guest') }}
</button> </button> -->
</form> </form>
</div> </div>
</template> </template>
+27 -1027
View File
File diff suppressed because it is too large. Load diff
+1
View File
@@ -1,6 +1,7 @@
services: services:
traefik: traefik:
image: traefik:v3.6.9 image: traefik:v3.6.9
restart: always
ports: ports:
- 8000:8000 - 8000:8000
- 8080:8080 - 8080:8080
-22
View File
@@ -1,4 +1,3 @@
version: '3'
services: services:
traefik: traefik:
image: traefik:v3.6.9 image: traefik:v3.6.9
@@ -29,26 +28,6 @@ services:
volumes: volumes:
- ./data:/data/db - ./data:/data/db
postgres:
image: postgres:18.6
networks:
- internal
ports:
- 5432:5432
volumes:
- ./postgres-data:/var/lib/postgresql
environment:
POSTGRES_USER: commander
POSTGRES_DB: odyssey-dev
POSTGRES_PASSWORD: mysecretpassword
adminer:
image: adminer
networks:
- internal
ports:
- 18080:8080
base: base:
image: linuxodyssey/quest-base image: linuxodyssey/quest-base
networks: networks:
@@ -56,7 +35,6 @@ services:
build: build:
context: . context: .
dockerfile: ./quests/Dockerfile dockerfile: ./quests/Dockerfile
command: exit 0
extra_hosts: extra_hosts:
- 'host.docker.internal:host-gateway' - 'host.docker.internal:host-gateway'
labels: labels:
+1 -1
View File
@@ -8,6 +8,6 @@
".": "./index.ts" ".": "./index.ts"
}, },
"dependencies": { "dependencies": {
"zod": "^3.24.2" "zod": "^4.6.7"
} }
} }
-5
View File
@@ -1,5 +0,0 @@
# Copy this file to `.env` and replace the placeholder with your real connection string.
# Connection string for PostgreSQL.
# Requires PostgreSQL >= 15.
DATABASE_URL="postgresql://user:password@localhost:5432/mydb"
-6
View File
@@ -1,6 +0,0 @@
src/prisma/contract.json linguist-generated
src/prisma/contract.d.ts linguist-generated
src/prisma/ops.json linguist-generated
src/prisma/migration.json linguist-generated
migrations/snapshots/**/contract.json linguist-generated
migrations/snapshots/**/contract.d.ts linguist-generated
-36
View File
@@ -1,36 +0,0 @@
# dependencies (bun install)
node_modules
# output
out
dist
*.tgz
# code coverage
coverage
*.lcov
# logs
logs
_.log
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# caches
.eslintcache
.cache
*.tsbuildinfo
# IntelliJ based IDEs
.idea
# Finder (MacOS) folder config
.DS_Store
node_modules/
dist/
-15
View File
@@ -1,15 +0,0 @@
# @odyssey/database
To install dependencies:
```bash
bun install
```
To run:
```bash
bun run index.ts
```
This project was created using `bun init` in bun v1.3.14. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
@@ -1,7 +0,0 @@
{
"from": null,
"to": "6fecb00f7a40489f8c1a9a4dad506cc6d4b95789cbb32519634132f006530c04",
"providedInvariants": [],
"createdAt": "2026-10-11T08:21:15.726Z",
"migrationHash": "99b3a1febb9f739bf258c99ebe1dbfe692b86792e8eb511fc7eef8e985e8e8cd"
}
@@ -1,268 +0,0 @@
#!/usr/bin/env -S node
import type { Contract as End } from '../../snapshots/6fecb00f7a40489f8c1a9a4dad506cc6d4b95789cbb32519634132f006530c04/contract';
import endContract from '../../snapshots/6fecb00f7a40489f8c1a9a4dad506cc6d4b95789cbb32519634132f006530c04/contract.json' with { type: 'json' };
import {
Migration,
MigrationCLI,
checkExpression,
col,
primaryKey,
} from '@prisma/orm-postgres/migration';
export default class M extends Migration<never, End> {
override readonly endContractJson = endContract;
override get operations() {
return [
this.createSchema({ schema: 'public' }),
this.createTable({
schema: 'public',
table: 'Command',
columns: [
col('command', 'text', { notNull: true, codecRef: { codecId: 'pg/text@1' } }),
col('error', 'text', { codecRef: { codecId: 'pg/text@1' } }),
col('id', 'BIGSERIAL', { notNull: true, codecRef: { codecId: 'pg/int8@1' } }),
col('output', 'text', { codecRef: { codecId: 'pg/text@1' } }),
col('pwd', 'text', { notNull: true, codecRef: { codecId: 'pg/text@1' } }),
col('sessionId', 'int8', { notNull: true, codecRef: { codecId: 'pg/int8@1' } }),
col('stage', 'text', { codecRef: { codecId: 'pg/text@1' } }),
col('timestamp', 'timestamptz', {
notNull: true,
codecRef: { codecId: 'pg/timestamptz-temporal@1' },
}),
],
constraints: [primaryKey(['id'])],
}),
this.createTable({
schema: 'public',
table: 'LoginAttempt',
columns: [
col('id', 'BIGSERIAL', { notNull: true, codecRef: { codecId: 'pg/int8@1' } }),
col('ip', 'text', { notNull: true, codecRef: { codecId: 'pg/text@1' } }),
col('loginMethod', 'text', { notNull: true, codecRef: { codecId: 'pg/text@1' } }),
col('message', 'text', { codecRef: { codecId: 'pg/text@1' } }),
col('success', 'bool', { notNull: true, codecRef: { codecId: 'pg/bool@1' } }),
col('time', 'timestamptz', {
notNull: true,
codecRef: { codecId: 'pg/timestamptz-temporal@1' },
}),
col('userAgent', 'text', { notNull: true, codecRef: { codecId: 'pg/text@1' } }),
col('userId', 'int8', { codecRef: { codecId: 'pg/int8@1' } }),
col('username', 'text', { notNull: true, codecRef: { codecId: 'pg/text@1' } }),
],
constraints: [
primaryKey(['id']),
checkExpression(
'LoginAttempt_loginMethod_check_f8485f11',
`"loginMethod" IN ('password', 'google', 'github')`,
),
],
}),
this.createTable({
schema: 'public',
table: 'Session',
columns: [
col('completedEvents', 'text[]', {
notNull: true,
codecRef: { codecId: 'pg/text@1', many: true },
}),
col('containerId', 'text', { codecRef: { codecId: 'pg/text@1' } }),
col('containerName', 'text', { codecRef: { codecId: 'pg/text@1' } }),
col('createdAt', 'timestamptz', {
notNull: true,
codecRef: { codecId: 'pg/timestamptz-temporal@1' },
}),
col('finishedAt', 'timestamptz', { codecRef: { codecId: 'pg/timestamptz-temporal@1' } }),
col('graph', 'jsonb', { notNull: true, codecRef: { codecId: 'pg/jsonb@1' } }),
col('id', 'BIGSERIAL', { notNull: true, codecRef: { codecId: 'pg/int8@1' } }),
col('lastActivityAt', 'timestamptz', {
notNull: true,
codecRef: { codecId: 'pg/timestamptz-temporal@1' },
}),
col('locale', 'text', { notNull: true, codecRef: { codecId: 'pg/text@1' } }),
col('quest', 'text', { notNull: true, codecRef: { codecId: 'pg/text@1' } }),
col('status', 'text', { notNull: true, codecRef: { codecId: 'pg/text@1' } }),
col('userId', 'int8', { notNull: true, codecRef: { codecId: 'pg/int8@1' } }),
],
constraints: [
primaryKey(['id']),
checkExpression(
'Session_completedEvents_elem_not_null_b55d23f4',
'array_position("completedEvents", NULL) IS NULL',
),
checkExpression(
'Session_status_check_8fa5bbc6',
`"status" IN ('active', 'finished', 'inactive')`,
),
],
}),
this.createTable({
schema: 'public',
table: 'User',
columns: [
col('createdAt', 'timestamptz', {
notNull: true,
codecRef: { codecId: 'pg/timestamptz-temporal@1' },
}),
col('email', 'text', { notNull: true, codecRef: { codecId: 'pg/text@1' } }),
col('id', 'BIGSERIAL', { notNull: true, codecRef: { codecId: 'pg/int8@1' } }),
col('isGuest', 'bool', { notNull: true, codecRef: { codecId: 'pg/bool@1' } }),
col('name', 'text', { codecRef: { codecId: 'pg/text@1' } }),
col('updatedAt', 'timestamptz', {
notNull: true,
codecRef: { codecId: 'pg/timestamptz-temporal@1' },
}),
col('username', 'text', { notNull: true, codecRef: { codecId: 'pg/text@1' } }),
],
constraints: [primaryKey(['id'])],
}),
this.createTable({
schema: 'public',
table: 'UserLoginMethod',
columns: [
col('createdAt', 'timestamptz', {
notNull: true,
codecRef: { codecId: 'pg/timestamptz-temporal@1' },
}),
col('displayName', 'text', { codecRef: { codecId: 'pg/text@1' } }),
col('hashedPassword', 'text', { codecRef: { codecId: 'pg/text@1' } }),
col('oauthId', 'text', { codecRef: { codecId: 'pg/text@1' } }),
col('type', 'text', { notNull: true, codecRef: { codecId: 'pg/text@1' } }),
col('updatedAt', 'timestamptz', {
notNull: true,
codecRef: { codecId: 'pg/timestamptz-temporal@1' },
}),
col('userId', 'int8', { notNull: true, codecRef: { codecId: 'pg/int8@1' } }),
],
constraints: [
primaryKey(['userId', 'type']),
checkExpression(
'UserLoginMethod_type_check_6814314f',
`"type" IN ('password', 'google', 'github')`,
),
],
}),
this.createTable({
schema: 'public',
table: 'UserProgress',
columns: [
col('finishedAt', 'timestamptz', { codecRef: { codecId: 'pg/timestamptz-temporal@1' } }),
col('quest', 'text', { notNull: true, codecRef: { codecId: 'pg/text@1' } }),
col('startedAt', 'timestamptz', {
notNull: true,
codecRef: { codecId: 'pg/timestamptz-temporal@1' },
}),
col('userId', 'int8', { notNull: true, codecRef: { codecId: 'pg/int8@1' } }),
],
constraints: [primaryKey(['userId', 'quest'])],
}),
this.addUnique({
schema: 'public',
table: 'User',
constraint: 'User_email_key',
columns: ['email'],
}),
this.addUnique({
schema: 'public',
table: 'User',
constraint: 'User_username_key',
columns: ['username'],
}),
this.addUnique({
schema: 'public',
table: 'UserLoginMethod',
constraint: 'UserLoginMethod_type_oauthId_key',
columns: ['type', 'oauthId'],
}),
this.createIndex({
schema: 'public',
table: 'Command',
index: 'Command_sessionId_idx_29f415d4',
columns: ['sessionId'],
}),
this.createIndex({
schema: 'public',
table: 'LoginAttempt',
index: 'LoginAttempt_userId_idx_a489d58a',
columns: ['userId'],
}),
this.createIndex({
schema: 'public',
table: 'Session',
index: 'Session_userId_idx_a489d58a',
columns: ['userId'],
}),
this.createIndex({
schema: 'public',
table: 'Session',
index: 'Session_userId_quest_idx_b13626bf',
columns: ['userId', 'quest'],
}),
this.createIndex({
schema: 'public',
table: 'Session',
index: 'Session_userId_status_idx_e4a128ba',
columns: ['userId', 'status'],
}),
this.createIndex({
schema: 'public',
table: 'UserLoginMethod',
index: 'UserLoginMethod_userId_idx_a489d58a',
columns: ['userId'],
}),
this.createIndex({
schema: 'public',
table: 'UserProgress',
index: 'UserProgress_userId_idx_a489d58a',
columns: ['userId'],
}),
this.addForeignKey({
schema: 'public',
table: 'Command',
foreignKey: {
name: 'Command_sessionId_fkey',
columns: ['sessionId'],
references: { schema: 'public', table: 'Session', columns: ['id'] },
},
}),
this.addForeignKey({
schema: 'public',
table: 'LoginAttempt',
foreignKey: {
name: 'LoginAttempt_userId_fkey',
columns: ['userId'],
references: { schema: 'public', table: 'User', columns: ['id'] },
},
}),
this.addForeignKey({
schema: 'public',
table: 'Session',
foreignKey: {
name: 'Session_userId_fkey',
columns: ['userId'],
references: { schema: 'public', table: 'User', columns: ['id'] },
},
}),
this.addForeignKey({
schema: 'public',
table: 'UserLoginMethod',
foreignKey: {
name: 'UserLoginMethod_userId_fkey',
columns: ['userId'],
references: { schema: 'public', table: 'User', columns: ['id'] },
},
}),
this.addForeignKey({
schema: 'public',
table: 'UserProgress',
foreignKey: {
name: 'UserProgress_userId_fkey',
columns: ['userId'],
references: { schema: 'public', table: 'User', columns: ['id'] },
},
}),
];
}
}
MigrationCLI.run(import.meta.url, M);
@@ -1,862 +0,0 @@
[
{
"id": "schema.public",
"label": "Create schema \"public\"",
"operationClass": "additive",
"target": {
"id": "postgres"
},
"precheck": [],
"execute": [
{
"description": "Create schema \"public\"",
"sql": "CREATE SCHEMA IF NOT EXISTS \"public\"",
"params": []
}
],
"postcheck": []
},
{
"id": "table.Command",
"label": "Create table \"Command\"",
"summary": "Creates table \"Command\"",
"operationClass": "additive",
"target": {
"id": "postgres",
"details": {
"schema": "public",
"objectType": "table",
"name": "Command"
}
},
"precheck": [
{
"description": "ensure table \"Command\" does not exist",
"sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"",
"params": [
"\"public\".\"Command\""
]
}
],
"execute": [
{
"description": "create table \"Command\"",
"sql": "CREATE TABLE \"public\".\"Command\" (\n \"command\" text NOT NULL,\n \"error\" text,\n \"id\" BIGSERIAL NOT NULL,\n \"output\" text,\n \"pwd\" text NOT NULL,\n \"sessionId\" int8 NOT NULL,\n \"stage\" text,\n \"timestamp\" timestamptz NOT NULL,\n PRIMARY KEY (\"id\")\n)",
"params": []
}
],
"postcheck": [
{
"description": "verify table \"Command\" exists",
"sql": "SELECT (to_regclass($1)) IS NOT NULL AS \"result\"",
"params": [
"\"public\".\"Command\""
]
}
]
},
{
"id": "table.LoginAttempt",
"label": "Create table \"LoginAttempt\"",
"summary": "Creates table \"LoginAttempt\"",
"operationClass": "additive",
"target": {
"id": "postgres",
"details": {
"schema": "public",
"objectType": "table",
"name": "LoginAttempt"
}
},
"precheck": [
{
"description": "ensure table \"LoginAttempt\" does not exist",
"sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"",
"params": [
"\"public\".\"LoginAttempt\""
]
}
],
"execute": [
{
"description": "create table \"LoginAttempt\"",
"sql": "CREATE TABLE \"public\".\"LoginAttempt\" (\n \"id\" BIGSERIAL NOT NULL,\n \"ip\" text NOT NULL,\n \"loginMethod\" text NOT NULL,\n \"message\" text,\n \"success\" bool NOT NULL,\n \"time\" timestamptz NOT NULL,\n \"userAgent\" text NOT NULL,\n \"userId\" int8,\n \"username\" text NOT NULL,\n PRIMARY KEY (\"id\"),\n CONSTRAINT \"LoginAttempt_loginMethod_check_f8485f11\" CHECK (\"loginMethod\" IN ('password', 'google', 'github'))\n)",
"params": []
}
],
"postcheck": [
{
"description": "verify table \"LoginAttempt\" exists",
"sql": "SELECT (to_regclass($1)) IS NOT NULL AS \"result\"",
"params": [
"\"public\".\"LoginAttempt\""
]
}
]
},
{
"id": "table.Session",
"label": "Create table \"Session\"",
"summary": "Creates table \"Session\"",
"operationClass": "additive",
"target": {
"id": "postgres",
"details": {
"schema": "public",
"objectType": "table",
"name": "Session"
}
},
"precheck": [
{
"description": "ensure table \"Session\" does not exist",
"sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"",
"params": [
"\"public\".\"Session\""
]
}
],
"execute": [
{
"description": "create table \"Session\"",
"sql": "CREATE TABLE \"public\".\"Session\" (\n \"completedEvents\" text[] NOT NULL,\n \"containerId\" text,\n \"containerName\" text,\n \"createdAt\" timestamptz NOT NULL,\n \"finishedAt\" timestamptz,\n \"graph\" jsonb NOT NULL,\n \"id\" BIGSERIAL NOT NULL,\n \"lastActivityAt\" timestamptz NOT NULL,\n \"locale\" text NOT NULL,\n \"quest\" text NOT NULL,\n \"status\" text NOT NULL,\n \"userId\" int8 NOT NULL,\n PRIMARY KEY (\"id\"),\n CONSTRAINT \"Session_completedEvents_elem_not_null_b55d23f4\" CHECK (array_position(\"completedEvents\", NULL) IS NULL),\n CONSTRAINT \"Session_status_check_8fa5bbc6\" CHECK (\"status\" IN ('active', 'finished', 'inactive'))\n)",
"params": []
}
],
"postcheck": [
{
"description": "verify table \"Session\" exists",
"sql": "SELECT (to_regclass($1)) IS NOT NULL AS \"result\"",
"params": [
"\"public\".\"Session\""
]
}
]
},
{
"id": "table.User",
"label": "Create table \"User\"",
"summary": "Creates table \"User\"",
"operationClass": "additive",
"target": {
"id": "postgres",
"details": {
"schema": "public",
"objectType": "table",
"name": "User"
}
},
"precheck": [
{
"description": "ensure table \"User\" does not exist",
"sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"",
"params": [
"\"public\".\"User\""
]
}
],
"execute": [
{
"description": "create table \"User\"",
"sql": "CREATE TABLE \"public\".\"User\" (\n \"createdAt\" timestamptz NOT NULL,\n \"email\" text NOT NULL,\n \"id\" BIGSERIAL NOT NULL,\n \"isGuest\" bool NOT NULL,\n \"name\" text,\n \"updatedAt\" timestamptz NOT NULL,\n \"username\" text NOT NULL,\n PRIMARY KEY (\"id\")\n)",
"params": []
}
],
"postcheck": [
{
"description": "verify table \"User\" exists",
"sql": "SELECT (to_regclass($1)) IS NOT NULL AS \"result\"",
"params": [
"\"public\".\"User\""
]
}
]
},
{
"id": "table.UserLoginMethod",
"label": "Create table \"UserLoginMethod\"",
"summary": "Creates table \"UserLoginMethod\"",
"operationClass": "additive",
"target": {
"id": "postgres",
"details": {
"schema": "public",
"objectType": "table",
"name": "UserLoginMethod"
}
},
"precheck": [
{
"description": "ensure table \"UserLoginMethod\" does not exist",
"sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"",
"params": [
"\"public\".\"UserLoginMethod\""
]
}
],
"execute": [
{
"description": "create table \"UserLoginMethod\"",
"sql": "CREATE TABLE \"public\".\"UserLoginMethod\" (\n \"createdAt\" timestamptz NOT NULL,\n \"displayName\" text,\n \"hashedPassword\" text,\n \"oauthId\" text,\n \"type\" text NOT NULL,\n \"updatedAt\" timestamptz NOT NULL,\n \"userId\" int8 NOT NULL,\n PRIMARY KEY (\"userId\", \"type\"),\n CONSTRAINT \"UserLoginMethod_type_check_6814314f\" CHECK (\"type\" IN ('password', 'google', 'github'))\n)",
"params": []
}
],
"postcheck": [
{
"description": "verify table \"UserLoginMethod\" exists",
"sql": "SELECT (to_regclass($1)) IS NOT NULL AS \"result\"",
"params": [
"\"public\".\"UserLoginMethod\""
]
}
]
},
{
"id": "table.UserProgress",
"label": "Create table \"UserProgress\"",
"summary": "Creates table \"UserProgress\"",
"operationClass": "additive",
"target": {
"id": "postgres",
"details": {
"schema": "public",
"objectType": "table",
"name": "UserProgress"
}
},
"precheck": [
{
"description": "ensure table \"UserProgress\" does not exist",
"sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"",
"params": [
"\"public\".\"UserProgress\""
]
}
],
"execute": [
{
"description": "create table \"UserProgress\"",
"sql": "CREATE TABLE \"public\".\"UserProgress\" (\n \"finishedAt\" timestamptz,\n \"quest\" text NOT NULL,\n \"startedAt\" timestamptz NOT NULL,\n \"userId\" int8 NOT NULL,\n PRIMARY KEY (\"userId\", \"quest\")\n)",
"params": []
}
],
"postcheck": [
{
"description": "verify table \"UserProgress\" exists",
"sql": "SELECT (to_regclass($1)) IS NOT NULL AS \"result\"",
"params": [
"\"public\".\"UserProgress\""
]
}
]
},
{
"id": "unique.User.User_email_key",
"label": "Add unique constraint on \"User\" (email)",
"operationClass": "additive",
"target": {
"id": "postgres",
"details": {
"schema": "public",
"objectType": "unique",
"name": "User_email_key",
"table": "User"
}
},
"precheck": [
{
"description": "ensure constraint \"User_email_key\" does not exist",
"sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"",
"params": [
"User_email_key",
"public",
"\"public\".\"User\""
]
}
],
"execute": [
{
"description": "add unique constraint \"User_email_key\"",
"sql": "ALTER TABLE \"public\".\"User\" ADD CONSTRAINT \"User_email_key\" UNIQUE (\"email\")"
}
],
"postcheck": [
{
"description": "verify constraint \"User_email_key\" exists",
"sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"",
"params": [
"User_email_key",
"public",
"\"public\".\"User\""
]
}
]
},
{
"id": "unique.User.User_username_key",
"label": "Add unique constraint on \"User\" (username)",
"operationClass": "additive",
"target": {
"id": "postgres",
"details": {
"schema": "public",
"objectType": "unique",
"name": "User_username_key",
"table": "User"
}
},
"precheck": [
{
"description": "ensure constraint \"User_username_key\" does not exist",
"sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"",
"params": [
"User_username_key",
"public",
"\"public\".\"User\""
]
}
],
"execute": [
{
"description": "add unique constraint \"User_username_key\"",
"sql": "ALTER TABLE \"public\".\"User\" ADD CONSTRAINT \"User_username_key\" UNIQUE (\"username\")"
}
],
"postcheck": [
{
"description": "verify constraint \"User_username_key\" exists",
"sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"",
"params": [
"User_username_key",
"public",
"\"public\".\"User\""
]
}
]
},
{
"id": "unique.UserLoginMethod.UserLoginMethod_type_oauthId_key",
"label": "Add unique constraint on \"UserLoginMethod\" (type, oauthId)",
"operationClass": "additive",
"target": {
"id": "postgres",
"details": {
"schema": "public",
"objectType": "unique",
"name": "UserLoginMethod_type_oauthId_key",
"table": "UserLoginMethod"
}
},
"precheck": [
{
"description": "ensure constraint \"UserLoginMethod_type_oauthId_key\" does not exist",
"sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"",
"params": [
"UserLoginMethod_type_oauthId_key",
"public",
"\"public\".\"UserLoginMethod\""
]
}
],
"execute": [
{
"description": "add unique constraint \"UserLoginMethod_type_oauthId_key\"",
"sql": "ALTER TABLE \"public\".\"UserLoginMethod\" ADD CONSTRAINT \"UserLoginMethod_type_oauthId_key\" UNIQUE (\"type\", \"oauthId\")"
}
],
"postcheck": [
{
"description": "verify constraint \"UserLoginMethod_type_oauthId_key\" exists",
"sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"",
"params": [
"UserLoginMethod_type_oauthId_key",
"public",
"\"public\".\"UserLoginMethod\""
]
}
]
},
{
"id": "index.Command.Command_sessionId_idx_29f415d4",
"label": "Create index \"Command_sessionId_idx_29f415d4\" on \"Command\"",
"operationClass": "additive",
"target": {
"id": "postgres",
"details": {
"schema": "public",
"objectType": "index",
"name": "Command_sessionId_idx_29f415d4",
"table": "Command"
}
},
"precheck": [
{
"description": "ensure index \"Command_sessionId_idx_29f415d4\" does not exist",
"sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"",
"params": [
"\"public\".\"Command_sessionId_idx_29f415d4\""
]
}
],
"execute": [
{
"description": "create index \"Command_sessionId_idx_29f415d4\"",
"sql": "CREATE INDEX \"Command_sessionId_idx_29f415d4\" ON \"public\".\"Command\" (\"sessionId\")",
"params": []
}
],
"postcheck": [
{
"description": "verify index \"Command_sessionId_idx_29f415d4\" exists",
"sql": "SELECT (to_regclass($1)) IS NOT NULL AS \"result\"",
"params": [
"\"public\".\"Command_sessionId_idx_29f415d4\""
]
}
]
},
{
"id": "index.LoginAttempt.LoginAttempt_userId_idx_a489d58a",
"label": "Create index \"LoginAttempt_userId_idx_a489d58a\" on \"LoginAttempt\"",
"operationClass": "additive",
"target": {
"id": "postgres",
"details": {
"schema": "public",
"objectType": "index",
"name": "LoginAttempt_userId_idx_a489d58a",
"table": "LoginAttempt"
}
},
"precheck": [
{
"description": "ensure index \"LoginAttempt_userId_idx_a489d58a\" does not exist",
"sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"",
"params": [
"\"public\".\"LoginAttempt_userId_idx_a489d58a\""
]
}
],
"execute": [
{
"description": "create index \"LoginAttempt_userId_idx_a489d58a\"",
"sql": "CREATE INDEX \"LoginAttempt_userId_idx_a489d58a\" ON \"public\".\"LoginAttempt\" (\"userId\")",
"params": []
}
],
"postcheck": [
{
"description": "verify index \"LoginAttempt_userId_idx_a489d58a\" exists",
"sql": "SELECT (to_regclass($1)) IS NOT NULL AS \"result\"",
"params": [
"\"public\".\"LoginAttempt_userId_idx_a489d58a\""
]
}
]
},
{
"id": "index.Session.Session_userId_idx_a489d58a",
"label": "Create index \"Session_userId_idx_a489d58a\" on \"Session\"",
"operationClass": "additive",
"target": {
"id": "postgres",
"details": {
"schema": "public",
"objectType": "index",
"name": "Session_userId_idx_a489d58a",
"table": "Session"
}
},
"precheck": [
{
"description": "ensure index \"Session_userId_idx_a489d58a\" does not exist",
"sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"",
"params": [
"\"public\".\"Session_userId_idx_a489d58a\""
]
}
],
"execute": [
{
"description": "create index \"Session_userId_idx_a489d58a\"",
"sql": "CREATE INDEX \"Session_userId_idx_a489d58a\" ON \"public\".\"Session\" (\"userId\")",
"params": []
}
],
"postcheck": [
{
"description": "verify index \"Session_userId_idx_a489d58a\" exists",
"sql": "SELECT (to_regclass($1)) IS NOT NULL AS \"result\"",
"params": [
"\"public\".\"Session_userId_idx_a489d58a\""
]
}
]
},
{
"id": "index.Session.Session_userId_quest_idx_b13626bf",
"label": "Create index \"Session_userId_quest_idx_b13626bf\" on \"Session\"",
"operationClass": "additive",
"target": {
"id": "postgres",
"details": {
"schema": "public",
"objectType": "index",
"name": "Session_userId_quest_idx_b13626bf",
"table": "Session"
}
},
"precheck": [
{
"description": "ensure index \"Session_userId_quest_idx_b13626bf\" does not exist",
"sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"",
"params": [
"\"public\".\"Session_userId_quest_idx_b13626bf\""
]
}
],
"execute": [
{
"description": "create index \"Session_userId_quest_idx_b13626bf\"",
"sql": "CREATE INDEX \"Session_userId_quest_idx_b13626bf\" ON \"public\".\"Session\" (\"userId\", \"quest\")",
"params": []
}
],
"postcheck": [
{
"description": "verify index \"Session_userId_quest_idx_b13626bf\" exists",
"sql": "SELECT (to_regclass($1)) IS NOT NULL AS \"result\"",
"params": [
"\"public\".\"Session_userId_quest_idx_b13626bf\""
]
}
]
},
{
"id": "index.Session.Session_userId_status_idx_e4a128ba",
"label": "Create index \"Session_userId_status_idx_e4a128ba\" on \"Session\"",
"operationClass": "additive",
"target": {
"id": "postgres",
"details": {
"schema": "public",
"objectType": "index",
"name": "Session_userId_status_idx_e4a128ba",
"table": "Session"
}
},
"precheck": [
{
"description": "ensure index \"Session_userId_status_idx_e4a128ba\" does not exist",
"sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"",
"params": [
"\"public\".\"Session_userId_status_idx_e4a128ba\""
]
}
],
"execute": [
{
"description": "create index \"Session_userId_status_idx_e4a128ba\"",
"sql": "CREATE INDEX \"Session_userId_status_idx_e4a128ba\" ON \"public\".\"Session\" (\"userId\", \"status\")",
"params": []
}
],
"postcheck": [
{
"description": "verify index \"Session_userId_status_idx_e4a128ba\" exists",
"sql": "SELECT (to_regclass($1)) IS NOT NULL AS \"result\"",
"params": [
"\"public\".\"Session_userId_status_idx_e4a128ba\""
]
}
]
},
{
"id": "index.UserLoginMethod.UserLoginMethod_userId_idx_a489d58a",
"label": "Create index \"UserLoginMethod_userId_idx_a489d58a\" on \"UserLoginMethod\"",
"operationClass": "additive",
"target": {
"id": "postgres",
"details": {
"schema": "public",
"objectType": "index",
"name": "UserLoginMethod_userId_idx_a489d58a",
"table": "UserLoginMethod"
}
},
"precheck": [
{
"description": "ensure index \"UserLoginMethod_userId_idx_a489d58a\" does not exist",
"sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"",
"params": [
"\"public\".\"UserLoginMethod_userId_idx_a489d58a\""
]
}
],
"execute": [
{
"description": "create index \"UserLoginMethod_userId_idx_a489d58a\"",
"sql": "CREATE INDEX \"UserLoginMethod_userId_idx_a489d58a\" ON \"public\".\"UserLoginMethod\" (\"userId\")",
"params": []
}
],
"postcheck": [
{
"description": "verify index \"UserLoginMethod_userId_idx_a489d58a\" exists",
"sql": "SELECT (to_regclass($1)) IS NOT NULL AS \"result\"",
"params": [
"\"public\".\"UserLoginMethod_userId_idx_a489d58a\""
]
}
]
},
{
"id": "index.UserProgress.UserProgress_userId_idx_a489d58a",
"label": "Create index \"UserProgress_userId_idx_a489d58a\" on \"UserProgress\"",
"operationClass": "additive",
"target": {
"id": "postgres",
"details": {
"schema": "public",
"objectType": "index",
"name": "UserProgress_userId_idx_a489d58a",
"table": "UserProgress"
}
},
"precheck": [
{
"description": "ensure index \"UserProgress_userId_idx_a489d58a\" does not exist",
"sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"",
"params": [
"\"public\".\"UserProgress_userId_idx_a489d58a\""
]
}
],
"execute": [
{
"description": "create index \"UserProgress_userId_idx_a489d58a\"",
"sql": "CREATE INDEX \"UserProgress_userId_idx_a489d58a\" ON \"public\".\"UserProgress\" (\"userId\")",
"params": []
}
],
"postcheck": [
{
"description": "verify index \"UserProgress_userId_idx_a489d58a\" exists",
"sql": "SELECT (to_regclass($1)) IS NOT NULL AS \"result\"",
"params": [
"\"public\".\"UserProgress_userId_idx_a489d58a\""
]
}
]
},
{
"id": "foreignKey.Command.Command_sessionId_fkey",
"label": "Add foreign key \"Command_sessionId_fkey\" on \"Command\"",
"operationClass": "additive",
"target": {
"id": "postgres",
"details": {
"schema": "public",
"objectType": "foreignKey",
"name": "Command_sessionId_fkey",
"table": "Command"
}
},
"precheck": [
{
"description": "ensure FK \"Command_sessionId_fkey\" does not exist",
"sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"",
"params": [
"Command_sessionId_fkey",
"public",
"\"public\".\"Command\""
]
}
],
"execute": [
{
"description": "add FK \"Command_sessionId_fkey\"",
"sql": "ALTER TABLE \"public\".\"Command\"\nADD CONSTRAINT \"Command_sessionId_fkey\"\nFOREIGN KEY (\"sessionId\")\nREFERENCES \"public\".\"Session\" (\"id\")"
}
],
"postcheck": [
{
"description": "verify FK \"Command_sessionId_fkey\" exists",
"sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"",
"params": [
"Command_sessionId_fkey",
"public",
"\"public\".\"Command\""
]
}
]
},
{
"id": "foreignKey.LoginAttempt.LoginAttempt_userId_fkey",
"label": "Add foreign key \"LoginAttempt_userId_fkey\" on \"LoginAttempt\"",
"operationClass": "additive",
"target": {
"id": "postgres",
"details": {
"schema": "public",
"objectType": "foreignKey",
"name": "LoginAttempt_userId_fkey",
"table": "LoginAttempt"
}
},
"precheck": [
{
"description": "ensure FK \"LoginAttempt_userId_fkey\" does not exist",
"sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"",
"params": [
"LoginAttempt_userId_fkey",
"public",
"\"public\".\"LoginAttempt\""
]
}
],
"execute": [
{
"description": "add FK \"LoginAttempt_userId_fkey\"",
"sql": "ALTER TABLE \"public\".\"LoginAttempt\"\nADD CONSTRAINT \"LoginAttempt_userId_fkey\"\nFOREIGN KEY (\"userId\")\nREFERENCES \"public\".\"User\" (\"id\")"
}
],
"postcheck": [
{
"description": "verify FK \"LoginAttempt_userId_fkey\" exists",
"sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"",
"params": [
"LoginAttempt_userId_fkey",
"public",
"\"public\".\"LoginAttempt\""
]
}
]
},
{
"id": "foreignKey.Session.Session_userId_fkey",
"label": "Add foreign key \"Session_userId_fkey\" on \"Session\"",
"operationClass": "additive",
"target": {
"id": "postgres",
"details": {
"schema": "public",
"objectType": "foreignKey",
"name": "Session_userId_fkey",
"table": "Session"
}
},
"precheck": [
{
"description": "ensure FK \"Session_userId_fkey\" does not exist",
"sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"",
"params": [
"Session_userId_fkey",
"public",
"\"public\".\"Session\""
]
}
],
"execute": [
{
"description": "add FK \"Session_userId_fkey\"",
"sql": "ALTER TABLE \"public\".\"Session\"\nADD CONSTRAINT \"Session_userId_fkey\"\nFOREIGN KEY (\"userId\")\nREFERENCES \"public\".\"User\" (\"id\")"
}
],
"postcheck": [
{
"description": "verify FK \"Session_userId_fkey\" exists",
"sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"",
"params": [
"Session_userId_fkey",
"public",
"\"public\".\"Session\""
]
}
]
},
{
"id": "foreignKey.UserLoginMethod.UserLoginMethod_userId_fkey",
"label": "Add foreign key \"UserLoginMethod_userId_fkey\" on \"UserLoginMethod\"",
"operationClass": "additive",
"target": {
"id": "postgres",
"details": {
"schema": "public",
"objectType": "foreignKey",
"name": "UserLoginMethod_userId_fkey",
"table": "UserLoginMethod"
}
},
"precheck": [
{
"description": "ensure FK \"UserLoginMethod_userId_fkey\" does not exist",
"sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"",
"params": [
"UserLoginMethod_userId_fkey",
"public",
"\"public\".\"UserLoginMethod\""
]
}
],
"execute": [
{
"description": "add FK \"UserLoginMethod_userId_fkey\"",
"sql": "ALTER TABLE \"public\".\"UserLoginMethod\"\nADD CONSTRAINT \"UserLoginMethod_userId_fkey\"\nFOREIGN KEY (\"userId\")\nREFERENCES \"public\".\"User\" (\"id\")"
}
],
"postcheck": [
{
"description": "verify FK \"UserLoginMethod_userId_fkey\" exists",
"sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"",
"params": [
"UserLoginMethod_userId_fkey",
"public",
"\"public\".\"UserLoginMethod\""
]
}
]
},
{
"id": "foreignKey.UserProgress.UserProgress_userId_fkey",
"label": "Add foreign key \"UserProgress_userId_fkey\" on \"UserProgress\"",
"operationClass": "additive",
"target": {
"id": "postgres",
"details": {
"schema": "public",
"objectType": "foreignKey",
"name": "UserProgress_userId_fkey",
"table": "UserProgress"
}
},
"precheck": [
{
"description": "ensure FK \"UserProgress_userId_fkey\" does not exist",
"sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"",
"params": [
"UserProgress_userId_fkey",
"public",
"\"public\".\"UserProgress\""
]
}
],
"execute": [
{
"description": "add FK \"UserProgress_userId_fkey\"",
"sql": "ALTER TABLE \"public\".\"UserProgress\"\nADD CONSTRAINT \"UserProgress_userId_fkey\"\nFOREIGN KEY (\"userId\")\nREFERENCES \"public\".\"User\" (\"id\")"
}
],
"postcheck": [
{
"description": "verify FK \"UserProgress_userId_fkey\" exists",
"sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"",
"params": [
"UserProgress_userId_fkey",
"public",
"\"public\".\"UserProgress\""
]
}
]
}
]
@@ -1 +0,0 @@
{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"LoginMethod":{"codecId":"pg/text@1","members":[{"name":"Password","value":"password"},{"name":"Google","value":"google"},{"name":"GitHub","value":"github"}]},"SessionStatus":{"codecId":"pg/text@1","members":[{"name":"Active","value":"active"},{"name":"Finished","value":"finished"},{"name":"Inactive","value":"inactive"}]}},"models":{"Command":{"fields":{"command":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"error":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/int8@1","kind":"scalar"}},"output":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"pwd":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"sessionId":{"nullable":false,"type":{"codecId":"pg/int8@1","kind":"scalar"}},"stage":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"timestamp":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}}},"relations":{"session":{"cardinality":"N:1","nullable":false,"on":{"localFields":["sessionId"],"targetFields":["id"]},"to":{"model":"Session","namespace":"public"}}},"storage":{"fields":{"command":{"column":"command"},"error":{"column":"error"},"id":{"column":"id"},"output":{"column":"output"},"pwd":{"column":"pwd"},"sessionId":{"column":"sessionId"},"stage":{"column":"stage"},"timestamp":{"column":"timestamp"}},"namespaceId":"public","table":"Command"}},"LoginAttempt":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/int8@1","kind":"scalar"}},"ip":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"loginMethod":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"LoginMethod","namespaceId":"public","plane":"domain"}},"message":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"success":{"nullable":false,"type":{"codecId":"pg/bool@1","kind":"scalar"}},"time":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"userAgent":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":true,"type":{"codecId":"pg/int8@1","kind":"scalar"}},"username":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":true,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"ip":{"column":"ip"},"loginMethod":{"column":"loginMethod"},"message":{"column":"message"},"success":{"column":"success"},"time":{"column":"time"},"userAgent":{"column":"userAgent"},"userId":{"column":"userId"},"username":{"column":"username"}},"namespaceId":"public","table":"LoginAttempt"}},"Session":{"fields":{"completedEvents":{"many":{"elementNullable":false},"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"containerId":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"containerName":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"finishedAt":{"nullable":true,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"graph":{"nullable":false,"type":{"codecId":"pg/jsonb@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/int8@1","kind":"scalar"}},"lastActivityAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"locale":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"quest":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"SessionStatus","namespaceId":"public","plane":"domain"}},"userId":{"nullable":false,"type":{"codecId":"pg/int8@1","kind":"scalar"}}},"relations":{"commands":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["sessionId"]},"to":{"model":"Command","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"completedEvents":{"column":"completedEvents"},"containerId":{"column":"containerId"},"containerName":{"column":"containerName"},"createdAt":{Line truncated
-23
View File
@@ -1,23 +0,0 @@
{
"name": "@odyssey/database",
"module": "src/index.ts",
"type": "module",
"devDependencies": {
"@odyssey/models": "workspace:*",
"@prisma/cli-engine": "0.7.0",
"@types/bun": "latest",
"@types/node": "^26.6.5",
"prisma": "^8.0.0-rc.22"
},
"peerDependencies": {
"typescript": "^5"
},
"scripts": {
"contract:emit": "prisma contract emit"
},
"dependencies": {
"@prisma/orm-postgres": "^8.0.0-rc.17",
"dotenv": "^18.0.7",
"temporal-polyfill": "^1.0.5"
}
}
-106
View File
@@ -1,106 +0,0 @@
# Welcome to Prisma ORM!
Prisma ORM lets you query your database in simple, easy-to-read TypeScript. Define what your data looks like, and Prisma ORM gives you a fully typed client — with autocomplete for every table, column, and relation.
This project is set up for PostgreSQL. Prisma ORM also supports other databases.
## Requirements
- **PostgreSQL 15 or newer.** Older servers are not supported. Run `SELECT version()` against your server to verify.
- The CLI never connects to your database without explicit consent. Pass `--probe-db` to `bun prisma orm init` if you want `init` to verify the server version itself.
## Your data contract
Your data contract is the heart of your application. It lives at [`src/prisma/contract.prisma`](src/prisma/contract.prisma) and describes your models:
```prisma
model User {
id Int @id @default(autoincrement())
email String @unique
username String?
name String?
}
```
Every model you define in your contract can be queried from your app. Your editor will autocomplete the query methods and show you what type each model field is:
```typescript
import { db } from './src/prisma/db';
const user = await db.orm.public.User
.where({ email: 'alice@example.com' })
.first();
// Your editor will show the type of user as
// { id: number; email: string; username: string | null; name: string | null; createdAt: Date; posts: Post[] } | null
```
Your contract has two companion files in the same directory:
- **`contract.json`** — this tells your application what models exist, just like `package-lock.json` tells your package manager what dependencies your project has
- **`contract.d.ts`** — this powers autocomplete and type checking in your editor
Commit both files to git. When you change your contract, run `bun prisma contract emit` to update them.
If you use a framework like Next.js or Vite, the Prisma ORM plugin will do this for you automatically.
## Configuration
[`prisma.config.ts`](prisma.config.ts) tells the CLI where your contract lives and how to connect to your database. It loads environment variables from `.env` automatically:
```typescript
import 'dotenv/config';
import { definePrismaConfig } from '@prisma/cli-engine';
import { defineConfig as ormConfig } from '@prisma/orm-postgres/config';
export default definePrismaConfig({
orm: ormConfig({
contract: './src/prisma/contract.prisma',
db: {
connection: process.env['DATABASE_URL']!,
},
}),
});
```
Notice the `DATABASE_URL` above? It's defined in your [`.env`](./.env) file:
```env
DATABASE_URL="postgresql://user:password@localhost:5432/mydb"
```
You can customize how your environment variables are loaded by changing or removing the `import 'dotenv/config'` line.
## Quick reference
### Commands
```bash
bun prisma contract emit # Update contract.json and contract.d.ts
bun prisma db init # Create tables in the database
bun prisma migration status # Show migration status
```
### Files
| File | Purpose |
|---|---|
| [`src/prisma/contract.prisma`](src/prisma/contract.prisma) | Your data contract — define your models here |
| [`prisma.config.ts`](prisma.config.ts) | CLI configuration |
| [`src/prisma/db.ts`](src/prisma/db.ts) | Database client — `import { db } from './src/prisma/db'` |
| `src/prisma/contract.json` | Compiled contract (generated) |
| `src/prisma/contract.d.ts` | Contract types (generated) |
### Workflow
1. Edit [`src/prisma/contract.prisma`](src/prisma/contract.prisma) to add or change models.
2. Run `bun prisma contract emit` to regenerate the contract.
3. Query your models — your IDE will autocomplete everything.
## Monorepo notes (pnpm workspaces)
If this project lives inside a pnpm workspace, a few things are worth knowing:
- **Catalogs.** When the workspace's `pnpm-workspace.yaml` defines a `catalogs` entry for `prisma` or `@prisma/orm-postgres`, pnpm uses the catalog version everywhere — `init` does too. If you wanted the published `latest` instead, update or remove the catalog entry, then re-run `pnpm install`.
- **`pnpm dlx`.** `pnpm dlx prisma@latest orm init …` works in any directory. Inside a workspace, pnpm still resolves dependencies through the workspace's catalog/overrides rather than the registry; expect the installed Prisma ORM packages to reflect the workspace's catalog rather than `latest`.
- **`pnpm` → `npm` fallback.** If `pnpm` ever fails to install Prisma ORM with a `workspace:*` or `catalog:` resolution error (a leak in a published artefact), `init` falls back to `npm install` and surfaces a warning. Once the offending package republishes a clean version you can switch back with `pnpm install`.
-12
View File
@@ -1,12 +0,0 @@
import 'dotenv/config';
import { definePrismaConfig } from '@prisma/cli-engine';
import { defineConfig as ormConfig } from '@prisma/orm-postgres/config';
export default definePrismaConfig({
orm: ormConfig({
contract: "./src/prisma/contract.prisma",
db: {
connection: process.env['DATABASE_URL']!,
},
}),
});
@@ -1,403 +0,0 @@
/**
* One-shot migration of the legacy Mongo data into Postgres.
*
* MONGO_URL=... DATABASE_URL=... bun run scripts/migrate-from-mongo.ts [--dry-run] [--force]
*
* - Stop the server before running; this is a snapshot copy, not a sync.
* - Everything runs in ONE Postgres transaction, so a failure leaves Postgres untouched
* and the script can simply be re-run.
* - Refuses to run if any target table is non-empty, unless --force is given
* (--force does not delete anything; it only skips the check).
* - New ids are assigned here (sequential), so relations can be rewired from ObjectIds.
* The id sequences are advanced afterwards.
* - Rows that cannot be migrated (orphans, unknown providers) are written to
* migration-skipped.json and counted in the summary.
*/
import 'dotenv/config'
import { writeFileSync } from 'node:fs'
import { Temporal } from 'temporal-polyfill'
import {
connectDB,
User,
UserProfile,
Session,
Command,
LoginAttempt,
} from '@odyssey/models'
import { db } from '../src/prisma/db'
const DRY_RUN = process.argv.includes('--dry-run')
const FORCE = process.argv.includes('--force')
const BATCH_SIZE = 1000
const LOGIN_METHODS = ['password', 'google', 'github'] as const
type LoginMethod = (typeof LOGIN_METHODS)[number]
const isLoginMethod = (v: string): v is LoginMethod =>
(LOGIN_METHODS as readonly string[]).includes(v)
type Tx = Parameters<Parameters<typeof db.transaction>[0]>[0]
const skipped: {
table: string
reason: string
id?: string
detail?: unknown
}[] = []
const stats: Record<
string,
{ source: number; inserted: number; skipped: number }
> = {}
function track(table: string) {
return (stats[table] ??= { source: 0, inserted: 0, skipped: 0 })
}
function skip(table: string, reason: string, id?: unknown, detail?: unknown) {
track(table).skipped++
skipped.push({
table,
reason,
id: id === undefined ? undefined : String(id),
detail,
})
}
const instant = (d: Date) => Temporal.Instant.fromEpochMilliseconds(d.getTime())
const instantOrNull = (d?: Date | null) => (d ? instant(d) : null)
// Postgres text cannot hold NUL bytes, which some captured terminal output contains.
let nulStripped = 0
function text(v: string): string
function text(v: string | null | undefined): string | null
function text(v: string | null | undefined) {
if (v == null) return null
if (!v.includes('\0')) return v
nulStripped++
return v.replaceAll('\0', '')
}
// Mongoose subdocuments carry _id; the Postgres graph column stores the bare tree.
interface GraphNode {
path: string
type: string
discovered: boolean
children?: GraphNode[]
}
function cleanGraph(node: any): GraphNode {
const out: GraphNode = {
path: node?.path ?? '/',
// legacy sessions used 'folder' for directories
type: node?.type === 'file' ? 'file' : 'directory',
discovered: node?.discovered ?? false,
}
if (node?.children?.length) out.children = node.children.map(cleanGraph)
return out
}
// A lean Mongoose Map comes back as a plain object, a hydrated one as a Map.
const entries = <T>(m: Map<string, T> | Record<string, T> | undefined) =>
m instanceof Map ? [...m.entries()] : Object.entries(m ?? {})
async function insert(tx: Tx, table: string, plan: unknown, count: number) {
if (DRY_RUN || count === 0) {
track(table).inserted += count
return
}
const { affectedRows } = await tx.execute(plan as never)
track(table).inserted += Number(affectedRows)
}
async function* batches<T>(cursor: AsyncIterable<T>, size: number) {
let buf: T[] = []
for await (const item of cursor) {
buf.push(item)
if (buf.length >= size) {
yield buf
buf = []
}
}
if (buf.length) yield buf
}
async function migrate(tx: Tx) {
const t = tx.sql.public
const userIds = new Map<string, bigint>()
const userIdsByUsername = new Map<string, bigint>()
const sessionIds = new Map<string, bigint>()
const seenEmails = new Set<string>()
const seenOAuth = new Set<string>()
let nextUserId = 1n
let nextSessionId = 1n
let nextCommandId = 1n
let nextAttemptId = 1n
// --- User + UserLoginMethod
for await (const users of batches(
User.find().sort({ _id: 1 }).lean().cursor(),
BATCH_SIZE
)) {
const userRows: any[] = []
const methodRows: any[] = []
for (const u of users) {
track('User').source++
const email = u.email.trim().toLowerCase()
if (seenEmails.has(email)) {
throw new Error(
`Duplicate email after lowercasing: ${email} (user ${u._id}). Resolve it in Mongo first.`
)
}
seenEmails.add(email)
const id = nextUserId++
userIds.set(String(u._id), id)
userIdsByUsername.set(u.username, id)
userRows.push({
id,
email,
username: u.username,
name: null,
createdAt: instant(u.createdAt),
updatedAt: instant(u.updatedAt),
isGuest: u.isGuest ?? false,
})
if (u.hashedPassword) {
methodRows.push({
userId: id,
type: 'password',
hashedPassword: u.hashedPassword,
oauthId: null,
displayName: null,
createdAt: instant(u.createdAt),
updatedAt: instant(u.updatedAt),
})
}
for (const [key, login] of entries(u.socialLogins)) {
const provider = (login.provider ?? key).toLowerCase()
if (!isLoginMethod(provider) || provider === 'password') {
skip('UserLoginMethod', 'unknown provider', u._id, { provider })
continue
}
const dedupe = `${provider}:${login.id}`
if (seenOAuth.has(dedupe)) {
skip('UserLoginMethod', 'duplicate (type, oauthId)', u._id, {
provider,
oauthId: login.id,
})
continue
}
seenOAuth.add(dedupe)
methodRows.push({
userId: id,
type: provider,
hashedPassword: null,
oauthId: login.id,
displayName: login.displayName ?? null,
createdAt: instant(u.createdAt),
updatedAt: instant(u.updatedAt),
})
}
}
await insert(tx, 'User', t.User.insert(userRows).build(), userRows.length)
track('UserLoginMethod').source += methodRows.length
await insert(
tx,
'UserLoginMethod',
t.UserLoginMethod.insert(methodRows).build(),
methodRows.length
)
}
// --- UserProgress (UserProfile.progress map, flattened)
for await (const profiles of batches(
UserProfile.find().lean().cursor(),
BATCH_SIZE
)) {
const rows: any[] = []
for (const p of profiles) {
const userId = userIds.get(String(p.user))
const items = entries(p.progress)
track('UserProgress').source += items.length
if (userId === undefined) {
for (const [quest] of items)
skip('UserProgress', 'user not found', p.user, { quest })
continue
}
for (const [key, prog] of items) {
rows.push({
userId,
quest: prog.quest ?? key,
startedAt: instant(prog.startedAt),
// `completed` is no longer stored: finishedAt IS NOT NULL means completed.
finishedAt: prog.completed
? instantOrNull(prog.finishedAt ?? (p as any).updatedAt)
: null,
})
}
}
await insert(
tx,
'UserProgress',
t.UserProgress.insert(rows).build(),
rows.length
)
}
// --- Session
for await (const sessions of batches(
Session.find().sort({ _id: 1 }).lean().cursor(),
BATCH_SIZE
)) {
const rows: any[] = []
for (const s of sessions) {
track('Session').source++
const userId = userIds.get(String(s.user))
if (userId === undefined) {
skip('Session', 'user not found', s._id, { user: String(s.user) })
continue
}
const id = nextSessionId++
sessionIds.set(String(s._id), id)
rows.push({
id,
userId,
quest: s.quest,
// sessions created before localization have no locale; they were zh-TW
locale: s.locale ?? 'zh-TW',
containerId: s.containerId ?? null,
containerName: s.containerName ?? null,
status: s.status,
createdAt: instant(s.createdAt),
lastActivityAt: instant(s.lastActivityAt ?? s.updatedAt),
finishedAt: instantOrNull(s.finishedAt),
completedEvents: s.completedEvents ?? [],
graph: cleanGraph(s.graph),
})
}
await insert(tx, 'Session', t.Session.insert(rows).build(), rows.length)
}
// --- Command (largest collection)
for await (const commands of batches(
Command.find().sort({ _id: 1 }).lean().cursor(),
BATCH_SIZE
)) {
const rows: any[] = []
for (const c of commands) {
track('Command').source++
const sessionId = sessionIds.get(String(c.session))
if (sessionId === undefined) {
skip('Command', 'session not found', c._id, {
session: String(c.session),
})
continue
}
rows.push({
id: nextCommandId++,
sessionId,
timestamp: instant(c.createdAt),
pwd: text(c.pwd),
command: text(c.command),
output: text(c.output),
error: text(c.error),
stage: text(c.stage),
})
}
await insert(tx, 'Command', t.Command.insert(rows).build(), rows.length)
}
// --- LoginAttempt (userId stays null when the username no longer exists)
for await (const attempts of batches(
LoginAttempt.find().sort({ _id: 1 }).lean().cursor(),
BATCH_SIZE
)) {
const rows = attempts.map((a) => {
track('LoginAttempt').source++
return {
id: nextAttemptId++,
username: a.username,
userId: userIdsByUsername.get(a.username) ?? null,
ip: a.ip,
loginMethod: 'password' as const, // legacy attempts were password-only
userAgent: a.userAgent,
time: instant(a.time ?? (a as any).createdAt),
success: a.success,
message: a.message ?? null,
}
})
await insert(
tx,
'LoginAttempt',
t.LoginAttempt.insert(rows).build(),
rows.length
)
}
// --- Advance the autoincrement sequences past the ids we assigned explicitly.
// Identifiers can't be bind parameters, so each statement is spelled out.
if (!DRY_RUN) {
const { raw } = db
const plans = [
raw.sql`SELECT setval(pg_get_serial_sequence('"User"', 'id'), COALESCE((SELECT MAX(id) FROM "User"), 0) + 1, false)`,
raw.sql`SELECT setval(pg_get_serial_sequence('"Session"', 'id'), COALESCE((SELECT MAX(id) FROM "Session"), 0) + 1, false)`,
raw.sql`SELECT setval(pg_get_serial_sequence('"Command"', 'id'), COALESCE((SELECT MAX(id) FROM "Command"), 0) + 1, false)`,
raw.sql`SELECT setval(pg_get_serial_sequence('"LoginAttempt"', 'id'), COALESCE((SELECT MAX(id) FROM "LoginAttempt"), 0) + 1, false)`,
]
for (const plan of plans)
await tx.execute(plan.affectedCount().build() as never)
}
}
async function assertTargetEmpty() {
const { orm } = db
const found = await Promise.all([
orm.public.User.first(),
orm.public.UserLoginMethod.first(),
orm.public.UserProgress.first(),
orm.public.Session.first(),
orm.public.Command.first(),
orm.public.LoginAttempt.first(),
])
if (found.some(Boolean)) {
throw new Error(
'Target tables are not empty. Truncate them or pass --force.'
)
}
}
async function main() {
const mongoUrl = process.env['MONGO_URL']
if (!mongoUrl) throw new Error('MONGO_URL is not set')
const mongo = await connectDB(mongoUrl)
console.log(
DRY_RUN ? 'DRY RUN: nothing will be written' : 'Migrating Mongo -> Postgres'
)
try {
if (!FORCE && !DRY_RUN) await assertTargetEmpty()
await db.transaction((tx) => migrate(tx as Tx))
} finally {
await mongo.close()
await db.close()
}
console.table(stats)
if (nulStripped)
console.log(`Stripped NUL bytes from ${nulStripped} command text fields`)
writeFileSync('migration-skipped.json', JSON.stringify(skipped, null, 2))
console.log(
`Skipped rows written to migration-skipped.json (${skipped.length})`
)
for (const [table, s] of Object.entries(stats)) {
if (!DRY_RUN && s.source !== s.inserted + s.skipped) {
throw new Error(
`Count mismatch for ${table}: ${s.source} != ${s.inserted} + ${s.skipped}`
)
}
}
}
main().catch((err) => {
console.error(err)
process.exit(1)
})
-3
View File
@@ -1,3 +0,0 @@
export { db } from './prisma/db'
console.log('Hello via Bun!')
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -1,102 +0,0 @@
// use prisma-8
model User {
id BigInt @id @default(autoincrement())
email String @unique
username String @unique
name String?
createdAt DateTime
updatedAt DateTime
isGuest Boolean
loginMethods UserLoginMethod[]
progress UserProgress[]
sessions Session[]
loginAttempts LoginAttempt[]
}
enum LoginMethod {
@@type("pg/text@1")
Password = "password"
Google = "google"
GitHub = "github"
}
// User login using one of the login method
model UserLoginMethod {
type LoginMethod
userId BigInt
createdAt DateTime
updatedAt DateTime
hashedPassword String?
oauthId String?
displayName String?
@@id([userId, type])
@@unique([type, oauthId])
user User @relation(fields: [userId], references: [id])
}
// Store what quest a user has completed
model UserProgress {
userId BigInt
quest String
startedAt DateTime
finishedAt DateTime?
user User @relation(fields: [userId], references: [id])
@@id([userId, quest])
}
enum SessionStatus {
@@type("pg/text@1")
Active = "active"
Finished = "finished"
Inactive = "inactive"
}
model Session {
id BigInt @id @default(autoincrement())
userId BigInt
quest String
locale String
containerId String?
containerName String?
status SessionStatus
createdAt DateTime
lastActivityAt DateTime
finishedAt DateTime?
completedEvents String[]
graph Jsonb
user User @relation(fields: [userId], references: [id])
commands Command[]
@@index([userId, quest])
@@index([userId, status])
}
model Command {
id BigInt @id @default(autoincrement())
sessionId BigInt
timestamp DateTime
pwd String
command String
output String?
error String?
stage String?
session Session @relation(fields: [sessionId], references: [id])
@@index([sessionId])
}
model LoginAttempt {
id BigInt @id @default(autoincrement())
username String
userId BigInt?
ip String
loginMethod LoginMethod
userAgent String
time DateTime
success Boolean
message String?
user User? @relation(fields: [userId], references: [id])
}
-9
View File
@@ -1,9 +0,0 @@
import 'dotenv/config'
import postgres from '@prisma/orm-postgres/runtime'
import type { Contract } from './contract.d'
import contractJson from './contract.json' with { type: 'json' }
export const db = postgres<Contract>({
contractJson,
url: process.env['DATABASE_URL']!,
})
-34
View File
@@ -1,34 +0,0 @@
{
"compilerOptions": {
// Environment setup & latest features
"lib": ["ESNext"],
"target": "ESNext",
"module": "preserve",
"moduleDetection": "force",
"jsx": "react-jsx",
"allowJs": true,
"types": [
"bun",
"node"
],
// Bundler mode
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,
// Best practices
"strict": true,
"skipLibCheck": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
// Some stricter flags (disabled by default)
"noUnusedLocals": false,
"noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false,
"resolveJsonModule": true
}
}
+1 -1
View File
@@ -9,7 +9,7 @@
}, },
"dependencies": { "dependencies": {
"@odyssey/file-graph": "workspace:*", "@odyssey/file-graph": "workspace:*",
"zod": "^3.24.2", "zod": "^4.6.7",
"zod-to-json-schema": "^3.24.5" "zod-to-json-schema": "^3.24.5"
} }
} }
+2 -2
View File
@@ -15,7 +15,6 @@
"@odyssey/game": "workspace:*", "@odyssey/game": "workspace:*",
"@odyssey/models": "workspace:*", "@odyssey/models": "workspace:*",
"@odyssey/utils": "workspace:*", "@odyssey/utils": "workspace:*",
"@odyssey/database": "workspace:*",
"@trpc/server": "^11.8.0", "@trpc/server": "^11.8.0",
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"commander": "^11.0.0", "commander": "^11.0.0",
@@ -39,12 +38,13 @@
"passport-google-oidc": "^0.1.0", "passport-google-oidc": "^0.1.0",
"passport-jwt": "^4.0.1", "passport-jwt": "^4.0.1",
"passport-local": "^1.0.0", "passport-local": "^1.0.0",
"rate-limiter-flexible": "^11.2.3",
"socket.io": "^4.7.1", "socket.io": "^4.7.1",
"swagger-ui-express": "^5.0.0", "swagger-ui-express": "^5.0.0",
"validator": "^13.15.22", "validator": "^13.15.22",
"winston": "^3.11.0", "winston": "^3.11.0",
"yaml": "^2.8.3", "yaml": "^2.8.3",
"zod": "^3.24.2" "zod": "^4.6.7"
}, },
"devDependencies": { "devDependencies": {
"@types/bcrypt": "^5.0.2", "@types/bcrypt": "^5.0.2",
+1 -1
View File
@@ -73,7 +73,7 @@ async function recordLogin(req: Request, success: boolean, message: string) {
router.post('/register', authenticateRateLimit, registerValidators, register) router.post('/register', authenticateRateLimit, registerValidators, register)
router.post('/register-guest', authenticateRateLimit, registerGuest) // router.post('/register-guest', authenticateRateLimit, registerGuest)
router.post('/logout', logout) router.post('/logout', logout)
-2
View File
@@ -6,7 +6,6 @@ import swaggerUI from 'swagger-ui-express'
import passport from 'passport' import passport from 'passport'
import * as trpcExpress from '@trpc/server/adapters/express' import * as trpcExpress from '@trpc/server/adapters/express'
import { connectDB } from '@odyssey/models' import { connectDB } from '@odyssey/models'
import { db } from '@odyssey/database'
import './auth/passport.js' import './auth/passport.js'
import socketServer from './api/socket.js' import socketServer from './api/socket.js'
@@ -23,7 +22,6 @@ import { appRouter } from './routers/index.js'
import { createContext } from './trpc.js' import { createContext } from './trpc.js'
async function main() { async function main() {
console.log(db)
if (!config.secret) { if (!config.secret) {
logger.error( logger.error(
'No SECRET_KEY found in .env! To set up a persistent key, please run `bun run setup`:' 'No SECRET_KEY found in .env! To set up a persistent key, please run `bun run setup`:'
+29 -7
View File
@@ -1,9 +1,12 @@
import rateLimit from 'express-rate-limit' import rateLimit from 'express-rate-limit'
import type { Options } from 'express-rate-limit' import type { Options } from 'express-rate-limit'
import type { Request, Response, NextFunction } from 'express' import type { Request, Response, NextFunction } from 'express'
import { RateLimiterMemory } from 'rate-limiter-flexible'
import { TRPCError } from '@trpc/server'
import { LoginAttempt } from '@odyssey/models' import { LoginAttempt } from '@odyssey/models'
import config from '../config.js' import config from '../config.js'
import logger from '../utils/logger.js' import logger from '../utils/logger.js'
import { middleware } from '../trpc.js'
function createRateLimiter(options: Partial<Options>) { function createRateLimiter(options: Partial<Options>) {
if (config.testing.enabled) { if (config.testing.enabled) {
@@ -22,13 +25,32 @@ export const authenticateRateLimit = createRateLimiter({
max: 5, max: 5,
}) })
export const sessionRateLimit = createRateLimiter({ export function trpcRateLimit({
windowMs: 10 * 1000, points,
max: 1, duration,
keyGenerator: (req: Request) => { }: {
return req.user ? (req.user as any)._id : req.ip || '' points: number
}, duration: number
}) }) {
const limiter = new RateLimiterMemory({ points, duration })
return middleware(async (opts) => {
if (config.testing.enabled) {
return opts.next()
}
const key = opts.ctx.user?.id ?? opts.ctx.ip ?? ''
try {
await limiter.consume(key)
} catch {
throw new TRPCError({
code: 'TOO_MANY_REQUESTS',
message: 'Too many requests. Please try again later.',
})
}
return opts.next()
})
}
export const sessionRateLimit = trpcRateLimit({ points: 3, duration: 60 })
export async function rateLimitByAccount( export async function rateLimitByAccount(
req: Request, req: Request,
+2
View File
@@ -8,6 +8,7 @@ import config from '../config.js'
import { protectedProcedure, router } from '../trpc.js' import { protectedProcedure, router } from '../trpc.js'
import { questManager } from '../models/quest.js' import { questManager } from '../models/quest.js'
import { genJWT } from '../utils/auth' import { genJWT } from '../utils/auth'
import { sessionRateLimit } from '../middleware/rateLimiter.js'
export type SessionDetail = Awaited<ReturnType<typeof sessionDetail>> export type SessionDetail = Awaited<ReturnType<typeof sessionDetail>>
@@ -47,6 +48,7 @@ async function sessionDetail(session: ISession) {
export const sessionRouter = router({ export const sessionRouter = router({
createSession: protectedProcedure createSession: protectedProcedure
.use(sessionRateLimit)
.input( .input(
z.object({ z.object({
questId: z.string(), questId: z.string(),
+1 -1
View File
@@ -18,7 +18,7 @@ const t = initTRPC.context<AppContext>().create()
* Export reusable router and procedure helpers * Export reusable router and procedure helpers
* that can be used throughout the router * that can be used throughout the router
*/ */
export const { router, procedure: publicProcedure } = t export const { router, procedure: publicProcedure, middleware } = t
export const protectedProcedure = publicProcedure.use(async (opts) => { export const protectedProcedure = publicProcedure.use(async (opts) => {
if (opts.ctx.isAuthenticated() && !('newUser' in opts.ctx.user)) { if (opts.ctx.isAuthenticated() && !('newUser' in opts.ctx.user)) {
+1 -1
View File
@@ -16,7 +16,7 @@
"express-basic-auth": "^1.2.1", "express-basic-auth": "^1.2.1",
"express-handlebars": "^7.1.2", "express-handlebars": "^7.1.2",
"mongoose": "^8.24.1", "mongoose": "^8.24.1",
"zod": "^3.24.2" "zod": "^4.6.7"
}, },
"devDependencies": { "devDependencies": {
"@types/express": "^4.17.21", "@types/express": "^4.17.21",