{"id":13719677,"url":"https://github.com/janhesters/french-house-stack","last_synced_at":"2025-04-12T18:13:18.438Z","repository":{"id":36981441,"uuid":"502452361","full_name":"janhesters/french-house-stack","owner":"janhesters","description":"The Remix Stack for SaaS apps (Web2) and Web3 \u0026 Web5 DApps with authentication with Magic, testing, linting, formatting, etc.","archived":false,"fork":false,"pushed_at":"2024-11-04T08:54:07.000Z","size":7886,"stargazers_count":63,"open_issues_count":10,"forks_count":10,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-12T18:13:13.725Z","etag":null,"topics":["remix-stack"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/janhesters.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-06-11T20:43:54.000Z","updated_at":"2025-03-27T13:06:45.000Z","dependencies_parsed_at":"2024-01-06T00:12:31.570Z","dependency_job_id":"b48079dd-9bba-4d59-913a-230e263b8165","html_url":"https://github.com/janhesters/french-house-stack","commit_stats":null,"previous_names":["ten-x-dev/french-house-stack","janhesters/french-house-stack"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/janhesters%2Ffrench-house-stack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/janhesters%2Ffrench-house-stack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/janhesters%2Ffrench-house-stack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/janhesters%2Ffrench-house-stack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/janhesters","download_url":"https://codeload.github.com/janhesters/french-house-stack/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248610341,"owners_count":21132919,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["remix-stack"],"created_at":"2024-08-03T01:00:54.088Z","updated_at":"2025-04-12T18:13:18.419Z","avatar_url":"https://github.com/janhesters.png","language":"TypeScript","funding_links":[],"categories":["Starter","Remix Starter","TypeScript"],"sub_categories":[],"readme":"# Remix French House Stack 🪩\n\n![The Remix French House Stack](./public/french-house-stack.png)\n\nThe Remix Stack for Web2, Web3 and\n[Web5](https://developer.tbd.website/blog/what-is-web5/) 💃🕺\n\nLearn more about [Remix Stacks](https://remix.run/stacks).\n\n```\nnpx create-remix --template ten-x-dev/french-house-stack\n```\n\n## What's in the Stack? 🤔\n\nThe French House Stack is a starter template for SaaS apps in general, but also\nfor [developing DApps by using Magic](https://magic.link/docs/home#blockchains).\nHowever, Magic is perfectly suited for a regular Web2 app, too.\n\n- Authentication with [Magic](https://magic.link/) and with\n  [cookie-based sessions](https://remix.run/docs/en/v1/api/remix#createcookiesessionstorage),\n  which enables you to both build Web2 and Web3 apps\n- [GitHub Actions](https://github.com/features/actions) for deploy on merge to\n  production and staging environments\n- Styling with [Tailwind](https://tailwindcss.com/).\n  - Includes [dark mode](https://tailwindcss.com/docs/dark-mode).\n- Components by [ShadcnUI](https://ui.shadcn.com/) (plus a handful of unique\n  custom components.)\n- End-to-end testing with [Playwright](https://playwright.dev)\n- Unit testing with [Vitest](https://vitest.dev) and\n  [Testing Library](https://testing-library.com)\n- [MSW](https://mswjs.io/) for mocking API requests in tests and during\n  development.\n- [SQLite](https://www.sqlite.org/index.html) database with\n  [Prisma](https://www.prisma.io/) as the ORM\n- Code formatting with [Prettier](https://prettier.io)\n- Linting with [ESLint](https://eslint.org)\n- Static Types with [TypeScript](https://typescriptlang.org)\n- Commit hooks with [Husky](https://github.com/typicode/husky) and\n  [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) with\n  [Commitizen](https://github.com/commitizen/cz-cli)\n\n### Versioning\n\nThis stack pinned all version of its dependencies in order to ensure that it\nalways works. You can use\n\n```\nnpx npm-check-updates -u\n```\n\nto check for updates and install the latest versions.\n\n## Development 🛠\n\n### Getting Started\n\n- Make sure you're using Node.js 20.10.0 or higher. You can run:\n\n  ```sh\n  node -v\n  ```\n\n  to check which version you're on.\n\n  If you need to upgrade, we recommend using\n  [`nvm`](https://github.com/nvm-sh/nvm):\n\n  ```sh\n  nvm install --lts\n  nvm use --lts\n  nvm alias default 'lts/*'\n  ```\n\n- Install dependencies:\n\n  ```sh\n  npm i\n  ```\n\n- Make sure your system can run the Husky hooks (Mac \u0026 Linux):\n\n  ```sh\n  chmod a+x .husky/pre-commit\n  chmod a+x .husky/commit-msg\n  ```\n\n- Create a `.env` file and add these environment variables (see `.env.example`,\n  too):\n\n  - `MAGIC_PUBLISHABLE_KEY` and `MAGIC_SECRET_KEY` - You'll need to grab a\n    public key and a secret key for your project from your\n    [Magic dashboard](https://magic.link).\n  - `SESSION_SECRET` - The session secret can be any string that is at least 32\n    characters long.\n  - `DATABASE_URL` - The url under which the SQLite database will operate. You\n    may use the value from `.env.example` for this.\n\n- Add a `console.warn` to the `'magicEmailRegistration'` case in the\n  `registerHandler()` in\n  `app/features/user-authentication/user-authentication-actions.server.ts`:\n\n  ```ts\n  const { email, issuer: did } =\n    await magicAdmin.users.getMetadataByToken(didToken);\n  console.warn('did', did);\n  ```\n\n- Set up the database:\n\n  ```sh\n  npm run prisma:setup\n  ```\n\n- Start dev server:\n\n  ```sh\n  npm run dev\n  ```\n\n- Sign up for an account under `/register` by signing up with Magic.\n\n- Grab the `did` that you logged out in the previous step from the terminal in\n  which you ran `npm run dev` and add it to your `.env` file as `SEED_USER_DID`.\n\n- Remove the `console.warn` from the `registerHandler()`.\n\n- Now you can add the remaining values to your `.env` file, which are used by\n  the main seed script:\n\n  - `SEED_USER_DID` - The steps above outlined how to get this value. This value\n    is the user id of the user that will be seeded in the database. This value\n    is required for the `\"prisma:seed\"` script.\n  - `SEED_SEED_USER_EMAIL` - The email of the user that will be seeded in the\n    database. This value is required for the `\"prisma:seed\"` script.\n  - `SENTRY_DSN` - The DSN for your Sentry project. This value is optional.\n\n- Lastly, stop your `npm run dev` script and run\n\n  ```sh\n  npm run prisma:reset-dev\n  ```\n\n  , which wipes the database, seeds the database with lots of data and starts up\n  the dev server again.\n\nThis starts your app in development mode, rebuilding assets on file changes.\n\n### Dev with Mocks\n\nYou can run the app with MSW mocking requests to third party services by\nrunning:\n\n```sh\nnpm run dev-with-mocks\n```\n\nmocking requests from both the client and the server, or\n\n```sh\nnpm run dev-with-server-mocks\n```\n\nmocking only requests from the server.\n\nMake sure you run `npx msw init ./public` once before you run this command to\ninitialize the MSW service worker. It should create a file in\n`/public/mockServiceWorker.js` for you.\n\nThis is useful for developing offline or without hitting any API.\n\nBy default, MSW is used in the French House Stack to mock requests to Magic in\nyour E2E tests. Check out `playwright/e2e/user-authentication/logout.spec.ts`\nand `app/test/mocks/handlers/magic.ts` to see how to use MSW on the server.\n\n### Prisma helper scripts\n\n- `\"prisma:deploy\"` - Applies all pending migrations from the\n  `prisma/migrations` directory to the database. This is typically used in a\n  production environment where you want to apply version-controlled schema\n  changes.\n- `\"prisma:migrate\"` - Run via `npm run prisma:migrate -- \"my_migration_name\"`\n  to create a new migration file in the `prisma/migrations` directory based on\n  the changes made to your Prisma schema. This command also applies the\n  migration to your development database.\n- `\"prisma:push\"` - Applies changes from the Prisma schema to the database\n  without creating a migration file. This is useful for quick prototyping and\n  development.\n- `\"prisma:reset-dev\"` - Wipes the database, seeds it, and starts the\n  development server. This is a utility script that you can use in development\n  to get a clean start.\n- `\"prisma:reset-dev-with-mocks\"` - Wipes the database, seeds it, starts the\n  development server, and mocks all API requests. This is a utility script that\n  you can use in development to get clean starts and to develop offline or\n  without hitting any API.\n- `\"prisma:seed\"` - Seeds the database with predefined data. This is useful for\n  setting up a consistent state for testing or development.\n- `\"prisma:setup\"` - Generates Prisma Client, applies all pending migrations to\n  the database, and then pushes any remaining changes in the Prisma schema that\n  are not yet represented by a migration.\n- `\"prisma:studio\"` - Opens Prisma Studio, a visual interface for viewing and\n  editing data in your database.\n- `\"prisma:wipe\"` - Wipes the database, deleting all data but keeping the\n  schema. This is a utility script that you can use in development to get a\n  clean start.\n\n### Generating boilerplate\n\nThis repository uses [Plop](https://plopjs.com/documentation/#getting-started)\nto automate the generation of common boilerplate.\n\nRun `npm run gen` and then choose what you want to create, e.g.:\n\n```\n$ npm run gen\n\n\u003e gen\n\u003e plop\n\n? What do you want to generate? React component\n? For what feature do you want to generate the React component? user profile\n? What is the name of the React component? user name\n✔  ++ /app/features/user-profile/user-name-component.tsx\n✔  ++ /app/features/user-profile/user-name-component.test.tsx\n```\n\nOut of the box, there are three options:\n\n- React component with unit test\n- Database model utils\n- E2E tests for a route\n\n### Routing\n\nWe're using flat routes, a feature which will\n[ship natively with Remix, soon](https://github.com/remix-run/remix/issues/4483).\n\nYou can\n[check out this video for an in-depth explanation](https://portal.gitnation.org/contents/remix-flat-routes-an-evolution-in-routing).\n\n### How authentication works 🛡️\n\nThe French House Stack uses [Magic](https://magic.link/) for authentication with\na custom session cookie. You can find the implementation in\n`app/features/user-authentication`.\n\nMagic keeps track of the user's session in a cookie, but the FHS ignores Magic's\nsession and uses a session cookie instead. This is because Magic's sessions only\nlast 2 weeks, while the cookie lasts a year. Additionally, it makes E2E tests\neasier because you can fully control the auth flow.\n\nAfter a user successfully authenticates via Magic, you create a unique session\nin your system, tracked by `UserAuthSession`. This session ID is then securely\nstored in our session cookie, which we manage using\n[Remix's session utils](https://remix.run/docs/en/v1/utils/sessions#using-sessions).\nThe code for managing these sessions is located in\n`app/features/user-authentication/user-authentication-session.server.ts`.\n\nThe use of custom auth sessions enables you to to proactively invalidate\nsessions is necessary.\n\nIf the user is signing up, you also create a user profile for them using their\nemail, which you can grab from Magic during the sign up flow.\n\nWhen a user signs out, you delete the `UserAuthSession` and clear the session\ncookie.\n\n### ShadcnUI \u0026 Custom Components\n\nShadcnUI is configured in the \"New York\" setting, but it uses icons from\n`lucide-react`, so when generating a component you need to switch out that\nimport because the \"New York\" setting usually uses icons from\n`@radix-ui/react-icons`. `lucide-react` is used because it has a wider selection\nof icons.\n\nIn addition to the components from ShadcnUI, the French House Stack comes with\nsome custom components:\n\n- `app/components/disableable-link.tsx` - A link tag that can be disabled.\n- `app/components/drag-and-drop.tsx` - A drag and drop file input component.\n- `app/components/general-error-boundary.tsx` - An error boundary component\n  inspired by the\n  [Epic Stack](https://github.com/epicweb-dev/epic-stack/blob/main/app/components/error-boundary.tsx).\n- `app/components/sidebar.tsx` - A sidebar with header and burger menu\n  component. It is recommended to configure things like its title using Remix'\n  `useMatches` on a per route basis. (See\n  `app/features/organizations/organizations-sidebar-component.tsx` for an\n  example.)\n- `app/components/text.tsx` - Various text components that can be used to render\n  text, links and code blocks.\n\n### i18n\n\nThe French House Stack comes with localization support through\n[remix-i18next](https://github.com/sergiodxa/remix-i18next).\n\nThe namespaces live in `public/locales/`.\n\nRemember to add new namespaces to `app/features/localization/i18next.server.ts`\nto make them available in the server bundle and to `app/test/i18n.ts` to make\nsure they're available in the React component tests.\n\n### Monitoring\n\nThe French House Stack comes with error reporting using Sentry build in.\n\nTo use it, you need to set the `SENTRY_DSN` environment variable. You can get\nthis value from your Sentry project.\n\nIf you want to configure source maps, look up how to do that in the\n[Sentry docs](https://docs.sentry.io/platforms/javascript/guides/remix/sourcemaps/).\n\n### Toasts\n\nThe French House Stack includes utilities for toast notifications based on flash\nsessions.\n\n**Flash Data:** Temporary session values, ideal for transferring data to the\nnext request without persisting in the session.\n\n**Redirect with Toast:**\n\n- Utility: `redirectWithToast` (Path: `app/utils/toast.server.ts`)\n- Use for redirecting with toast notifications.\n- Example:\n  ```tsx\n  return redirectWithToast(`/organizations/${newOrganizations.slug}/home`, {\n    title: 'Organization created',\n    description: 'Your organization has been created.',\n  });\n  ```\n- Accepts extra arguments for `ResponseInit` to set headers.\n\n**Direct Toast Headers:**\n\n- Utility: `createToastHeaders` (Path: `app/utils/toast.server.ts`)\n- Use for non-redirect scenarios.\n- Example:\n  ```tsx\n  return json(\n    { success: true },\n    {\n      headers: await createToastHeaders({\n        description: 'Organization updated',\n        type: 'success',\n      }),\n    },\n  );\n  ```\n\n**Combining Multiple Headers:**\n\n- Utility: `combineHeaders` (Path: `app/utils/toast.server.tsx`)\n- Combine toast headers with additional headers.\n- Example:\n  ```tsx\n  return json(\n    { success: true },\n    {\n      headers: combineHeaders(\n        await createToastHeaders({ title: 'Profile updated' }),\n        { 'x-foo': 'bar' },\n      ),\n    },\n  );\n  ```\n\n## GitHub Actions\n\nWe use GitHub Actions for pull request checks. Any pull request triggers checks\nsuch as linting, type checks, unit tests and E2E tests.\n\nCheck out the\n[Remix team's official stacks](https://remix.run/docs/en/v1/pages/stacks) to\nlearn how to use GitHub Actions for continuous integration and deployment.\n\n## Testing 🧪\n\n### Playwright 🎭\n\n\u003e **Note:** make sure you've run `npm run dev` at least one time before you run\n\u003e the E2E tests!\n\nWe use Playwright for our End-to-End tests in this project. You'll find those in\nthe `playwright/` directory. As you make changes to your app, add to an existing\nfile or create a new file in the `playwright/e2e` directory to test your\nchanges.\n\n[Playwright natively features testing library selectors](https://playwright.dev/docs/release-notes#locators)\nfor selecting elements on the page semantically.\n\nTo run these tests in development, run `npm run test:e2e` which will start the\ndev server for the app as well as the Playwright client.\n\n\u003e **Note:** You might need to run `npx playwright install` to install the\n\u003e Playwright browsers before running your tests for the first time.\n\n#### Problems with ShadcnUI\n\nSome of the colors of ShadcnUI's components are lacking the necessary contrast.\n\nYou can deactivate those elements in checks like this:\n\n```ts\nconst accessibilityScanResults = await new AxeBuilder({ page })\n  .disableRules('color-contrast')\n  .analyze();\n\n// or\n\nconst accessibilityScanResults = await new AxeBuilder({ page })\n  .disableRules('color-contrast')\n  .analyze();\n```\n\nor pick a color scheme like \"purple\" that has good contrast.\n\n#### VSCode Extension\n\nIf you're using VSCode, you can install the\n[Playwright extension](https://github.com/microsoft/playwright-vscode) for a\nbetter developer experience.\n\n#### Utilities\n\nWe have a utility for testing authenticated features without having to go\nthrough the login flow:\n\n```ts\ntest('something that requires an authenticated user', async ({ page }) =\u003e {\n  await loginByCookie({ page });\n  // ... your tests ...\n});\n```\n\nCheck out the `playwright/utils.ts` file for other utility functions.\n\n#### Miscellaneous\n\nTo mark a test as todo in Playwright,\n[you have to use `.fixme()`](https://github.com/microsoft/playwright/issues/10918).\n\n```ts\ntest('something that should be done later', ({}, testInfo) =\u003e {\n  testInfo.fixme();\n});\n\ntest.fixme('something that should be done later', async ({ page }) =\u003e {\n  // ...\n});\n\ntest('something that should be done later', ({ page }) =\u003e {\n  test.fixme();\n  // ...\n});\n```\n\nThe version using `testInfo.fixme()` is the \"preferred\" way and can be picked up\nby the VSCode extension.\n\n### Vitest ⚡️\n\nFor lower level tests of utilities and individual components, we use `vitest`.\nWe have DOM-specific assertion helpers via\n[`@testing-library/jest-dom`](https://testing-library.com/jest-dom).\n\nBy default, Vitest runs tests in the\n[`\"happy-dom\"` environment](https://vitest.dev/config/#environment). However,\ntest files that have `.server` in the name will be run in the `\"node\"`\nenvironment.\n\n### Test Scripts\n\n- `npm run test` - Runs all Vitest tests.\n- `npm run test:unit` - Runs all unit tests with Vitest. Your unit tests should\n  test components or function in isolation, run fast, and are files that end\n  with `.test.ts` or `.test.tsx`.\n- `npm run test:integration` - Runs all integration tests Vitest. Your\n  integration tests should test multiple components or functions together, run\n  slower than unit tests (e.g. because they hit the database), and are files\n  that end with `.spec.ts` or `.spec.tsx`.\n- `npm run test:coverage` - Runs all Vitest tests and generates a coverage\n  report.\n- `npm run test:e2e` - Runs all E2E tests with Playwright.\n- `npm run test:e2e:ui` - Runs all E2E tests with Playwright in UI mode.\n\n### Type Checking\n\nThis project uses TypeScript. It's recommended to get TypeScript set up for your\neditor to get a really great in-editor experience with type checking and\nauto-complete. To run type checking across the whole project, run\n`npm run type-check`.\n\n### Linting\n\nThis project uses ESLint for linting. That is configured in `.eslintrc.cjs`.\n\n### Formatting\n\nWe use [Prettier](https://prettier.io/) for auto-formatting in this project.\nIt's recommended to install an editor plugin (like the\n[VSCode Prettier plugin](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode))\nto get auto-formatting on save. There's also a `npm run format` script you can\nrun to format all files in the project.\n\n## Next Steps 🚀\n\n### Remove the license\n\nRemember to remove the MIT license and add your own license if you're building a\ncommercial app.\n\n### Pick a Database\n\nThe French House Stack comes with a SQLite database out of the box. It uses\n[Prisma](https://www.prisma.io/) to abstract away the database layer, so you can\neasily switch it out for another database.\n\nIf you're looking for inspiration for a centralized database, check out the\n[Blues Stack](https://github.com/remix-run/blues-stack) for a enterprise grade\nPostgeSQL setup.\n\nIf you build a DApp, you might want to use the [IPFS](https://ipfs.io/) or\nsomething like [3Box](https://3boxlabs.com/).\n\n### Pick a Blockchain\n\nMagic is compatible with a\n[variety of blockchains](https://magic.link/docs/home#blockchains). The most\npopular for DApps is\n[Ethereum](https://magic.link/docs/advanced/blockchains/ethereum/javascript) and\nthe most popular chain in general is\n[Bitcoin](https://magic.link/docs/advanced/blockchains/bitcoin).\n\n### Deployment\n\nLearn how you can\n[deploy your Remix app here](https://remix.run/docs/en/v1/guides/deployment).\nFor examples of setups you can check out the\n[official Remix stacks](https://remix.run/docs/en/v1/pages/stacks).\n\n### Explore Magic\n\nThe French House Stack comes with a magic link setup via email preconfigured.\nHowever, Magic also offers social auth (e.g. for\n[Google](https://magic.link/docs/login-methods/social-logins/integration/social-providers/google)),\n[multi-factor auth](https://magic.link/docs/login-methods/mfa) and\n[WebAuthn](https://magic.link/docs/login-methods/webauthn).\n\n**Note:** the included cookie based authentication with\n`createCookieSessionStorage` is set up\n[as recommended by the Magic docs](https://magic.link/docs/introduction/faq#sessions-and-tokens).\nHowever, it doesn't work for Web3 functions. You'll need to\n[stay logged in with Magic](https://magic.link/docs/introduction/faq#how-long-does-a-user-remain-logged-in)\nto work with any chain.\n\n### To-Dos\n\nHere is a list of things this app could use:\n\n- feature flags\n- user feedback capturing and tracking (you can use Sentry for this).\n\n### [Buidl!](https://www.urbandictionary.com/define.php?term=%23BUIDL)\n\nNow go out there make some magic! 🧙‍♂️\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjanhesters%2Ffrench-house-stack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjanhesters%2Ffrench-house-stack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjanhesters%2Ffrench-house-stack/lists"}