Uh oh!
There was an error while loading. Please reload this page.
feat: add mailing and organizations - #28
Conversation
…dules
Reorganizes apps/dashboard/app into modules/{auth,dashboard,shared}, moving
components, composables, types and utils under the module that owns them, and
turns pages/ into a thin routing layer using (auth) and (dashboard) route
groups so the URLs stay /login and /.
Component tag names and composable call sites are unchanged: nuxt.config.ts
now registers the three module component roots explicitly, so each file keeps
the auto-import name it had under the default app/components scan.
The former package-root shared/ directory is folded into modules/shared/utils.
It existed for Nuxt's app-plus-server sharing convention, but the dashboard
owns no server, so the #shared alias is dropped from vitest, knip and tsconfig.Adds packages/mail, following supastarter's mail layout: Maizzle templates in emails/, a registry in src/util/templates.ts that maps a template id to its file and subject, and a single provider export point in src/provider/. Console delivery is the default so an unconfigured deployment logs rather than attempting real delivery, and the suite never opens a socket. Resend and Nodemailer SMTP sit behind the same MailProvider contract and are imported lazily, so only the configured transport has to be installed. sendEmail renders HTML and plaintext through Maizzle per send; the templates carry per-recipient tokens, so there is no reusable compiled artifact to cache. createMailer binds a provider and sender once for injection, which is what lets packages/auth consume mail structurally instead of depending on this package. resend is pinned to 6.17.1: 6.18.1 dropped the trusted-publisher provenance that earlier releases carry, and the installer refuses it.
…izations commit These two jobs predate this branch and are unrelated to mailing or organizations. The i18n job also checks packages/i18n/schema.json, packages/i18n/schemas and packages/i18n/locales, none of which exist in this repo — i18n lives under apps/dashboard/i18n/ — so the check would either fail or silently no-op against the wrong paths. Left for whoever intended to add CI coverage for the dashboard's i18n tooling to reintroduce correctly, in its own change.
…ons code Hoists regex literals used inside functions to module scope, replaces unsafe type assertions in mail/provider tests with a narrowing assertion function and optional chaining, rewrites mailProviderFromEnvironment's provider switch as if/else so every path is a recognized return, moves useOrganizations' messageFrom helper to module scope since it captures nothing from its parent, and adds a vi.fn() type parameter. Also declares packages/mail in knip.jsonc: maizzle.config.ts is discovered by Maizzle's render() through its own filesystem convention rather than imported, and @maizzle/tailwindcss is resolved by package name as a Maizzle plugin.
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
…nd delivery guarantees
- surface Better Auth API errors that resolve with { data, error } in the
organizations composable and the accept-invitation page instead of
treating them as success
- initialize the active organization on refresh so the switcher's
displayed selection matches state
- require STARTTLS on non-implicit-TLS SMTP connections so delivery
never falls back to plaintext
- withhold sendInvitationEmail from createAuth when the console mail
provider is configured, so the startup guard rejects an
organizations-enabled deployment with no delivering transport
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>…nt result types intact
The generic unwrap helper collapsed the client's { data, error } result
union to {}, failing typecheck and type-aware lint. Destructuring at
each call site and throwing via throwOnApiError preserves the inferred
data types while still routing resolved API errors through run's shared
error handling.
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>Following supastarter's package layout, moves everything i18n-related out of
apps/dashboard into a new shared @agent-zero/i18n package: the locale registry
(config/i18n.ts -> src/config.ts), the translation content (i18n/locales,
i18n/schemas), the maintenance scripts (compare-translations,
find-invalid-translations, generate-i18n-schema, i18n-status,
remove-unused-translations, and their shared i18n-locale-files.ts helpers),
and the Lunaria translation-status config. config/i18n-empty-placeholders.ts
stays in the dashboard — it's a Vite plugin, not shared content.
apps/dashboard now imports @agent-zero/i18n. @nuxtjs/i18n's langDir does not
support absolute paths in production, so nuxt.config.ts resolves the
package's installed locales/ directory and passes absolute file paths per
locale instead (the module's own documented pattern for module-provided
locale files), keeping the package's own i18nLocales export portable.
Also fixes the two i18n tooling scripts that scan the consuming app's source
(find-invalid-translations, remove-unused-translations): their VUE_FILES_GLOB
now points at ../../apps/dashboard/app since the scripts no longer run from
inside that app. Running the moved i18n:report surfaced a real pre-existing
issue in this branch's own InviteForm.vue — a dynamic i18n key
(`organizations.roles.${value}`) that vue-i18n-extract cannot statically
verify — replaced with three static t() calls.
Restores the CI jobs (knip, i18n) dropped earlier in this branch as broken:
the i18n job's schema-drift check now points at the real packages/i18n/{schemas,locales}
paths. Also wires i18n:report and i18n:schema as root aube run scripts
(turbo run passthrough) alongside the existing i18n:status — neither had ever
been wired at the root, so the CI job would have failed regardless of the
path fix.…running knip The standalone knip CI job runs 'aube run knip' straight after install, but apps/dashboard/nuxt.config.ts (and vitest.config.ts through defineVitestProject) now import @agent-zero/i18n and @agent-zero/auth, whose exports resolve to dist/. With no build, knip fails to load both configs and cascades into false unused-file/dependency/export findings; the lint job's knip run passes only because 'turbo run lint' builds dependencies first. Route the root knip script through a filtered turbo build of the two packages (turbo-cached, so it is a no-op replay where dists already exist). Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
nuxt dev and vite dev both load .env automatically; a plain `node --import tsx` entrypoint does not. apps/auth-server's dev script crashed on startup with "missing required environment variable: AUTH_DASHBOARD_ORIGIN" even though .env already had it set, because nothing ever read the file into the process. Node 24's --env-file-if-exists loads it without failing when a contributor hasn't created .env yet.
RedStar071
commented
Aug 13, 2026
RedStar071
commented
Aug 13, 2026
Uh oh!
There was an error while loading. Please reload this page.
removeMember() called refreshMembers() unconditionally, whose own run() call clears the shared error state at its start. A failed removal's error was silently wiped out the moment the follow-up refresh succeeded. Refresh only after a successful removal, matching the pattern already used in create().
Too many files changed for review (101 files, 100 file limit). Bypass the limit by tagging |
Uh oh!
There was an error while loading. Please reload this page.
Merges main's mailing, i18n, and organizations work (PR #28) into the source-control branch. No overlap with the provider-neutral adapters or apps/server routing; pnpm-lock.yaml is reconciled by hand (aube is unavailable in this environment) using the same packages/source-control rename applied to the previous merge. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MJQxEwqaaP4aMG4E7yeaky
Summary
Following supastarter's Nuxt guides (mailing overview, organizations: store data for organizations):
packages/mail: a Maizzle-backed mail package. Templates live inemails/(Vue SFC + Tailwind, compiled and inlined per send), registered by id insrc/util/templates.tswith their subject. Delivery goes through a singleMailProvidercontract selected insrc/provider/index.ts; console is the default (logs instead of sending), with Resend and Nodemailer SMTP behind it, both imported lazily so only the configured transport has to be installed.organizationplugin wired intopackages/auth, a Drizzle migration fororganization/member/invitationplussession.active_organization_id, and anapp/modules/organizationsdashboard module (switcher, member list, invite form, accept-invitation page).apps/dashboardrestructured into supastarter-style feature modules (modules/{auth,dashboard,shared,organizations}) before adding the above, so the new organizations module has somewhere to land that matches the rest of the app.Architecture boundary
packages/authdeclaresSendInvitationEmailstructurally and never imports@agent-zero/mail— one capability package must not import another.apps/auth-server(the composition root) builds a mailer withcreateMailer()and injects it intocreateAuth().createAuthrefuses to construct when organizations are enabled without a transport, so a misconfigured deployment fails at startup rather than accepting invitations nobody is ever told about.apps/dashboardstays frontend-only: the organizations module only calls the Better Auth client (organizationClient()), declares its ownOrganization/OrganizationMembertypes rather than importing them from@agent-zero/auth(which would pull the database adapter into the browser bundle), and every mutation refetches from the server rather than patching local state.Both features are off by default:
AUTH_ENABLE_ORGANIZATIONS=falseandMAIL_PROVIDER=console, so this PR doesn't change behavior for existing deployments.Also in this branch
refactor(dashboard): reorganizesapps/dashboard/appintomodules/{auth,dashboard,shared}(prerequisite restructuring, done first at the user's request and reviewed separately in-session).chore(ci): drops two CI jobs (knip,i18n) that were unrelated to this work and got swept into an earlier commit — thei18njob checkedpackages/i18n/*paths that don't exist in this repo (i18n lives underapps/dashboard/i18n/), so it would have failed or silently no-op'd against the wrong paths. Left for whoever intended that CI coverage to reintroduce it correctly, separately.Type of Change
Test Procedure
Ran the full required check set from
CONTRIBUTING.md:Also manually rendered
OrganizationInvitation.vuethrough@maizzle/framework'srender()outside the test suite to confirm Tailwind classes compile to inlinedstyle=attributes rather than leakingclass=into the output (mail clients routinely strip<style>/classes).resendis pinned to6.17.1inpackages/mail/package.json:aube addrefused6.18.1because it dropped the trusted-publisher provenance that6.17.1carries (ERR_AUBE_TRUST_DOWNGRADE) — not bypassed.Safety Impact
packages/authandapps/dashboardstill do not execute repository work or own persistence outsideapps/auth-server, per the architecture boundaries inAGENTS.md.packages/auth/drizzle/0001_peaceful_franklin_storm.sql) is additive only: new tables plus one nullable column onsession, safe against an existing database.Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is enabled.Confidence Score: 4/5
Member removal is not ready to merge until a rejected request reliably remains visible to the administrator.
One confirmed user-facing failure remains: the follow-up member refresh clears the error from a rejected removal request.
Files Needing Attention: apps/dashboard/app/modules/organizations/composables/useOrganizations.ts
What T-Rex did
Prompt To Fix All With AI
Reviews (3): Last reviewed commit: "Merge branch 'main' into feat/mailing-an..." | Re-trigger Greptile