From 4b6f689d0415c13fed557c7bdcede84933e98ca2 Mon Sep 17 00:00:00 2001 From: Viacheslav Mamchurovskyi Date: Tue, 4 Feb 2025 14:13:59 +0200 Subject: [PATCH] feat: add dotenvx support for CLI --- .../{.env.sample.hbs => .env.local.hbs} | 0 .../createApp/templates/package.json.hbs | 8 +- adminforth/commands/createApp/utils.js | 25 +++--- .../docs/tutorial/001-gettingStarted.md | 28 +++---- .../docs/tutorial/01-helloWorld.md | 83 ++++++++++--------- .../documentation/docs/tutorial/04-deploy.md | 28 ++----- 6 files changed, 86 insertions(+), 86 deletions(-) rename adminforth/commands/createApp/templates/{.env.sample.hbs => .env.local.hbs} (100%) diff --git a/adminforth/commands/createApp/templates/.env.sample.hbs b/adminforth/commands/createApp/templates/.env.local.hbs similarity index 100% rename from adminforth/commands/createApp/templates/.env.sample.hbs rename to adminforth/commands/createApp/templates/.env.local.hbs diff --git a/adminforth/commands/createApp/templates/package.json.hbs b/adminforth/commands/createApp/templates/package.json.hbs index d4be9e456..e1f7ea7b7 100644 --- a/adminforth/commands/createApp/templates/package.json.hbs +++ b/adminforth/commands/createApp/templates/package.json.hbs @@ -8,15 +8,17 @@ "license": "ISC", "description": "", "scripts": { - "start": "tsx watch --env-file=.env index.ts", - "migrate": "npx prisma migrate deploy", - "makemigration": "npx --yes prisma migrate deploy; npx --yes prisma migrate dev", + "env": "dotenvx run -f .env.local -f .env --overload --", + "start": "npm run env -- tsx watch index.ts", + "migrateLocal": "npm run env -- npx prisma migrate deploy", + "makemigration": "npm run migrateLocal; npm run env -- npx --yes prisma migrate dev", "test": "echo \"Error: no test specified\" && exit 1" }, "engines": { "node": ">=20" }, "dependencies": { + "@dotenvx/dotenvx": "^1.34.0", "adminforth": "latest", "express": "latest" }, diff --git a/adminforth/commands/createApp/utils.js b/adminforth/commands/createApp/utils.js index 85774eedf..abac9fcea 100644 --- a/adminforth/commands/createApp/utils.js +++ b/adminforth/commands/createApp/utils.js @@ -180,15 +180,16 @@ async function writeTemplateFiles(dirname, cwd, options) { data: {}, }, { - src: '.env.sample.hbs', - dest: '.env.sample', + src: '.env.local.hbs', + dest: '.env.local', data: { dbUrl, prismaDbUrl }, }, { // We'll write .env using the same content as .env.sample - src: '.env.sample.hbs', + src: '.env.local.hbs', dest: '.env', - data: { dbUrl, prismaDbUrl }, + data: {}, + empty: true, }, { src: 'adminuser.ts.hbs', @@ -211,12 +212,16 @@ async function writeTemplateFiles(dirname, cwd, options) { // If a condition is specified and false, skip this file if (task.condition === false) continue; - const templatePath = path.join(dirname, 'templates', task.src); - const compiled = renderHBSTemplate(templatePath, task.data); const destPath = path.join(cwd, task.dest); - // Ensure parent directory exists // fse.ensureDirSync(path.dirname(destPath)); - fs.writeFileSync(destPath, compiled); + + if (task.empty) { + fs.writeFileSync(destPath, ''); + } else { + const templatePath = path.join(dirname, 'templates', task.src); + const compiled = renderHBSTemplate(templatePath, task.data); + fs.writeFileSync(destPath, compiled); + } } } @@ -233,11 +238,11 @@ function generateFinalInstructions(skipPrismaSetup) { let instruction = '⏭️ Run the following commands to get started:\n'; if (!skipPrismaSetup) instruction += ` - ${chalk.dim('// runs "npx prisma migrate dev --name init" to generate and apply initial migration')}; + ${chalk.dim('// Generate and apply initial migration')} ${chalk.cyan('$ npm run makemigration -- --name init')}\n`; instruction += ` - ${chalk.dim('// Starts dev server with tsx watch for hot-reloading')} + ${chalk.dim('// Start dev server with tsx watch for hot-reloading')} ${chalk.cyan('$ npm start')}\n `; diff --git a/adminforth/documentation/docs/tutorial/001-gettingStarted.md b/adminforth/documentation/docs/tutorial/001-gettingStarted.md index 70321d7ba..36752bfed 100644 --- a/adminforth/documentation/docs/tutorial/001-gettingStarted.md +++ b/adminforth/documentation/docs/tutorial/001-gettingStarted.md @@ -1,6 +1,6 @@ -# Getting Started +# Getting Started -This page provides a step-by-step guide to quickly get started with AdminForth using the `adminforth` CLI. +This page provides a step-by-step guide to quickly get started with AdminForth using the `adminforth` CLI. You will learn how to set up a new project using the `adminforth create-app` command and explore AdminForth’s fundamentals. > πŸ‘† For setup example without CLI check out [Hello World without CLI](./01-helloWorld.md) @@ -17,7 +17,7 @@ nvm use 20 ## Creating an AdminForth Project -The recommended way to get started with AdminForth is via the **`create-app`** CLI, which scaffolds a basic fully functional back-office application. Apart boilerplate it creates one resource for users management. +The recommended way to get started with AdminForth is via the **`create-app`** CLI, which scaffolds a basic fully functional back-office application. Apart boilerplate it creates one resource for users management. First, create and enter a directory where you want your AdminForth project to live. For instance: @@ -69,15 +69,15 @@ myadmin/ β”œβ”€β”€ index.ts # Main entry point: configures AdminForth & starts the server β”œβ”€β”€ package.json # Project dependencies β”œβ”€β”€ tsconfig.json # TypeScript configuration -β”œβ”€β”€ .env # Environment variables (e.g. database connection string) -β”œβ”€β”€ .env.sample # Sample env file (for distribution to teammates) -└── .gitignore +β”œβ”€β”€ .env # Env vars like tokens, secrets that should not be in version control +β”œβ”€β”€ .env.local # General local environment variables +└── .gitignore ``` ### Initial Migration & Future Migrations -> ☝️ CLI creates Prisma schema file for managing migrations in relational databases, however you are not forced to use it. Instead you are free to use your favourite or existing migration tool. In this case just ignore generated prisma file, and don't run migration command which will be suggested by CLI. However you have to ensure that your migration tool will generate required table `adminuser` with same fields and types for Users resource. +> ☝️ CLI creates Prisma schema file for managing migrations in relational databases, however you are not forced to use it. Instead you are free to use your favourite or existing migration tool. In this case just ignore generated prisma file, and don't run migration command which will be suggested by CLI. However you have to ensure that your migration tool will generate required table `adminuser` with same fields and types for Users resource. CLI will suggest you a command to initialize the database with Prisma: @@ -85,7 +85,7 @@ CLI will suggest you a command to initialize the database with Prisma: npm run makemigration -- --name init ``` -This will create a migration file in `migrations` and apply it to the database. +This will create a migration file in `migrations` and apply it to the database. In future, when you need to add new resources, you need to modify `schema.prisma` (add models, change fields, etc.). After doing any modification you need to create a new migration using next command: @@ -93,7 +93,7 @@ In future, when you need to add new resources, you need to modify `schema.prisma npm run makemigration -- --name ``` -Other developers need to pull migration and run `npm run migrate` to apply any unapplied migrations. +Other developers need to pull migration and run `npm run migrateLocal` to apply any unapplied migrations. ## Run the Server @@ -107,12 +107,11 @@ Open http://localhost:3500 in your browser and (default credentials are `adminfo ![alt text](localhost_3500_login.png) - ## AdminForth Basic Philosophy AdminForth connects to existing databases and provides a back-office for managing data including CRUD operations, filtering, sorting, and more. -Database can be already created by using any database management tool, ORM or migrator. +Database can be already created by using any database management tool, ORM or migrator. AdminForth itself never modifies database schema, does not add columns or new tables. However for those who have no own migration managment AdminForth CLI suggests using Prisma. This allows to provide simple and reliable schema management for standalone projects which have no DB yet. @@ -124,11 +123,9 @@ Also in AdminForth you can define in "Vue" way: * create own pages e.g. Dashboard using AdminForth Components Library (AFCL) or any other Vue componetns. * insert injections into standard pages (e.g. add diagram to list view) - - ## Adding an `apartments` Model -So far, our freshly generated AdminForth project includes a default `adminuser` model and a corresponding `adminuser` resource. +So far, our freshly generated AdminForth project includes a default `adminuser` model and a corresponding `adminuser` resource. Let’s expand our app to suport managment of **`apartments`** model. Adding new resource will involve next steps: @@ -373,7 +370,7 @@ export const admin = new AdminForth({ ``` -## Generating fake appartments +## Generating fake appartments ```ts title="./index.ts" //diff-add @@ -435,4 +432,3 @@ This will create records during first launch. Now you should see: ![alt text](localhost_3500_resource_aparts.png) Feel free to play with the data, add more fields, and customize the UI to your liking. - diff --git a/adminforth/documentation/docs/tutorial/01-helloWorld.md b/adminforth/documentation/docs/tutorial/01-helloWorld.md index b9a3c0bc4..ffb6130d0 100644 --- a/adminforth/documentation/docs/tutorial/01-helloWorld.md +++ b/adminforth/documentation/docs/tutorial/01-helloWorld.md @@ -6,11 +6,11 @@ sidebar_class_name: hidden-sidebar # Hello world app without CLI -While AdminForth CLI is the fastest way to create a new project, you can also create a new project manually. +While AdminForth CLI is the fastest way to create a new project, you can also create a new project manually. This might help you better understand how AdminForth project is structured and how to customize it. -Here we create database with users and posts tables and admin panel for it. +Here we create database with users and posts tables and admin panel for it. Users table will be used to store a credentials for login into admin panel itself. @@ -36,31 +36,56 @@ npm i adminforth express @types/express typescript tsx @types/node -D npx --yes tsc --init --module NodeNext --target ESNext ``` -## Env variables +## Environment variables -Create `.env` file in root directory and put following content: +Create two files in your project's root directory: -```bash title="./.env" +- `.env.local` β€” Place your non-sensitive environment variables here (e.g., local database paths, default configurations). This file can be safely committed to your repository as a demo or template configuration. +- `.env` β€” Store sensitive tokens and secrets here (for example, `ADMINFORTH_SECRET` and other private keys). Ensure that `.env` is added to your `.gitignore` to prevent accidentally committing sensitive data. + +Put the following content to the `.env.local` file: + +```bash title="./.env.local" +ADMINFORTH_SECRET=123 +NODE_ENV=development DATABASE_FILE=../db.sqlite DATABASE_FILE_URL=file:${DATABASE_FILE} -ADMINFORTH_SECRET=123 ``` > ☝️ Production best practices: +> > 1) Most likely you not need `.env` file at all, instead you should use environment variables (from Docker, Kubernetes, Operating System, etc.) -> 2) You should set `NODE_ENV` to `production` so it will not waste extra resources on hot reload. +> 2) Set `NODE_ENV` to `production` in your deployment environment to optimize performance and disable development features like hot reloading. > 3) You should generate very unique value `ADMINFORTH_SECRET` and store it in Vault or other secure place. +## Setting up the scripts + +Open `package.json` and add the following scripts: -> ☝️ If you are using Git, obviously you should make sure you will never commit `.env` file to the repository, because -it might contain your own sensitive secrets. So to follow best practices, we recommend to add `.env` into `.gitignore` and create `.env.sample` as template for other repository users with demo data. +```json title="./package.json" +{ + ... +//diff-add + "type": "module", + "scripts": { + ... +//diff-add + "env": "dotenvx run -f .env.local -f .env --overload --", +//diff-add + "start": "npm run env -- tsx watch index.ts", +//diff-add + "migrateLocal": "npm run env -- npx prisma migrate deploy", +//diff-add + "makemigration": "npm run env -- npx --yes prisma migrate deploy; npm run env -- npx --yes prisma migrate dev", + }, +} +``` ## Database creation -> ☝️ For demo purposes we will create a database using Prisma and SQLite. +> ☝️ For demo purposes we will create a database using Prisma and SQLite. > You can also create it using any other favorite tool or ORM and skip this step. - Create `./schema.prisma` and put next content there: ```text title="./schema.prisma" @@ -75,19 +100,19 @@ datasource db { model User { id String @id - createdAt DateTime + createdAt DateTime email String @unique - role String + role String passwordHash String posts Post[] } model Post { id String @id - createdAt DateTime + createdAt DateTime title String content String? - published Boolean + published Boolean author User? @relation(fields: [authorId], references: [id]) authorId String? } @@ -97,25 +122,10 @@ model Post { Create database using `prisma migrate`: ```bash -npx --yes prisma migrate dev --name init +npm run makemigration --name init ``` -## Setting up adminforth - -Open `package.json`, set `type` to `module` and add `start` script: - -```json title="./package.json" -{ - ... -//diff-add - "type": "module", - "scripts": { - ... -//diff-add - "start": "NODE_ENV=development tsx watch --env-file=.env index.ts", - }, -} -``` +## Setting up AdminForth Create `index.ts` file in root directory with following content: @@ -311,7 +321,7 @@ if (import.meta.url === `file://${process.argv[1]}`) { } ``` -> ☝️ For simplicity we defined whole configuration in one file. Normally once configuration grows you should +> ☝️ For simplicity we defined whole configuration in one file. Normally once configuration grows you should > move each resource configuration to separate file and organize them to folder and import them in `index.ts`. Now you can run your app: @@ -322,10 +332,8 @@ npm start Open http://localhost:3500 in your browser and login with credentials `adminforth` / `adminforth`. - ![alt text](localhost_3500_login.png) - ## Initializing custom directory If you are not using CLI, you can create `custom` directory and initialize it with `npm`: @@ -336,7 +344,7 @@ npm init -y ``` Also, for better development experience we recommend to create file `custom/tsconfig.json` with the following content: - + ```json { "compilerOptions": { @@ -356,7 +364,6 @@ Also, for better development experience we recommend to create file `custom/tsco } ``` - ## Possible configuration options -Check [AdminForthConfig](/docs/api/Back/interfaces/AdminForthConfig.md) for all possible options. \ No newline at end of file +Check [AdminForthConfig](/docs/api/Back/interfaces/AdminForthConfig.md) for all possible options. diff --git a/adminforth/documentation/docs/tutorial/04-deploy.md b/adminforth/documentation/docs/tutorial/04-deploy.md index a8f0edf75..dc1ec269a 100644 --- a/adminforth/documentation/docs/tutorial/04-deploy.md +++ b/adminforth/documentation/docs/tutorial/04-deploy.md @@ -7,7 +7,7 @@ It will start the server on configured HTTP port and you can use any proxy like ## Building SPA in Docker build time -In current index.ts file you might use call to `bundleNow` method which starts building internal SPA bundle when `index.ts` started +In current index.ts file you might use call to `bundleNow` method which starts building internal SPA bundle when `index.ts` started executing. SPA building generally takes from 10 seconds to minute depending on the external modules you will add into AdminForth and extended functionality you will create. To fully exclude this bundle time we recommend doing bundling in build time. @@ -49,7 +49,6 @@ node_modules *.sqlite ``` - In root directory create file `Dockerfile`: ```Dockerfile @@ -57,7 +56,7 @@ In root directory create file `Dockerfile`: FROM node:20-alpine WORKDIR /code/ ADD package.json package-lock.json /code/ -RUN npm ci +RUN npm ci ADD . /code/ RUN --mount=type=cache,target=/tmp npx tsx bundleNow.ts CMD ["npm", "run", "migrateLiveAndStart"] @@ -69,22 +68,17 @@ Add `bundleNow` and `startLive` to `package.json`: { "type": "module", "scripts": { - "start": "tsx watch --env-file=.env index.ts", -//diff-add - "startLive": "NODE_ENV=production tsx index.ts" -//diff-add - "migrate": "npx --yes prisma migrate deploy", + "env": "dotenvx run -f .env.local -f .env --overload --", + "start": "npm run env -- tsx watch index.ts", + ... //diff-add - "migrateLiveAndStart": "npm run migrate && npm run startLive" + "migrateLiveAndStart": "npx --yes prisma migrate deploy && tsx index.ts" }, } ``` - - ## Building the image - Now you can build your image: ```bash @@ -103,10 +97,8 @@ docker run -p 3500:3500 \ myadminapp ``` - Now open your browser and go to `http://localhost:3500` to see your AdminForth application running in Docker container. - ## Adding SSL (https) to AdminForth There are lots of ways today to put your application behind SSL gateway. You might simply put AdminForth instance behind free Cloudflare CDN, @@ -184,12 +176,12 @@ Now pull this compose file and all directories to your server and run: docker compose -p stack-my-app -f compose.yml up -d --build --remove-orphans --wait ``` -> ☝️ You can also test this compose stack locally on your machine but SSL will not work, +> ☝️ You can also test this compose stack locally on your machine but SSL will not work, > so locally you can ignore Chrome warning about SSL and test your AdminForth application. ## Subpath deployment -If you want to deploy your AdminForth application to a sub-folder like `https://mydomain.com/admin` you +If you want to deploy your AdminForth application to a sub-folder like `https://mydomain.com/admin` you should do the following: 1) Open `index.ts` file and change `ADMIN_BASE_URL` constant to your subpath: @@ -218,12 +210,10 @@ Now you can access your AdminForth application by going to `https://mydomain.com If you want to automate the deployment process with CI follow [our docker - traefik guide](https://devforth.io/blog/onlogs-open-source-simplified-web-logs-viewer-for-dockers/) - # Nginx version If you are using Nginx instead of traefik, here is siple proxy pass config: - ``` server { listen 80; @@ -250,4 +240,4 @@ server { proxy_pass http://127.0.0.1:3500; } } -``` \ No newline at end of file +```