├── .github └── workflows │ └── static.yml ├── .gitignore ├── .vscode └── launch.json ├── README.md ├── common ├── db-dialects │ ├── mysql-dialect-strategy.ts │ ├── postgresql-dialect-strategy.ts │ └── sqlite-dialect-strategy.ts ├── db-packages │ ├── better-sqlite3-package-strategy.ts │ ├── mysql2-package-strategy.ts │ └── pg-package-strategy.ts ├── lib │ ├── case-utils.ts │ ├── handlebars-helpers.ts │ ├── log.ts │ ├── pk-strategy.ts │ ├── strategy-factory.ts │ └── utils.ts ├── package-lock.json ├── package-pinned.json ├── package.json ├── processors │ ├── db-dialect-processor.ts │ └── drizzle-scaffold-processor.ts ├── scripts │ └── merge-package-json.ts ├── templates │ ├── db-dialect-processor │ │ ├── drizzle.config.ts.hbs │ │ ├── env.hbs │ │ ├── gitignore.hbs │ │ ├── src │ │ │ └── config │ │ │ │ ├── env.ts.hbs │ │ │ │ └── schema.ts.hbs │ │ └── tsconfig.json.hbs │ ├── db-packages │ │ ├── scripts │ │ │ ├── create-user.ts.hbs │ │ │ └── migrate.ts.hbs │ │ └── src │ │ │ └── config │ │ │ ├── custom-types.ts.mysql2.hbs │ │ │ ├── custom-types.ts.pg.hbs │ │ │ ├── db.ts.better-sqlite3.hbs │ │ │ ├── db.ts.mysql2.hbs │ │ │ └── db.ts.pg.hbs │ ├── scaffold-processor │ │ └── src │ │ │ └── schema │ │ │ ├── table.ts.mysql.hbs │ │ │ ├── table.ts.postgresql.hbs │ │ │ └── table.ts.sqlite.hbs │ └── test-scripts │ │ ├── .prettierrc.hbs │ │ └── load-fake-data.ts.hbs ├── test │ ├── case-utils.test.ts │ └── logs.test.ts └── types │ └── types.ts ├── docs ├── .vitepress │ ├── config.mts │ └── theme │ │ ├── custom.css │ │ └── index.js ├── about.md ├── api-examples.md ├── drizzle-admin │ ├── guide.md │ ├── index.md │ └── installation.md ├── drizzle-express │ ├── guide.md │ ├── index.md │ └── installation.md ├── drizzle-next │ ├── guide.md │ ├── index.md │ └── installation.md ├── drizzle-ui │ ├── guide.md │ ├── index.md │ └── installation.md ├── drizzle-util │ ├── guide.md │ ├── index.md │ └── installation.md ├── index.md ├── markdown-examples.md ├── package.json └── public │ └── favicon.ico ├── drizzle-admin ├── .gitignore ├── README.md ├── app │ ├── (admin) │ │ ├── _components │ │ │ ├── admin-layout.tsx │ │ │ └── users-components.tsx │ │ ├── _lib │ │ │ ├── drizzle-admin.config.ts │ │ │ └── users-table.config.ts │ │ ├── admin │ │ │ ├── [...segments] │ │ │ │ └── page.tsx │ │ │ ├── custom-page │ │ │ │ └── page.tsx │ │ │ └── page.tsx │ │ ├── api │ │ │ └── [[...segments]] │ │ │ │ └── route.ts │ │ └── layout.tsx │ ├── favicon.ico │ ├── globals.css │ ├── layout.tsx │ └── page.tsx ├── drizzle.config.ts ├── drizzle │ ├── 0000_unique_rictor.sql │ ├── 0001_gifted_sunset_bain.sql │ ├── 0002_silly_hannibal_king.sql │ ├── 0003_smart_killraven.sql │ ├── 0004_greedy_lethal_legion.sql │ ├── 0005_worried_magdalene.sql │ └── meta │ │ ├── 0000_snapshot.json │ │ ├── 0001_snapshot.json │ │ ├── 0002_snapshot.json │ │ ├── 0003_snapshot.json │ │ ├── 0004_snapshot.json │ │ ├── 0005_snapshot.json │ │ └── _journal.json ├── eslint.config.mjs ├── lib │ ├── config.ts │ ├── db.ts │ └── schema.ts ├── next.config.ts ├── package-lock.json ├── package.json ├── postcss.config.mjs ├── rollup.cjs.mjs ├── rollup.esm.mjs ├── schema │ ├── categories.ts │ ├── posts-tags.ts │ ├── posts.ts │ ├── tags.ts │ └── users.ts ├── scripts │ ├── create-password-hash.ts │ ├── create-user.ts │ ├── grant-admin.ts │ ├── migrate.ts │ └── seed.ts ├── src │ ├── components │ │ ├── drizzle-admin.tsx │ │ ├── drizzle-filter.tsx │ │ ├── index.ts │ │ ├── object-create-form.tsx │ │ ├── object-delete-form.tsx │ │ ├── object-table.tsx │ │ ├── object-update-form.tsx │ │ └── render-form-control.tsx │ ├── drizzle-ui │ │ ├── components │ │ │ └── ui │ │ │ │ ├── alert.tsx │ │ │ │ ├── app-layout.tsx │ │ │ │ ├── avatar.tsx │ │ │ │ ├── button.tsx │ │ │ │ ├── card.tsx │ │ │ │ ├── checkbox.tsx │ │ │ │ ├── code-block.tsx │ │ │ │ ├── dark-mode.tsx │ │ │ │ ├── dropdown-menu.tsx │ │ │ │ ├── form.tsx │ │ │ │ ├── input.tsx │ │ │ │ ├── label.tsx │ │ │ │ ├── pagination.tsx │ │ │ │ ├── rich-text-editor.tsx │ │ │ │ ├── search-input.tsx │ │ │ │ ├── select.tsx │ │ │ │ ├── sortable.tsx │ │ │ │ ├── table.tsx │ │ │ │ └── textarea.tsx │ │ ├── index.ts │ │ ├── lib │ │ │ └── utils.ts │ │ └── styles │ │ │ ├── rich-text-editor.css │ │ │ ├── styles.css │ │ │ └── typography.css │ ├── index.ts │ ├── lib │ │ ├── client-constants.ts │ │ ├── server-constants.ts │ │ ├── server-utils.tsx │ │ └── shared-utils.ts │ ├── pages │ │ ├── delete-page.tsx │ │ ├── edit-page.tsx │ │ ├── list-page.tsx │ │ ├── new-page.tsx │ │ ├── root-page.tsx │ │ └── view-page.tsx │ ├── routes │ │ ├── delete-route.ts │ │ ├── get-route.ts │ │ ├── index.ts │ │ ├── patch-route.ts │ │ ├── post-route.ts │ │ ├── put-route.ts │ │ └── route-utils.ts │ ├── styles │ │ └── styles.css │ └── types │ │ ├── index.ts │ │ └── types.ts ├── tsconfig.cjs.json ├── tsconfig.esm.json └── tsconfig.json ├── drizzle-express ├── README.md ├── commands │ ├── init-command.ts │ └── scaffold-command.ts ├── index.ts ├── lib │ └── utils.ts ├── package-lock.json ├── package.json ├── processors │ ├── express-init-processor.ts │ └── express-scaffold-processor.ts ├── scripts │ └── test.sh ├── templates │ └── express-templates │ │ ├── .gitignore.hbs │ │ ├── src │ │ ├── app.ts.hbs │ │ ├── config │ │ │ └── env.ts.hbs │ │ ├── controllers │ │ │ └── controller.ts.hbs │ │ ├── middlewares │ │ │ └── error-handler.middleware.ts.hbs │ │ ├── routes │ │ │ └── routes.ts.hbs │ │ ├── server.ts.hbs │ │ └── services │ │ │ └── service.ts.hbs │ │ └── tsconfig.json.hbs └── tsconfig.json ├── drizzle-next ├── README.md ├── commands │ ├── init-command.ts │ └── scaffold-command.ts ├── cypress.config.ts ├── cypress │ ├── e2e │ │ ├── test-demo.cy.ts │ │ ├── test-mysql.cy.ts │ │ ├── test-postgresql-auto-increment.cy.ts │ │ ├── test-postgresql.cy.ts │ │ └── test-sqlite.cy.ts │ ├── fixtures │ │ └── example.json │ └── support │ │ ├── commands.ts │ │ └── e2e.ts ├── index.ts ├── lib │ └── utils.ts ├── package-lock.json ├── package.json ├── processors │ ├── admin-processor.ts │ ├── auth-processor.ts │ ├── new-project-processor.ts │ └── next-scaffold-processor.ts ├── scripts │ ├── load-fake-data-postgresql.sql │ ├── test-mysql.sh │ ├── test-postgresql-auto-increment.sh │ ├── test-postgresql.sh │ └── test-sqlite.sh ├── templates │ ├── admin-processor │ │ ├── scripts │ │ │ ├── create-password-hash.ts.hbs │ │ │ └── grant-admin.ts.hbs │ │ └── src │ │ │ └── app │ │ │ ├── (admin) │ │ │ ├── _components │ │ │ │ ├── admin-layout.tsx.hbs │ │ │ │ └── users-components.tsx.hbs │ │ │ ├── _lib │ │ │ │ ├── drizzle-admin.config.ts.hbs │ │ │ │ └── users-table.config.ts.hbs │ │ │ ├── admin │ │ │ │ ├── [...segments] │ │ │ │ │ └── page.tsx.hbs │ │ │ │ ├── page.tsx.hbs │ │ │ │ └── settings │ │ │ │ │ └── page.tsx.hbs │ │ │ ├── api │ │ │ │ └── [...segments] │ │ │ │ │ └── route.ts.hbs │ │ │ └── layout.tsx.hbs │ │ │ └── (auth) │ │ │ ├── _actions │ │ │ └── admin-signin.action.ts.hbs │ │ │ ├── _components │ │ │ └── admin-signin-form.tsx.hbs │ │ │ └── admin-signin │ │ │ └── page.tsx.hbs │ ├── auth-processor │ │ └── src │ │ │ ├── app │ │ │ ├── (auth) │ │ │ │ ├── _actions │ │ │ │ │ └── signin.action.ts.hbs │ │ │ │ ├── _components │ │ │ │ │ └── signin-form.tsx.hbs │ │ │ │ ├── layout.tsx.hbs │ │ │ │ ├── signin │ │ │ │ │ └── page.tsx.hbs │ │ │ │ └── signout │ │ │ │ │ └── page.tsx.hbs │ │ │ ├── (private) │ │ │ │ ├── _components │ │ │ │ │ └── private-layout.tsx.hbs │ │ │ │ ├── dashboard │ │ │ │ │ └── page.tsx.hbs │ │ │ │ ├── layout.tsx.hbs │ │ │ │ └── profile │ │ │ │ │ └── page.tsx.hbs │ │ │ └── api │ │ │ │ └── auth │ │ │ │ └── [...nextauth] │ │ │ │ └── route.ts.hbs │ │ │ ├── lib │ │ │ ├── auth.ts.hbs │ │ │ └── authorize.ts.hbs │ │ │ ├── schema │ │ │ ├── auth-tables.ts.mysql.hbs │ │ │ ├── auth-tables.ts.postgresql.hbs │ │ │ ├── auth-tables.ts.sqlite.hbs │ │ │ ├── users.ts.mysql.hbs │ │ │ ├── users.ts.postgresql.hbs │ │ │ └── users.ts.sqlite.hbs │ │ │ └── types │ │ │ └── next-auth.d.ts.hbs │ ├── drizzle-ui │ │ ├── components │ │ │ └── ui │ │ │ │ ├── alert.tsx.hbs │ │ │ │ ├── app-layout.tsx.hbs │ │ │ │ ├── avatar.tsx.hbs │ │ │ │ ├── button.tsx.hbs │ │ │ │ ├── card.tsx.hbs │ │ │ │ ├── checkbox.tsx.hbs │ │ │ │ ├── code-block.tsx.hbs │ │ │ │ ├── dark-mode.tsx.hbs │ │ │ │ ├── dashboard-layout.tsx.hbs │ │ │ │ ├── dropdown-menu.tsx.hbs │ │ │ │ ├── form.tsx.hbs │ │ │ │ ├── input.tsx.hbs │ │ │ │ ├── label.tsx.hbs │ │ │ │ ├── pagination.tsx.hbs │ │ │ │ ├── rich-text-editor.tsx.hbs │ │ │ │ ├── search-input.tsx.hbs │ │ │ │ ├── select.tsx.hbs │ │ │ │ ├── sortable.tsx.hbs │ │ │ │ ├── table.tsx.hbs │ │ │ │ └── textarea.tsx.hbs │ │ ├── index.ts.hbs │ │ ├── lib │ │ │ └── utils.ts.hbs │ │ └── styles │ │ │ ├── rich-text-editor.css.hbs │ │ │ ├── styles.css.hbs │ │ │ └── typography.css.hbs │ ├── new-project-processor │ │ ├── eslint.config.mjs.hbs │ │ └── src │ │ │ ├── app │ │ │ ├── (development) │ │ │ │ ├── development │ │ │ │ │ └── page.tsx.hbs │ │ │ │ └── layout.tsx.hbs │ │ │ ├── (public) │ │ │ │ ├── _components │ │ │ │ │ └── public-layout.tsx.hbs │ │ │ │ └── layout.tsx.hbs │ │ │ ├── layout.tsx.hbs │ │ │ ├── page.tsx.hbs │ │ │ └── uploads │ │ │ │ └── [...segments] │ │ │ │ └── route.ts.hbs │ │ │ ├── config │ │ │ └── env.ts.hbs │ │ │ └── lib │ │ │ ├── config.ts.hbs │ │ │ ├── search-params.ts.hbs │ │ │ └── upload.ts.hbs │ └── scaffold-processor │ │ ├── form-controls │ │ ├── create-checkbox.tsx.hbs │ │ ├── create-file.tsx.hbs │ │ ├── create-input.tsx.hbs │ │ ├── create-references-input.tsx.hbs │ │ ├── create-references-select.tsx.hbs │ │ ├── create-textarea.tsx.hbs │ │ ├── update-checkbox.tsx.hbs │ │ ├── update-file.tsx.hbs │ │ ├── update-input-date.tsx.hbs │ │ ├── update-input-hidden.tsx.hbs │ │ ├── update-input-json.tsx.hbs │ │ ├── update-input-timestamp.tsx.hbs │ │ ├── update-input.tsx.hbs │ │ ├── update-references-input.tsx.hbs │ │ ├── update-references-select.tsx.hbs │ │ └── update-textarea.tsx.hbs │ │ └── src │ │ └── app │ │ └── (development) │ │ └── table │ │ ├── [id] │ │ ├── delete │ │ │ └── page.tsx.hbs │ │ ├── edit │ │ │ └── page.tsx.hbs │ │ └── page.tsx.hbs │ │ ├── _actions │ │ ├── create-action.ts.hbs │ │ ├── delete-action.ts.hbs │ │ └── update-action.ts.hbs │ │ ├── _components │ │ ├── create-form.tsx.hbs │ │ ├── delete-form.tsx.hbs │ │ ├── table-component.tsx.hbs │ │ └── update-form.tsx.hbs │ │ ├── _queries │ │ ├── get-by-id.ts.hbs │ │ └── get-list.ts.hbs │ │ ├── new │ │ └── page.tsx.hbs │ │ └── page.tsx.hbs └── tsconfig.json ├── drizzle-ui ├── .gitignore ├── .prettierrc ├── README.md ├── app │ ├── (docs) │ │ ├── (pages) │ │ │ ├── [id] │ │ │ │ └── page.tsx │ │ │ └── layout.tsx │ │ ├── components │ │ │ └── [id] │ │ │ │ └── page.tsx │ │ ├── layout.tsx │ │ └── not-found.tsx │ ├── app-layout-demo │ │ └── page.tsx │ ├── favicon.ico │ ├── globals.css │ ├── layout.tsx │ └── page.tsx ├── cli │ └── index.ts ├── components │ ├── component-demo │ │ ├── alert-demo.tsx │ │ ├── app-layout-demo.tsx │ │ ├── avatar-demo.tsx │ │ ├── button-demo.tsx │ │ ├── card-demo.tsx │ │ ├── checkbox-demo.tsx │ │ ├── dark-mode-demo.tsx │ │ ├── dropdown-menu-demo.tsx │ │ ├── form-demo.tsx │ │ ├── input-demo.tsx │ │ ├── label-demo.tsx │ │ ├── pagination-demo.tsx │ │ ├── rich-text-editor-demo.tsx │ │ ├── search-input-demo.tsx │ │ ├── select-demo.tsx │ │ ├── sortable-demo.tsx │ │ ├── table-demo.tsx │ │ └── textarea-demo.tsx │ ├── layouts │ │ └── docs-layout.tsx │ ├── rain │ │ ├── rain.css │ │ └── rain.tsx │ └── scroll-link.tsx ├── content │ ├── components │ │ ├── alert.md │ │ ├── app-layout.md │ │ ├── avatar.md │ │ ├── button.md │ │ ├── card.md │ │ ├── checkbox.md │ │ ├── dark-mode.md │ │ ├── dropdown-menu.md │ │ ├── form.md │ │ ├── input.md │ │ ├── label.md │ │ ├── pagination.md │ │ ├── rich-text-editor.md │ │ ├── search-input.md │ │ ├── select.md │ │ ├── sortable.md │ │ ├── table.md │ │ └── textarea.md │ ├── installation.md │ └── introduction.md ├── eslint.config.mjs ├── lib │ ├── file-utils.ts │ ├── markdown-utils.ts │ └── utils.ts ├── next.config.ts ├── package-lock.json ├── package.json ├── postcss.config.mjs ├── public │ ├── file.svg │ ├── globe.svg │ ├── next.svg │ ├── vercel.svg │ └── window.svg ├── rollup.cjs.mjs ├── rollup.esm.mjs ├── src │ ├── components │ │ └── ui │ │ │ ├── alert.tsx │ │ │ ├── app-layout.tsx │ │ │ ├── avatar.tsx │ │ │ ├── button.tsx │ │ │ ├── card.tsx │ │ │ ├── checkbox.tsx │ │ │ ├── code-block.tsx │ │ │ ├── dark-mode.tsx │ │ │ ├── dropdown-menu.tsx │ │ │ ├── form.tsx │ │ │ ├── input.tsx │ │ │ ├── label.tsx │ │ │ ├── pagination.tsx │ │ │ ├── rich-text-editor.tsx │ │ │ ├── search-input.tsx │ │ │ ├── select.tsx │ │ │ ├── sortable.tsx │ │ │ ├── table.tsx │ │ │ └── textarea.tsx │ ├── index.ts │ ├── lib │ │ └── utils.ts │ └── styles │ │ ├── rich-text-editor.css │ │ ├── styles.css │ │ └── typography.css ├── tsconfig.cjs.json ├── tsconfig.cli.json ├── tsconfig.esm.json └── tsconfig.json ├── drizzle-util ├── README.md ├── commands │ ├── init-command.ts │ └── scaffold-command.ts ├── index.ts ├── package-lock.json ├── package.json └── tsconfig.json ├── example ├── .gitignore ├── README.md ├── app │ ├── (admin) │ │ ├── _components │ │ │ ├── admin-layout.tsx │ │ │ └── users-components.tsx │ │ ├── _lib │ │ │ ├── drizzle-admin.config.ts │ │ │ └── users-table.config.ts │ │ ├── admin │ │ │ ├── [...segments] │ │ │ │ └── page.tsx │ │ │ ├── custom-page │ │ │ │ └── page.tsx │ │ │ └── page.tsx │ │ ├── api │ │ │ └── [[...segments]] │ │ │ │ └── route.ts │ │ └── layout.tsx │ ├── favicon.ico │ ├── globals.css │ ├── layout.tsx │ └── page.tsx ├── drizzle.config.ts ├── drizzle │ ├── 0000_unique_rictor.sql │ ├── 0001_gifted_sunset_bain.sql │ ├── 0002_silly_hannibal_king.sql │ ├── 0003_smart_killraven.sql │ └── meta │ │ ├── 0000_snapshot.json │ │ ├── 0001_snapshot.json │ │ ├── 0002_snapshot.json │ │ ├── 0003_snapshot.json │ │ └── _journal.json ├── eslint.config.mjs ├── lib │ ├── config.ts │ ├── db.ts │ └── schema.ts ├── next.config.ts ├── package-lock.json ├── package.json ├── postcss.config.mjs ├── public │ ├── file.svg │ ├── globe.svg │ ├── next.svg │ ├── vercel.svg │ └── window.svg ├── schema │ ├── categories.ts │ ├── posts-tags.ts │ ├── posts.ts │ ├── tags.ts │ └── users.ts ├── scripts │ ├── create-password-hash.ts │ ├── create-user.ts │ ├── grant-admin.ts │ ├── migrate.ts │ └── seed.ts └── tsconfig.json ├── package.json └── scripts └── sync-components.ts /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | demo* 3 | .env 4 | dist 5 | /docs/dist/ 6 | cypress/downloads 7 | docs/.vitepress/dist 8 | docs/.vitepress/cache -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "skipFiles": [ 12 | "/**" 13 | ], 14 | "program": "${workspaceFolder}\\index.ts", 15 | "outFiles": [ 16 | "${workspaceFolder}/dist/**/*.js", 17 | ], 18 | // "console": "externalTerminal", 19 | "args": ["db", "better-sqlite3"] 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Drizzle Next 2 | 3 | Generate apps using CLI commands inspired by Ruby on Rails and shadcn/ui 4 | 5 | ## Documentation 6 | 7 | - [Drizzle Next](https://www.drizzlenext.com) - Full Stack Next.js and Drizzle ORM Framework 8 | 9 | ## Author 10 | 11 | [travisluong](https://linktr.ee/travisluong) 12 | 13 | ## License 14 | 15 | MIT 16 | -------------------------------------------------------------------------------- /common/lib/handlebars-helpers.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | 3 | import Handlebars from "handlebars"; 4 | 5 | export function registerHandlebarsHelpers() { 6 | Handlebars.registerHelper("switch", function (value, options) { 7 | this._switch_value_ = value; 8 | this._switch_case_matched_ = false; 9 | return options.fn(this); 10 | }); 11 | 12 | Handlebars.registerHelper("case", function (value, options) { 13 | if (value === this._switch_value_) { 14 | this._switch_case_matched_ = true; 15 | return options.fn(this); 16 | } 17 | }); 18 | 19 | Handlebars.registerHelper("default", function (options) { 20 | if (!this._switch_case_matched_) { 21 | return options.fn(this); 22 | } 23 | }); 24 | } 25 | 26 | let runOnce = true; 27 | export function getHandlebars() { 28 | if (runOnce) { 29 | registerHandlebarsHelpers(); 30 | runOnce = false; 31 | } 32 | return Handlebars; 33 | } 34 | -------------------------------------------------------------------------------- /common/lib/log.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | 3 | export const log = { 4 | blue(str: string) { 5 | console.log(chalk.blueBright(str)); 6 | }, 7 | 8 | red(str: string) { 9 | console.log(chalk.red(str)); 10 | }, 11 | 12 | green(str: string) { 13 | console.log(chalk.green(str)); 14 | }, 15 | 16 | yellow(str: string) { 17 | console.log(chalk.yellow(str)); 18 | }, 19 | 20 | gray(str: string) { 21 | console.log(chalk.gray(str)); 22 | }, 23 | 24 | log(str: string) { 25 | console.log(str); 26 | }, 27 | 28 | success(str: string) { 29 | console.log(chalk.greenBright(str)); 30 | }, 31 | 32 | init(str: string) { 33 | console.log(chalk.bold.blueBright(str)); 34 | }, 35 | 36 | checklist(str: string) { 37 | console.log(chalk.magentaBright.underline(str)); 38 | }, 39 | 40 | task(str: string) { 41 | console.log("- " + str); 42 | }, 43 | 44 | cmdtask(str: string) { 45 | console.log("- terminal: " + str); 46 | }, 47 | 48 | subtask(str: string) { 49 | console.log(" - " + str); 50 | }, 51 | 52 | cmdsubtask(str: string) { 53 | console.log(" - terminal: " + str); 54 | }, 55 | 56 | exec(str: string) { 57 | console.log("> " + str); 58 | }, 59 | }; 60 | -------------------------------------------------------------------------------- /common/lib/pk-strategy.ts: -------------------------------------------------------------------------------- 1 | import { PkStrategy } from "../types/types"; 2 | 3 | export const pkStrategyImportTemplates: Record = { 4 | cuid2: `import { createId } from "@paralleldrive/cuid2";`, 5 | uuidv7: `import { uuidv7 } from "uuidv7";`, 6 | uuidv4: ``, 7 | nanoid: `import { nanoid } from "nanoid";`, 8 | auto_increment: "", 9 | }; 10 | 11 | export const pkKeyValTemplates: Record = { 12 | cuid2: "id: createId(),", 13 | uuidv7: "id: uuidv7(),", 14 | uuidv4: "id: crypto.randomUUID(),", 15 | nanoid: "id: nanoid(),", 16 | auto_increment: "", 17 | }; 18 | 19 | export const pkDependencies: Record = { 20 | cuid2: ["@paralleldrive/cuid2"], 21 | uuidv7: ["uuidv7"], 22 | uuidv4: [], 23 | nanoid: ["nanoid"], 24 | auto_increment: [], 25 | }; 26 | 27 | export const pkFunctionInvoke: Record = { 28 | cuid2: "createId()", 29 | uuidv7: "uuidv7()", 30 | uuidv4: "crypto.randomUUID()", 31 | nanoid: "nanoid()", 32 | auto_increment: "", 33 | }; 34 | -------------------------------------------------------------------------------- /common/lib/strategy-factory.ts: -------------------------------------------------------------------------------- 1 | import { DbDialect, DbPackageStrategyOpts } from "../types/types"; 2 | import { BetterSqlite3PackageStrategy } from "../db-packages/better-sqlite3-package-strategy"; 3 | import { Mysql2PackageStrategy } from "../db-packages/mysql2-package-strategy"; 4 | import { PgPackageStrategy } from "../db-packages/pg-package-strategy"; 5 | import { sqliteDialectStrategy } from "../db-dialects/sqlite-dialect-strategy"; 6 | import { mysqlDialectStrategy } from "../db-dialects/mysql-dialect-strategy"; 7 | import { postgresqlDialectStrategy } from "../db-dialects/postgresql-dialect-strategy"; 8 | 9 | export function packageStrategyFactory(opts: DbPackageStrategyOpts) { 10 | switch (opts.dbPackage) { 11 | case "better-sqlite3": 12 | return new BetterSqlite3PackageStrategy(opts); 13 | case "mysql2": 14 | return new Mysql2PackageStrategy(opts); 15 | case "pg": 16 | return new PgPackageStrategy(opts); 17 | default: 18 | throw new Error("invalid db package: " + opts.dbPackage); 19 | } 20 | } 21 | 22 | export function dialectStrategyFactory(dialect: DbDialect) { 23 | switch (dialect) { 24 | case "sqlite": 25 | return sqliteDialectStrategy; 26 | case "mysql": 27 | return mysqlDialectStrategy; 28 | case "postgresql": 29 | return postgresqlDialectStrategy; 30 | default: 31 | throw new Error("invalid dialect: " + dialect); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /common/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "common", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "vitest" 7 | }, 8 | "author": "", 9 | "license": "ISC", 10 | "description": "", 11 | "dependencies": { 12 | "chalk": "^5.4.1", 13 | "change-case-all": "^2.1.0", 14 | "handlebars": "^4.7.8", 15 | "pluralize": "^8.0.0", 16 | "vitest": "^3.1.3" 17 | }, 18 | "devDependencies": { 19 | "@types/pluralize": "^0.0.33" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /common/templates/db-dialect-processor/drizzle.config.ts.hbs: -------------------------------------------------------------------------------- 1 | import "dotenv/config"; 2 | import { defineConfig } from "drizzle-kit"; 3 | 4 | export default defineConfig({ 5 | dialect: "{{dialect}}", 6 | schema: "./src/schema/*", 7 | dbCredentials: { 8 | url: process.env.DB_URL!, 9 | }, 10 | verbose: true, 11 | strict: true, 12 | out: "./drizzle", 13 | casing: "snake_case", 14 | }); 15 | -------------------------------------------------------------------------------- /common/templates/db-dialect-processor/env.hbs: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | PORT=3000 -------------------------------------------------------------------------------- /common/templates/db-dialect-processor/gitignore.hbs: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /common/templates/db-dialect-processor/src/config/env.ts.hbs: -------------------------------------------------------------------------------- 1 | import "dotenv/config"; 2 | import { z } from "zod"; 3 | 4 | const envSchema = z.object({ 5 | NODE_ENV: z.enum(['development', 'production', 'test']), 6 | PORT: z.string(), 7 | DB_URL: z.string(), 8 | }); 9 | 10 | export const env = envSchema.parse(process.env); -------------------------------------------------------------------------------- /common/templates/db-dialect-processor/src/config/schema.ts.hbs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drizzlenext/drizzle-next/d0ac8e84099a17925252e0a932ba0925f2e879d6/common/templates/db-dialect-processor/src/config/schema.ts.hbs -------------------------------------------------------------------------------- /common/templates/db-dialect-processor/tsconfig.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "CommonJS", 5 | "lib": ["ES2020"], 6 | "strict": true, 7 | "esModuleInterop": true, 8 | "skipLibCheck": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "outDir": "./dist", 11 | "resolveJsonModule": true, 12 | "moduleResolution": "node", 13 | "types": ["node"], 14 | "paths": { 15 | "@/*": ["./src/*"] 16 | } 17 | }, 18 | "include": ["**/*.ts"], 19 | "exclude": ["node_modules", "dist"] 20 | } -------------------------------------------------------------------------------- /common/templates/db-packages/scripts/create-user.ts.hbs: -------------------------------------------------------------------------------- 1 | import { users } from "@/schema/users"; 2 | import bcrypt from "bcrypt"; 3 | import { openConnection } from "@/config/db"; 4 | 5 | async function main() { 6 | const { db, closeConnection } = await openConnection(); 7 | const email = process.argv[2]; 8 | const password = process.argv[3]; 9 | const hash = bcrypt.hashSync(password, 10); 10 | await db.insert(users).values({ email: email, password: hash, role: "user" }); 11 | console.log("created user " + email); 12 | await closeConnection(); 13 | } 14 | 15 | main(); 16 | -------------------------------------------------------------------------------- /common/templates/db-packages/scripts/migrate.ts.hbs: -------------------------------------------------------------------------------- 1 | {{migratorImport}} 2 | import { openConnection } from "@/config/db"; 3 | 4 | async function main() { 5 | const { db, closeConnection } = await openConnection(); 6 | await migrate(db, { migrationsFolder: "drizzle" }); 7 | await closeConnection(); 8 | } 9 | 10 | main(); 11 | -------------------------------------------------------------------------------- /common/templates/db-packages/src/config/custom-types.ts.mysql2.hbs: -------------------------------------------------------------------------------- 1 | import { customType } from "drizzle-orm/mysql-core"; 2 | 3 | export const customDecimal = customType<{ data: number }>({ 4 | dataType() { 5 | return "decimal"; 6 | }, 7 | fromDriver(value) { 8 | return Number(value); 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /common/templates/db-packages/src/config/custom-types.ts.pg.hbs: -------------------------------------------------------------------------------- 1 | import { customType } from "drizzle-orm/pg-core"; 2 | 3 | export const customDecimal = customType<{ data: number }>({ 4 | dataType() { 5 | return "decimal"; 6 | }, 7 | fromDriver(value) { 8 | return Number(value); 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /common/templates/db-packages/src/config/db.ts.better-sqlite3.hbs: -------------------------------------------------------------------------------- 1 | import { drizzle } from 'drizzle-orm/better-sqlite3'; 2 | import Database from 'better-sqlite3'; 3 | import { env } from "@/config/env"; 4 | import * as schema from "@/config/schema"; 5 | 6 | const sqlite = new Database(env.DB_URL); 7 | 8 | export const db = drizzle(sqlite, { schema, casing: "snake_case" }); 9 | 10 | export async function openConnection() { 11 | const betterSqlite = new Database(env.DB_URL); 12 | const db = drizzle(betterSqlite, { schema, casing: "snake_case" }); 13 | const closeConnection = async () => await betterSqlite.close(); 14 | return { 15 | db, 16 | closeConnection, 17 | } 18 | } -------------------------------------------------------------------------------- /common/templates/db-packages/src/config/db.ts.mysql2.hbs: -------------------------------------------------------------------------------- 1 | import { drizzle } from "drizzle-orm/mysql2"; 2 | import mysql from "mysql2/promise"; 3 | import { createConnection } from "mysql2"; 4 | import { env } from "@/config/env"; 5 | import * as schema from "@/config/schema"; 6 | 7 | const poolConnection = mysql.createPool({ 8 | uri: env.DB_URL, 9 | }); 10 | 11 | export const db = drizzle(poolConnection, { schema, mode: "default", casing: "snake_case" }); 12 | 13 | export async function openConnection() { 14 | const connection = createConnection(env.DB_URL); 15 | const db = drizzle(connection, { schema, mode: "default", casing: "snake_case" }); 16 | const closeConnection = async () => await connection.end(); 17 | return { 18 | db, 19 | closeConnection, 20 | } 21 | } -------------------------------------------------------------------------------- /common/templates/db-packages/src/config/db.ts.pg.hbs: -------------------------------------------------------------------------------- 1 | import { drizzle } from "drizzle-orm/node-postgres"; 2 | import { Pool, Client } from "pg"; 3 | import { env } from "@/config/env"; 4 | import * as schema from "@/config/schema"; 5 | 6 | const pool = new Pool({ 7 | connectionString: env.DB_URL, 8 | }); 9 | 10 | export const db = drizzle(pool, { schema, casing: "snake_case" }); 11 | 12 | export async function openConnection() { 13 | const client = new Client({ connectionString: env.DB_URL }); 14 | await client.connect(); 15 | const db = drizzle(client, { schema, casing: "snake_case" }); 16 | const closeConnection = async () => await client.end(); 17 | return { 18 | db, 19 | closeConnection, 20 | } 21 | } -------------------------------------------------------------------------------- /common/templates/scaffold-processor/src/schema/table.ts.mysql.hbs: -------------------------------------------------------------------------------- 1 | import { relations } from "drizzle-orm"; 2 | {{imports}} 3 | export const {{tableObj.pluralCamelCase}} = mysqlTable( 4 | "{{tableObj.pluralSnakeCase}}", 5 | { 6 | {{columns}} 7 | } 8 | ) 9 | 10 | export type Select{{tableObj.singularPascalCase}} = typeof {{tableObj.pluralCamelCase}}.$inferSelect; 11 | export type Insert{{tableObj.singularPascalCase}} = typeof {{tableObj.pluralCamelCase}}.$inferInsert; 12 | 13 | export const {{tableObj.pluralCamelCase}}Relations = relations({{tableObj.pluralCamelCase}}, ({ one, many }) => ({ 14 | {{#each referencesColumnList}} 15 | {{this.referenceTableVars.singularCamelCase}}: one({{this.referenceTableVars.pluralCamelCase}}, { 16 | fields: [{{../tableObj.pluralCamelCase}}.{{this.caseVariants.originalCamelCase}}], 17 | references: [{{this.referenceTableVars.pluralCamelCase}}.id] 18 | }), 19 | {{/each}} 20 | })); 21 | -------------------------------------------------------------------------------- /common/templates/scaffold-processor/src/schema/table.ts.postgresql.hbs: -------------------------------------------------------------------------------- 1 | import { relations } from "drizzle-orm"; 2 | {{imports}} 3 | export const {{tableObj.pluralCamelCase}} = pgTable( 4 | "{{tableObj.pluralSnakeCase}}", 5 | { 6 | {{columns}} 7 | } 8 | ) 9 | 10 | export type Select{{tableObj.singularPascalCase}} = typeof {{tableObj.pluralCamelCase}}.$inferSelect; 11 | export type Insert{{tableObj.singularPascalCase}} = typeof {{tableObj.pluralCamelCase}}.$inferInsert; 12 | 13 | export const {{tableObj.pluralCamelCase}}Relations = relations({{tableObj.pluralCamelCase}}, ({ one, many }) => ({ 14 | {{#each referencesColumnList}} 15 | {{this.referenceTableVars.singularCamelCase}}: one({{this.referenceTableVars.pluralCamelCase}}, { 16 | fields: [{{../tableObj.pluralCamelCase}}.{{this.caseVariants.originalCamelCase}}], 17 | references: [{{this.referenceTableVars.pluralCamelCase}}.id] 18 | }), 19 | {{/each}} 20 | })); 21 | -------------------------------------------------------------------------------- /common/templates/scaffold-processor/src/schema/table.ts.sqlite.hbs: -------------------------------------------------------------------------------- 1 | import { sql, relations } from "drizzle-orm"; 2 | {{imports}} 3 | export const {{tableObj.pluralCamelCase}} = sqliteTable( 4 | "{{tableObj.pluralSnakeCase}}", 5 | { 6 | {{columns}} 7 | } 8 | ) 9 | 10 | export type Select{{tableObj.singularPascalCase}} = typeof {{tableObj.pluralCamelCase}}.$inferSelect; 11 | export type Insert{{tableObj.singularPascalCase}} = typeof {{tableObj.pluralCamelCase}}.$inferInsert; 12 | 13 | export const {{tableObj.pluralCamelCase}}Relations = relations({{tableObj.pluralCamelCase}}, ({ one, many }) => ({ 14 | {{#each referencesColumnList}} 15 | {{this.referenceTableVars.singularCamelCase}}: one({{this.referenceTableVars.pluralCamelCase}}, { 16 | fields: [{{../tableObj.pluralCamelCase}}.{{this.caseVariants.originalCamelCase}}], 17 | references: [{{this.referenceTableVars.pluralCamelCase}}.id] 18 | }), 19 | {{/each}} 20 | })); 21 | -------------------------------------------------------------------------------- /common/templates/test-scripts/.prettierrc.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["prettier-plugin-tailwindcss"] 3 | } 4 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/custom.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --vp-home-hero-name-color: transparent; 3 | --vp-home-hero-name-background: -webkit-linear-gradient(120deg, #3C3C43, #3C3C43); 4 | --vp-home-hero-image-background-image: linear-gradient( -45deg, #4FC5F7 50%, #C5F74F 50% ); 5 | --vp-home-hero-image-filter: blur(68px); 6 | } 7 | 8 | .dark { 9 | --vp-home-hero-name-background: -webkit-linear-gradient(120deg, #4FC5F7, #C5F74F); 10 | } 11 | 12 | .VPHero .VPImage { 13 | border-radius: 50%; 14 | box-shadow: 0 0 20px rgba(0, 0, 0, 0.5); 15 | } 16 | 17 | .VPImage.logo { 18 | border-radius: 50%; 19 | } -------------------------------------------------------------------------------- /docs/.vitepress/theme/index.js: -------------------------------------------------------------------------------- 1 | import DefaultTheme from 'vitepress/theme' 2 | import './custom.css' 3 | 4 | export default DefaultTheme -------------------------------------------------------------------------------- /docs/api-examples.md: -------------------------------------------------------------------------------- 1 | --- 2 | outline: deep 3 | --- 4 | 5 | # Runtime API Examples 6 | 7 | This page demonstrates usage of some of the runtime APIs provided by VitePress. 8 | 9 | The main `useData()` API can be used to access site, theme, and page data for the current page. It works in both `.md` and `.vue` files: 10 | 11 | ```md 12 | 17 | 18 | ## Results 19 | 20 | ### Theme Data 21 |
{{ theme }}
22 | 23 | ### Page Data 24 |
{{ page }}
25 | 26 | ### Page Frontmatter 27 |
{{ frontmatter }}
28 | ``` 29 | 30 | 35 | 36 | ## Results 37 | 38 | ### Theme Data 39 |
{{ theme }}
40 | 41 | ### Page Data 42 |
{{ page }}
43 | 44 | ### Page Frontmatter 45 |
{{ frontmatter }}
46 | 47 | ## More 48 | 49 | Check out the documentation for the [full list of runtime APIs](https://vitepress.dev/reference/runtime-api#usedata). 50 | -------------------------------------------------------------------------------- /docs/drizzle-express/index.md: -------------------------------------------------------------------------------- 1 | # Drizzle Express 2 | 3 | Drizzle Express is a framework for generating REST APIs built with Express.js and Drizzle ORM. 4 | 5 | ## Why Drizzle Express? 6 | 7 | 1. Automated configuration – Start building immediately with sensible defaults. 8 | 2. Scaffold automation – Accelerate development by generating common boilerplate code. 9 | 3. Standard architecture – Route → Controller → Service → Schema. 10 | 11 | ## Introduction 12 | 13 | Drizzle Express enables rapid REST API development with a CLI tool inspired by Ruby on Rails. Unlike traditional frameworks, it is not installed as a project dependency. 14 | 15 | With Drizzle Express, you can generate a complete Express.js API project structure and scaffold resources in minutes. Its configuration defaults and automated scaffolding save setup time and reduce boilerplate. 16 | 17 | Drizzle Express follows this standard architecture: Route → Controller → Service → Schema. This approach is scalable, modular, and easy to test. 18 | -------------------------------------------------------------------------------- /docs/drizzle-next/index.md: -------------------------------------------------------------------------------- 1 | # Drizzle Next 2 | 3 | Drizzle Next is a full stack Next.js and Drizzle ORM code generation web framework. 4 | 5 | ## Why Drizzle Next? 6 | 7 | 1. Curated technology - Minimize decision fatigue with pre-selected libraries. 8 | 2. Configuration automation - Build immediately with sensible configuration defaults. 9 | 3. Scaffold automation - Build faster by generating common boilerplate code. 10 | 11 | ## Introduction 12 | 13 | This CLI Tool generates full-stack applications built on Next.js and Drizzle ORM. 14 | 15 | Unlike traditional frameworks, Drizzle Next is not installed into your project as a dependency. 16 | 17 | Instead, it is a command line interface code generation tool with a focus on keeping dependencies to a minimum. 18 | 19 | The generated code is fully customizable as it is written to your project. 20 | 21 | [See About page for more details](/about) 22 | 23 | ## When Drizzle Next might be for you 24 | 25 | - If you want ownership of data. 26 | - If you want to work directly with Next.js and Drizzle ORM code. 27 | - If you want a light-weight solution with minimal dependencies. 28 | 29 | ## When Drizzle Next might not be for you 30 | 31 | - If you want a third-party service to control your data. 32 | - If you want to work with complex abstractions and configuration-heavy solutions. 33 | - If you want less direct control over the code. 34 | -------------------------------------------------------------------------------- /docs/drizzle-ui/guide.md: -------------------------------------------------------------------------------- 1 | # Drizzle UI Guide 2 | 3 | [Click here for the complete Drizzle UI documentation](https://www.drizzlenext.com/ui) -------------------------------------------------------------------------------- /docs/drizzle-ui/index.md: -------------------------------------------------------------------------------- 1 | # Drizzle UI 2 | 3 | Drizzle UI is a minimalist component library. 4 | 5 | ## Why Drizzle UI? 6 | 7 | 1. You want complete control over the styling and implementation of your UI components. 8 | 2. You prefer not to rely on or be locked into a third-party UI library. 9 | 3. You are looking for an approach similar to shadcn/ui, but with fewer external dependencies to install. 10 | 11 | ## Introduction 12 | 13 | Drizzle UI was developed for use in Drizzle Next and Drizzle Admin. 14 | 15 | A copy of Drizzle UI is included in every installation of Drizzle Next and Drizzle Admin. 16 | 17 | Drizzle UI can also be used as an installable dependency or copied and pasted into your project. 18 | 19 | The documentation and component showcase can be found here: 20 | 21 | https://www.drizzlenext.com/ui/ 22 | -------------------------------------------------------------------------------- /docs/drizzle-util/index.md: -------------------------------------------------------------------------------- 1 | # Drizzle Util 2 | 3 | Drizzle Util is a CLI tool to help you quickly generate Drizzle ORM configuration and schemas. 4 | 5 | ## Why Drizzle Util? 6 | 7 | 1. You want to quickly set up Drizzle ORM. 8 | 2. You only want to generate the Drizzle ORM scaffolding (without Next.js and Express.js). 9 | 3. You want to use the Drizzle ORM scaffolding with another web framework. 10 | 11 | ## Introduction 12 | 13 | Drizzle Util is a command-line tool designed to help you quickly generate scaffolding for Drizzle ORM projects. It streamlines the setup process by automating the creation of configuration files and schemas, making it easier to start building with Drizzle ORM. 14 | 15 | Drizzle Util uses the same underlying Drizzle ORM scaffolding logic as Drizzle Next and Drizzle Express. 16 | -------------------------------------------------------------------------------- /docs/drizzle-util/installation.md: -------------------------------------------------------------------------------- 1 | # Drizzle Util Installation 2 | 3 | This guide covers everything you need to know to install and start using Drizzle Util. 4 | 5 | ## Step 1: Create new project 6 | 7 | ```bash 8 | mkdir my-app 9 | cd my-app 10 | npm init -y 11 | ``` 12 | 13 | ## Step 2: Run the CLI 14 | 15 | Initialize Drizzle ORM configuration. 16 | 17 | ```bash 18 | npx drizzle-util@latest init 19 | ``` 20 | 21 | ## Step 3: Configure project 22 | 23 | ```text 24 | ? Which package manager would you like to use? npm 25 | ? Which database dialect would you like to use? sqlite 26 | ? Which primary key generation strategy would you like to use? cuid2 27 | ``` 28 | 29 | ## Step 4: Generate scaffold 30 | 31 | In this example, we'll create a simple blog schema. 32 | 33 | Create a `categories` schema: 34 | 35 | ```bash 36 | npx drizzle-util@latest scaffold category -c name:text 37 | ``` 38 | 39 | Create a `posts` schema: 40 | 41 | ```bash 42 | npx drizzle-util@latest scaffold post -c title:text category_id:references content:text is_draft:boolean published_at:timestamp 43 | ``` 44 | 45 | Run the drizzle migrations: 46 | 47 | ```bash 48 | npx drizzle-kit generate 49 | npx drizzle-kit migrate 50 | ``` 51 | -------------------------------------------------------------------------------- /docs/markdown-examples.md: -------------------------------------------------------------------------------- 1 | # Markdown Extension Examples 2 | 3 | This page demonstrates some of the built-in markdown extensions provided by VitePress. 4 | 5 | ## Syntax Highlighting 6 | 7 | VitePress provides Syntax Highlighting powered by [Shiki](https://github.com/shikijs/shiki), with additional features like line-highlighting: 8 | 9 | **Input** 10 | 11 | ````md 12 | ```js{4} 13 | export default { 14 | data () { 15 | return { 16 | msg: 'Highlighted!' 17 | } 18 | } 19 | } 20 | ``` 21 | ```` 22 | 23 | **Output** 24 | 25 | ```js{4} 26 | export default { 27 | data () { 28 | return { 29 | msg: 'Highlighted!' 30 | } 31 | } 32 | } 33 | ``` 34 | 35 | ## Custom Containers 36 | 37 | **Input** 38 | 39 | ```md 40 | ::: info 41 | This is an info box. 42 | ::: 43 | 44 | ::: tip 45 | This is a tip. 46 | ::: 47 | 48 | ::: warning 49 | This is a warning. 50 | ::: 51 | 52 | ::: danger 53 | This is a dangerous warning. 54 | ::: 55 | 56 | ::: details 57 | This is a details block. 58 | ::: 59 | ``` 60 | 61 | **Output** 62 | 63 | ::: info 64 | This is an info box. 65 | ::: 66 | 67 | ::: tip 68 | This is a tip. 69 | ::: 70 | 71 | ::: warning 72 | This is a warning. 73 | ::: 74 | 75 | ::: danger 76 | This is a dangerous warning. 77 | ::: 78 | 79 | ::: details 80 | This is a details block. 81 | ::: 82 | 83 | ## More 84 | 85 | Check out the documentation for the [full list of markdown extensions](https://vitepress.dev/guide/markdown). 86 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "devDependencies": { 6 | "vitepress": "^1.5.0" 7 | }, 8 | "scripts": { 9 | "test": "echo \"Error: no test specified\" && exit 1", 10 | "docs:dev": "vitepress dev", 11 | "docs:build": "vitepress build", 12 | "docs:preview": "vitepress preview" 13 | }, 14 | "author": "", 15 | "license": "ISC", 16 | "description": "" 17 | } -------------------------------------------------------------------------------- /docs/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drizzlenext/drizzle-next/d0ac8e84099a17925252e0a932ba0925f2e879d6/docs/public/favicon.ico -------------------------------------------------------------------------------- /drizzle-admin/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # env files (can opt-in for committing if needed) 34 | .env* 35 | 36 | # vercel 37 | .vercel 38 | 39 | # typescript 40 | *.tsbuildinfo 41 | next-env.d.ts 42 | 43 | uploads/ 44 | sqlite.db -------------------------------------------------------------------------------- /drizzle-admin/README.md: -------------------------------------------------------------------------------- 1 | # Drizzle Admin 2 | 3 | Admin Dashboard for Drizzle Next 4 | 5 | React component that turns your Drizzle schema into an admin dashboard 6 | 7 | ## Documentation 8 | 9 | https://www.drizzlenext.com/drizzle-admin 10 | 11 | ## Author 12 | 13 | [travisluong](https://linktr.ee/travisluong) 14 | 15 | ## License 16 | 17 | MIT -------------------------------------------------------------------------------- /drizzle-admin/app/(admin)/_lib/drizzle-admin.config.ts: -------------------------------------------------------------------------------- 1 | import { db } from "@/lib/db"; 2 | import { DrizzleAdminConfig } from "@/src/types"; 3 | import { categories } from "@/schema/categories"; 4 | import { postsTags } from "@/schema/posts-tags"; 5 | import { tags } from "@/schema/tags"; 6 | import { usersTableConfig } from "@/app/(admin)/_lib/users-table.config"; 7 | import { posts } from "@/schema/posts"; 8 | 9 | export const config: DrizzleAdminConfig = { 10 | basePath: "/admin", 11 | schema: { 12 | users: usersTableConfig, 13 | posts: { 14 | drizzleTable: posts, 15 | formControlMap: { 16 | // badges: "json" 17 | } 18 | }, 19 | categories: { 20 | drizzleTable: categories, 21 | searchBy: ["id", "name"] 22 | }, 23 | tags: { 24 | drizzleTable: tags, 25 | }, 26 | postsTags: { 27 | drizzleTable: postsTags, 28 | }, 29 | }, 30 | db: db, 31 | dbDialect: "sqlite", 32 | paginationOpts: {enablePageInput: true, buttonVariant: "info", perPageInputType: "text"} 33 | }; 34 | -------------------------------------------------------------------------------- /drizzle-admin/app/(admin)/_lib/users-table.config.ts: -------------------------------------------------------------------------------- 1 | import { users } from "@/schema/users"; 2 | import { DrizzleTableConfig } from "@/src/types"; 3 | import { 4 | UserDeletePageNav, 5 | UserEditPageNav, 6 | UserListPageNav, 7 | UserRoleCustomFormControl, 8 | UserRowNav, 9 | UserViewPageNav, 10 | } from "@/app/(admin)/_components/users-components"; 11 | 12 | export const usersTableConfig: DrizzleTableConfig = { 13 | drizzleTable: users, 14 | formControlMap: { image: "file", role: "custom" }, 15 | customFormControlMap: { role: UserRoleCustomFormControl }, 16 | components: { 17 | RowNav: UserRowNav, 18 | ViewPageNav: UserViewPageNav, 19 | EditPageNav: UserEditPageNav, 20 | DeletePageNav: UserDeletePageNav, 21 | ListPageNav: UserListPageNav, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /drizzle-admin/app/(admin)/admin/[...segments]/page.tsx: -------------------------------------------------------------------------------- 1 | import { config } from "@/app/(admin)/_lib/drizzle-admin.config"; 2 | import { DrizzleAdmin } from "@/src/components"; 3 | 4 | export type Params = Promise<{ [key: string]: string }>; 5 | export type SearchParams = Promise<{ [key: string]: string | undefined }>; 6 | 7 | export default async function Page(props: { 8 | params: Params; 9 | searchParams: SearchParams; 10 | }) { 11 | return ( 12 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /drizzle-admin/app/(admin)/admin/custom-page/page.tsx: -------------------------------------------------------------------------------- 1 | type Params = Promise<{ id: string }>; 2 | type SearchParams = Promise<{ [key: string]: string | string[] | undefined }>; 3 | 4 | export default async function Page(props: { 5 | params: Params; 6 | searchParams: SearchParams; 7 | }) { 8 | const params = await props.params; 9 | const searchParams = await props.searchParams; 10 | return ( 11 |
12 |

params: {JSON.stringify(params)}

13 |

searchParams: {JSON.stringify(searchParams)}

14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /drizzle-admin/app/(admin)/admin/page.tsx: -------------------------------------------------------------------------------- 1 | import { Alert } from "@/src/drizzle-ui"; 2 | import { ConstructionIcon } from "lucide-react"; 3 | 4 | type Params = Promise<{ id: string }>; 5 | type SearchParams = Promise<{ [key: string]: string | string[] | undefined }>; 6 | 7 | export default async function Page(props: { 8 | params: Params; 9 | searchParams: SearchParams; 10 | }) { 11 | const params = await props.params; 12 | const searchParams = await props.searchParams; 13 | return ( 14 |
15 | 16 | Welcome to Drizzle Admin. Use this space to build a dashboard home page. 17 | 18 |
19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /drizzle-admin/app/(admin)/api/[[...segments]]/route.ts: -------------------------------------------------------------------------------- 1 | import { config } from "@/app/(admin)/_lib/drizzle-admin.config"; 2 | import { 3 | DELETE_ROUTE, 4 | GET_ROUTE, 5 | PATCH_ROUTE, 6 | POST_ROUTE, 7 | PUT_ROUTE, 8 | } from "@/src/routes"; 9 | 10 | export const POST = POST_ROUTE(config); 11 | export const GET = GET_ROUTE(config); 12 | export const PUT = PUT_ROUTE(config); 13 | export const PATCH = PATCH_ROUTE(config); 14 | export const DELETE = DELETE_ROUTE(config); 15 | -------------------------------------------------------------------------------- /drizzle-admin/app/(admin)/layout.tsx: -------------------------------------------------------------------------------- 1 | import { DarkModeScript } from "@/src/drizzle-ui"; 2 | import { AdminLayout } from "./_components/admin-layout"; 3 | import "@/src/styles/styles.css"; 4 | 5 | export default function Layout({ children }: { children: React.ReactNode }) { 6 | return ( 7 | 8 | {children} 9 | 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /drizzle-admin/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drizzlenext/drizzle-next/d0ac8e84099a17925252e0a932ba0925f2e879d6/drizzle-admin/app/favicon.ico -------------------------------------------------------------------------------- /drizzle-admin/app/globals.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | 3 | @custom-variant dark (&:where(.dark, .dark *)); 4 | -------------------------------------------------------------------------------- /drizzle-admin/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Geist, Geist_Mono } from "next/font/google"; 3 | import "./globals.css"; 4 | 5 | const geistSans = Geist({ 6 | variable: "--font-geist-sans", 7 | subsets: ["latin"], 8 | }); 9 | 10 | const geistMono = Geist_Mono({ 11 | variable: "--font-geist-mono", 12 | subsets: ["latin"], 13 | }); 14 | 15 | export const metadata: Metadata = { 16 | title: "Drizzle Next App", 17 | description: "Generated by Drizzle Next", 18 | }; 19 | 20 | export default function RootLayout({ 21 | children, 22 | }: Readonly<{ 23 | children: React.ReactNode; 24 | }>) { 25 | return ( 26 | 27 | 30 | {children} 31 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /drizzle-admin/app/page.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | 3 | export default function Page() { 4 | return ( 5 |
6 |
7 |
8 |

9 | Drizzle Admin Development 10 |

11 | Admin Dashboard 12 |
13 |
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /drizzle-admin/drizzle.config.ts: -------------------------------------------------------------------------------- 1 | import "dotenv/config"; 2 | import { defineConfig } from "drizzle-kit"; 3 | 4 | export default defineConfig({ 5 | dialect: "sqlite", 6 | schema: "./schema/*", 7 | dbCredentials: { 8 | url: process.env.DB_URL!, 9 | }, 10 | verbose: true, 11 | strict: true, 12 | out: "./drizzle", 13 | casing: "snake_case", 14 | }); 15 | -------------------------------------------------------------------------------- /drizzle-admin/drizzle/0001_gifted_sunset_bain.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE `categories` ( 2 | `id` text PRIMARY KEY NOT NULL, 3 | `name` text, 4 | `created_at` integer DEFAULT (strftime('%s', 'now')) NOT NULL, 5 | `updated_at` integer DEFAULT (strftime('%s', 'now')) NOT NULL 6 | ); 7 | --> statement-breakpoint 8 | CREATE TABLE `posts` ( 9 | `id` text PRIMARY KEY NOT NULL, 10 | `title` text, 11 | `category_id` text, 12 | `content` text, 13 | `is_published` integer, 14 | `created_at` integer DEFAULT (strftime('%s', 'now')) NOT NULL, 15 | `updated_at` integer DEFAULT (strftime('%s', 'now')) NOT NULL, 16 | FOREIGN KEY (`category_id`) REFERENCES `categories`(`id`) ON UPDATE no action ON DELETE no action 17 | ); 18 | -------------------------------------------------------------------------------- /drizzle-admin/drizzle/0002_silly_hannibal_king.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE `tags` ( 2 | `id` text PRIMARY KEY NOT NULL, 3 | `name` text, 4 | `created_at` integer DEFAULT (strftime('%s', 'now')) NOT NULL, 5 | `updated_at` integer DEFAULT (strftime('%s', 'now')) NOT NULL 6 | ); 7 | -------------------------------------------------------------------------------- /drizzle-admin/drizzle/0003_smart_killraven.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE `posts_tags` ( 2 | `id` text PRIMARY KEY NOT NULL, 3 | `post_id` text, 4 | `tag_id` text, 5 | `created_at` integer DEFAULT (strftime('%s', 'now')) NOT NULL, 6 | `updated_at` integer DEFAULT (strftime('%s', 'now')) NOT NULL, 7 | FOREIGN KEY (`post_id`) REFERENCES `posts`(`id`) ON UPDATE no action ON DELETE no action, 8 | FOREIGN KEY (`tag_id`) REFERENCES `tags`(`id`) ON UPDATE no action ON DELETE no action 9 | ); 10 | -------------------------------------------------------------------------------- /drizzle-admin/drizzle/0004_greedy_lethal_legion.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE `accounts`;--> statement-breakpoint 2 | DROP TABLE `authenticators`;--> statement-breakpoint 3 | DROP TABLE `sessions`;--> statement-breakpoint 4 | DROP TABLE `verification_tokens`; -------------------------------------------------------------------------------- /drizzle-admin/drizzle/0005_worried_magdalene.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE `posts` ADD `badges` text; -------------------------------------------------------------------------------- /drizzle-admin/drizzle/meta/_journal.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "7", 3 | "dialect": "sqlite", 4 | "entries": [ 5 | { 6 | "idx": 0, 7 | "version": "6", 8 | "when": 1738808830825, 9 | "tag": "0000_unique_rictor", 10 | "breakpoints": true 11 | }, 12 | { 13 | "idx": 1, 14 | "version": "6", 15 | "when": 1738816919800, 16 | "tag": "0001_gifted_sunset_bain", 17 | "breakpoints": true 18 | }, 19 | { 20 | "idx": 2, 21 | "version": "6", 22 | "when": 1740452077235, 23 | "tag": "0002_silly_hannibal_king", 24 | "breakpoints": true 25 | }, 26 | { 27 | "idx": 3, 28 | "version": "6", 29 | "when": 1740452150727, 30 | "tag": "0003_smart_killraven", 31 | "breakpoints": true 32 | }, 33 | { 34 | "idx": 4, 35 | "version": "6", 36 | "when": 1741738729567, 37 | "tag": "0004_greedy_lethal_legion", 38 | "breakpoints": true 39 | }, 40 | { 41 | "idx": 5, 42 | "version": "6", 43 | "when": 1742095944319, 44 | "tag": "0005_worried_magdalene", 45 | "breakpoints": true 46 | } 47 | ] 48 | } -------------------------------------------------------------------------------- /drizzle-admin/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { dirname } from "path"; 2 | import { fileURLToPath } from "url"; 3 | import { FlatCompat } from "@eslint/eslintrc"; 4 | 5 | const __filename = fileURLToPath(import.meta.url); 6 | const __dirname = dirname(__filename); 7 | 8 | const compat = new FlatCompat({ 9 | baseDirectory: __dirname, 10 | }); 11 | 12 | const eslintConfig = [ 13 | ...compat.config({ 14 | extends: ["next/core-web-vitals", "next"], 15 | plugins: ["@typescript-eslint"], 16 | rules: {}, 17 | }), 18 | ]; 19 | 20 | export default eslintConfig; 21 | -------------------------------------------------------------------------------- /drizzle-admin/lib/config.ts: -------------------------------------------------------------------------------- 1 | import "dotenv/config"; 2 | 3 | export const config = { 4 | DB_URL: process.env.DB_URL!, 5 | }; 6 | -------------------------------------------------------------------------------- /drizzle-admin/lib/db.ts: -------------------------------------------------------------------------------- 1 | import { drizzle } from 'drizzle-orm/better-sqlite3'; 2 | import Database from 'better-sqlite3'; 3 | import { config } from "@/lib/config"; 4 | import * as schema from "@/lib/schema"; 5 | 6 | const sqlite = new Database(config.DB_URL); 7 | 8 | export const db = drizzle(sqlite, { schema, casing: "snake_case" }); 9 | 10 | export async function openConnection() { 11 | const betterSqlite = new Database(config.DB_URL); 12 | const db = drizzle(betterSqlite, { schema, casing: "snake_case" }); 13 | const closeConnection = async () => await betterSqlite.close(); 14 | return { 15 | db, 16 | closeConnection, 17 | } 18 | } -------------------------------------------------------------------------------- /drizzle-admin/lib/schema.ts: -------------------------------------------------------------------------------- 1 | export * from "@/schema/users"; 2 | export * from "@/schema/categories"; 3 | export * from "@/schema/posts"; 4 | export * from "@/schema/tags"; 5 | export * from "@/schema/posts-tags"; 6 | -------------------------------------------------------------------------------- /drizzle-admin/next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | /* config options here */ 5 | }; 6 | 7 | export default nextConfig; 8 | -------------------------------------------------------------------------------- /drizzle-admin/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: ["@tailwindcss/postcss"], 3 | }; 4 | 5 | export default config; 6 | -------------------------------------------------------------------------------- /drizzle-admin/rollup.cjs.mjs: -------------------------------------------------------------------------------- 1 | import resolve from "@rollup/plugin-node-resolve"; 2 | import commonjs from "@rollup/plugin-commonjs"; 3 | import typescript from "@rollup/plugin-typescript"; 4 | import PeerDepsExternalPlugin from "rollup-plugin-peer-deps-external"; 5 | import dts from "rollup-plugin-dts"; 6 | import preserveDirectives from "rollup-preserve-directives"; 7 | 8 | // import packageJson from "./package.json" assert { type: "json" }; 9 | 10 | // eslint-disable-next-line import/no-anonymous-default-export 11 | export default [ 12 | { 13 | input: "src/index.ts", 14 | output: [ 15 | { 16 | // file: packageJson.main, 17 | dir: "dist/cjs", 18 | format: "cjs", 19 | sourcemap: true, 20 | preserveModules: true, 21 | }, 22 | ], 23 | plugins: [ 24 | PeerDepsExternalPlugin(), 25 | resolve(), 26 | commonjs(), 27 | typescript({ 28 | tsconfig: "./tsconfig.cjs.json", 29 | }), 30 | preserveDirectives(), 31 | ], 32 | }, 33 | { 34 | input: "dist/cjs/types/index.d.ts", 35 | output: [{ file: "dist/index.d.ts", format: "cjs" }], 36 | plugins: [dts()], 37 | }, 38 | ]; 39 | -------------------------------------------------------------------------------- /drizzle-admin/rollup.esm.mjs: -------------------------------------------------------------------------------- 1 | import resolve from "@rollup/plugin-node-resolve"; 2 | import commonjs from "@rollup/plugin-commonjs"; 3 | import typescript from "@rollup/plugin-typescript"; 4 | import PeerDepsExternalPlugin from "rollup-plugin-peer-deps-external"; 5 | import dts from "rollup-plugin-dts"; 6 | import preserveDirectives from "rollup-preserve-directives"; 7 | 8 | // import packageJson from "./package.json" assert { type: "json" }; 9 | 10 | // eslint-disable-next-line import/no-anonymous-default-export 11 | export default [ 12 | { 13 | input: "src/index.ts", 14 | output: [ 15 | { 16 | // file: packageJson.module, 17 | dir: "dist/esm", 18 | format: "esm", 19 | sourcemap: true, 20 | preserveModules: true, 21 | }, 22 | ], 23 | plugins: [ 24 | PeerDepsExternalPlugin(), 25 | resolve(), 26 | commonjs(), 27 | typescript({ 28 | tsconfig: "./tsconfig.esm.json", 29 | }), 30 | preserveDirectives(), 31 | ], 32 | }, 33 | { 34 | input: "dist/esm/types/index.d.ts", 35 | output: [{ file: "dist/index.d.ts", format: "esm" }], 36 | plugins: [dts()], 37 | }, 38 | ]; 39 | -------------------------------------------------------------------------------- /drizzle-admin/schema/categories.ts: -------------------------------------------------------------------------------- 1 | import { sql, relations } from "drizzle-orm"; 2 | import { 3 | sqliteTable, 4 | text, 5 | integer, 6 | } from "drizzle-orm/sqlite-core"; 7 | 8 | 9 | 10 | export type Category = typeof categories.$inferSelect; 11 | 12 | export const categories = sqliteTable( 13 | "categories", 14 | { 15 | id: text().primaryKey().$defaultFn(() => crypto.randomUUID()), 16 | name: text(), 17 | createdAt: integer({ mode: "timestamp" }).notNull().default(sql`(strftime('%s', 'now'))`), 18 | updatedAt: integer({ mode: "timestamp" }).notNull().default(sql`(strftime('%s', 'now'))`).$onUpdate(() => new Date()), 19 | } 20 | ) 21 | 22 | export const categoriesRelations = relations(categories, ({ one, many }) => ({ 23 | })); 24 | -------------------------------------------------------------------------------- /drizzle-admin/schema/posts-tags.ts: -------------------------------------------------------------------------------- 1 | import { sql, relations } from "drizzle-orm"; 2 | import { 3 | sqliteTable, 4 | text, 5 | integer, 6 | } from "drizzle-orm/sqlite-core"; 7 | 8 | 9 | import { posts } from "@/schema/posts"; 10 | import { tags } from "@/schema/tags"; 11 | 12 | 13 | export type PostsTag = typeof postsTags.$inferSelect; 14 | 15 | export const postsTags = sqliteTable( 16 | "posts_tags", 17 | { 18 | id: text().primaryKey().$defaultFn(() => crypto.randomUUID()), 19 | postId: text().references(() => posts.id), 20 | tagId: text().references(() => tags.id), 21 | createdAt: integer({ mode: "timestamp" }).notNull().default(sql`(strftime('%s', 'now'))`), 22 | updatedAt: integer({ mode: "timestamp" }).notNull().default(sql`(strftime('%s', 'now'))`).$onUpdate(() => new Date()), 23 | } 24 | ) 25 | 26 | export const postsTagsRelations = relations(postsTags, ({ one, many }) => ({ 27 | post: one(posts, { 28 | fields: [postsTags.postId], 29 | references: [posts.id] 30 | }), 31 | tag: one(tags, { 32 | fields: [postsTags.tagId], 33 | references: [tags.id] 34 | }), 35 | })); 36 | -------------------------------------------------------------------------------- /drizzle-admin/schema/posts.ts: -------------------------------------------------------------------------------- 1 | import { sql, relations } from "drizzle-orm"; 2 | import { 3 | sqliteTable, 4 | text, 5 | integer, 6 | } from "drizzle-orm/sqlite-core"; 7 | 8 | 9 | import { categories } from "@/schema/categories"; 10 | 11 | 12 | export type Post = typeof posts.$inferSelect; 13 | 14 | export const posts = sqliteTable( 15 | "posts", 16 | { 17 | id: text().primaryKey().$defaultFn(() => crypto.randomUUID()), 18 | title: text(), 19 | categoryId: text().references(() => categories.id), 20 | content: text(), 21 | isPublished: integer({ mode: "boolean" } ), 22 | badges: text({ mode: "json" }), 23 | createdAt: integer({ mode: "timestamp" }).notNull().default(sql`(strftime('%s', 'now'))`), 24 | updatedAt: integer({ mode: "timestamp" }).notNull().default(sql`(strftime('%s', 'now'))`).$onUpdate(() => new Date()), 25 | } 26 | ) 27 | 28 | export const postsRelations = relations(posts, ({ one, many }) => ({ 29 | category: one(categories, { 30 | fields: [posts.categoryId], 31 | references: [categories.id] 32 | }), 33 | })); 34 | -------------------------------------------------------------------------------- /drizzle-admin/schema/tags.ts: -------------------------------------------------------------------------------- 1 | import { sql, relations } from "drizzle-orm"; 2 | import { 3 | sqliteTable, 4 | text, 5 | integer, 6 | } from "drizzle-orm/sqlite-core"; 7 | 8 | 9 | 10 | export type Tag = typeof tags.$inferSelect; 11 | 12 | export const tags = sqliteTable( 13 | "tags", 14 | { 15 | id: text().primaryKey().$defaultFn(() => crypto.randomUUID()), 16 | name: text(), 17 | createdAt: integer({ mode: "timestamp" }).notNull().default(sql`(strftime('%s', 'now'))`), 18 | updatedAt: integer({ mode: "timestamp" }).notNull().default(sql`(strftime('%s', 'now'))`).$onUpdate(() => new Date()), 19 | } 20 | ) 21 | 22 | export const tagsRelations = relations(tags, ({ one, many }) => ({ 23 | })); 24 | -------------------------------------------------------------------------------- /drizzle-admin/schema/users.ts: -------------------------------------------------------------------------------- 1 | import { sql } from "drizzle-orm"; 2 | import { 3 | sqliteTable, 4 | integer, 5 | text, 6 | 7 | } from "drizzle-orm/sqlite-core" 8 | 9 | 10 | export const users = sqliteTable("users", { 11 | id: text().primaryKey().$defaultFn(() => crypto.randomUUID()), 12 | name: text(), 13 | email: text().notNull(), 14 | emailVerified: integer({ mode: "timestamp_ms" }), 15 | image: text(), 16 | role: text().notNull(), 17 | password: text(), 18 | createdAt: integer({ mode: "timestamp" }).notNull().default(sql`(strftime('%s', 'now'))`), 19 | updatedAt: integer({ mode: "timestamp" }).notNull().default(sql`(strftime('%s', 'now'))`).$onUpdate(() => new Date()), 20 | // [CODE_MARK users-table] 21 | }) 22 | 23 | export type User = typeof users.$inferSelect; 24 | -------------------------------------------------------------------------------- /drizzle-admin/scripts/create-password-hash.ts: -------------------------------------------------------------------------------- 1 | import bcrypt from "bcrypt"; 2 | 3 | async function main() { 4 | const password = process.argv[2]; 5 | const hash = bcrypt.hashSync(password, 10); 6 | console.log(hash); 7 | } 8 | 9 | main(); 10 | -------------------------------------------------------------------------------- /drizzle-admin/scripts/create-user.ts: -------------------------------------------------------------------------------- 1 | import { users } from "@/schema/users"; 2 | import bcrypt from "bcrypt"; 3 | import { openConnection } from "@/lib/db"; 4 | 5 | async function main() { 6 | const { db, closeConnection } = await openConnection(); 7 | const email = process.argv[2]; 8 | const password = process.argv[3]; 9 | const hash = bcrypt.hashSync(password, 10); 10 | await db.insert(users).values({ email: email, password: hash, role: "user" }); 11 | console.log("created user " + email); 12 | await closeConnection(); 13 | } 14 | 15 | main(); 16 | -------------------------------------------------------------------------------- /drizzle-admin/scripts/grant-admin.ts: -------------------------------------------------------------------------------- 1 | import { eq } from "drizzle-orm"; 2 | import { openConnection } from "@/lib/db"; 3 | import { users } from "@/schema/users"; 4 | 5 | async function main() { 6 | const { db, closeConnection } = await openConnection(); 7 | 8 | const email = process.argv[2]; 9 | 10 | const userObj = await db.query.users.findFirst({ 11 | where: eq(users.email, email), 12 | }); 13 | 14 | if (!userObj) { 15 | throw new Error("user not found " + email); 16 | } 17 | 18 | await db.update(users).set({ role: "admin" }).where(eq(users.email, email)); 19 | 20 | console.log("granted admin role to user " + email); 21 | 22 | await closeConnection(); 23 | 24 | process.exit(0); 25 | } 26 | 27 | main(); 28 | -------------------------------------------------------------------------------- /drizzle-admin/scripts/migrate.ts: -------------------------------------------------------------------------------- 1 | import { migrate } from "drizzle-orm/better-sqlite3/migrator"; 2 | import { openConnection } from "@/lib/db"; 3 | 4 | async function main() { 5 | const { db, closeConnection } = await openConnection(); 6 | await migrate(db, { migrationsFolder: "drizzle" }); 7 | await closeConnection(); 8 | } 9 | 10 | main(); 11 | -------------------------------------------------------------------------------- /drizzle-admin/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export { DrizzleAdmin } from "./drizzle-admin"; 2 | -------------------------------------------------------------------------------- /drizzle-admin/src/drizzle-ui/components/ui/alert.tsx: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT. THIS FILE IS AUTOGENERATED. */ 2 | import * as React from "react"; 3 | import { cn } from "../../lib/utils"; 4 | 5 | type AlertVariant = 6 | | "default" 7 | | "muted" 8 | | "success" 9 | | "destructive" 10 | | "warning" 11 | | "info"; 12 | 13 | const alertVariantMap: Record = { 14 | default: "border-primary text-primary", 15 | muted: "border-muted text-muted-foreground", 16 | success: "border-success text-success", 17 | destructive: "border-destructive text-destructive", 18 | warning: "border-warning text-warning", 19 | info: "border-info text-info", 20 | }; 21 | 22 | const Alert = React.forwardRef< 23 | HTMLDivElement, 24 | React.HTMLAttributes & { variant?: AlertVariant } 25 | >(({ className, variant = "default", ...props }, ref) => ( 26 |
35 | )); 36 | Alert.displayName = "Alert"; 37 | 38 | export { Alert }; 39 | -------------------------------------------------------------------------------- /drizzle-admin/src/drizzle-ui/components/ui/avatar.tsx: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT. THIS FILE IS AUTOGENERATED. */ 2 | import * as React from "react"; 3 | import { cn } from "../../lib/utils"; 4 | 5 | const Avatar = ({ 6 | src, 7 | className, 8 | children, 9 | }: { 10 | src?: string | null; 11 | className?: string; 12 | children?: React.ReactNode; 13 | }) => ( 14 |
15 | {src ? ( 16 | avatar 21 | ) : ( 22 |
{children}
23 | )} 24 |
25 | ); 26 | Avatar.displayName = "Avatar"; 27 | 28 | export { Avatar }; 29 | -------------------------------------------------------------------------------- /drizzle-admin/src/drizzle-ui/components/ui/checkbox.tsx: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT. THIS FILE IS AUTOGENERATED. */ 2 | import * as React from "react"; 3 | import { cn } from "../../lib/utils"; 4 | 5 | const Checkbox = React.forwardRef< 6 | HTMLInputElement, 7 | React.ComponentProps<"input"> 8 | >(({ className, ...props }, ref) => { 9 | return ( 10 | 16 | ); 17 | }); 18 | Checkbox.displayName = "Checkbox"; 19 | 20 | export { Checkbox }; 21 | -------------------------------------------------------------------------------- /drizzle-admin/src/drizzle-ui/components/ui/form.tsx: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT. THIS FILE IS AUTOGENERATED. */ 2 | import * as React from "react"; 3 | import { cn } from "../../lib/utils"; 4 | 5 | type FormMessageVariant = "default" | "success" | "error"; 6 | 7 | const formMessageVariantMap: Record = { 8 | default: "text-primary", 9 | success: "text-success", 10 | error: "text-destructive", 11 | }; 12 | 13 | const Form = React.forwardRef>( 14 | ({ className, ...props }, ref) => ( 15 |
20 | ), 21 | ); 22 | Form.displayName = "Form"; 23 | 24 | const FormControl = React.forwardRef< 25 | HTMLDivElement, 26 | React.HTMLAttributes 27 | >(({ className, ...props }, ref) => ( 28 |
29 | )); 30 | FormControl.displayName = "FormControl"; 31 | 32 | const FormMessage = React.forwardRef< 33 | HTMLDivElement, 34 | React.HTMLAttributes & { variant?: FormMessageVariant } 35 | >(({ className, variant, ...props }, ref) => ( 36 |
41 | )); 42 | FormMessage.displayName = "FormMessage"; 43 | 44 | export { Form, FormControl, FormMessage }; 45 | -------------------------------------------------------------------------------- /drizzle-admin/src/drizzle-ui/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT. THIS FILE IS AUTOGENERATED. */ 2 | import * as React from "react"; 3 | import { cn } from "../../lib/utils"; 4 | 5 | const Input = React.forwardRef>( 6 | ({ type, className, ...props }, ref) => { 7 | return ( 8 | 17 | ); 18 | }, 19 | ); 20 | Input.displayName = "Input"; 21 | 22 | export { Input }; 23 | -------------------------------------------------------------------------------- /drizzle-admin/src/drizzle-ui/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT. THIS FILE IS AUTOGENERATED. */ 2 | import * as React from "react"; 3 | import { cn } from "../../lib/utils"; 4 | 5 | const Label = React.forwardRef< 6 | HTMLLabelElement, 7 | React.LabelHTMLAttributes 8 | >(({ className, ...props }, ref) => ( 9 |