Skip to content

Commit fa3fa5c

Browse files
committed
fix: enhance project initialization and menu transformation capabilities
1 parent 0f81067 commit fa3fa5c

5 files changed

Lines changed: 176 additions & 62 deletions

File tree

README.md

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,35 +37,37 @@ Why AdminForth:
3737

3838
## Project initialisation
3939

40-
To create an AdminForth project, run:
40+
AdminForth supports two setup paths:
4141

42-
```bash
43-
npx adminforth create-app
44-
```
42+
### Path 1: Existing database
4543

46-
During the interactive initialization process, AdminForth will ask you to provide a local database URL.
44+
Use this path when you already have a database and your own schema or migrations. Provide your database URL with `--db`, or enter it when the CLI asks `Please specify the database URL to use`.
4745

48-
### Integrating AdminForth into your existing application
46+
```bash
47+
npx adminforth create-app --app-name myadmin --db "postgresql://user:password@localhost:5432/dbname"
48+
cd myadmin
49+
```
4950

50-
If you want to build an admin panel for an existing project that already has a database with tables, you can provide the connection URL to your existing development database, such as a local or deployed one.
51+
When you provide your own database URL, AdminForth connects to your database but does not create Prisma schema or migrations for it. The generated project README includes the SQL or schema notes needed to add the required `adminuser` table with your own migration tool.
5152

52-
After that, you may want to generate AdminForth resource files from your existing database tables:
53+
After project creation, generate AdminForth resource files from your existing tables:
5354

5455
```bash
5556
npx adminforth resource
5657
```
5758

58-
Resource files are needed for AdminForth to “know” about your tables and define how to work with them.
59+
### Path 2: New database
5960

60-
Use the command above every time you add new tables or change their schema.
61+
Use this path when you want AdminForth to scaffold a standalone app with a new local SQLite database. Omit `--db`, or accept the default `sqlite://.db.sqlite` value in the interactive prompt:
6162

62-
### Starting from scratch
63-
64-
If you do not have a database yet, start an empty local database, for example PostgreSQL in Docker, and provide its URL to the AdminForth CLI.
65-
66-
If the adminforth CLI does not detect any tables, it will suggest adding Prisma as a migration tool. Prisma is not related to AdminForth, but it is one of the most convenient migration tools.
63+
```bash
64+
npx adminforth create-app --app-name myadmin
65+
cd myadmin
66+
pnpm makemigration --name init && pnpm migrate:local
67+
pnpm dev
68+
```
6769

68-
Please follow [getting started](https://adminforth.dev/docs/tutorial/gettingStarted/).
70+
For the new database path, the CLI can scaffold Prisma files and migration scripts for the default `sqlite://.db.sqlite` database. Please follow [getting started](https://adminforth.dev/docs/tutorial/gettingStarted/) for the full guide.
6971

7072
# For AdminForth developers
7173

adminforth/commands/createApp/templates/readme.md.hbs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,21 @@ Install dependencies:
66
{{packageManager}} install
77
```
88

9+
{{#if adminUserTableInstructions}}
10+
Prepare the admin users table in your existing database before starting the app. AdminForth uses this table for back-office authentication, and your own migration tool should own this schema change:
11+
12+
{{{adminUserTableInstructions}}}
13+
14+
The generated app will seed the default `adminforth` / `adminforth` user on first start if the table is empty.
15+
{{/if}}
16+
17+
{{#if prismaDbUrl}}
918
Migrate the database:
1019

1120
```bash
1221
{{packageManagerRun}} migrate:local
1322
```
23+
{{/if}}
1424

1525
Start the server:
1626

adminforth/commands/createApp/utils.js

Lines changed: 86 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ function detectAdminforthVersion() {
3939
const adminforthVersion = detectAdminforthVersion();
4040
const SUPPORTED_DB_URL_SCHEMES = ['sqlite://', 'postgresql://', 'mongodb://', 'mysql://', 'clickhouse://'];
4141
const PRISMA_MIGRATION_DB_PROTOCOLS = ['sqlite', 'postgres', 'postgresql', 'mysql'];
42+
const DEFAULT_DB_URL = 'sqlite://.db.sqlite';
4243

4344

4445
export function parseArgumentsIntoOptions(rawArgs) {
@@ -57,10 +58,69 @@ export function parseArgumentsIntoOptions(rawArgs) {
5758
return {
5859
appName: args['--app-name'],
5960
db: args['--db'],
61+
dbProvided: args['--db'] !== undefined,
6062
useNpm: args['--use-npm'],
6163
};
6264
}
6365

66+
function generateAdminUserTableInstructions(provider) {
67+
if (provider === 'postgresql') {
68+
return `\`\`\`sql
69+
CREATE TABLE adminuser (
70+
id TEXT PRIMARY KEY,
71+
email TEXT NOT NULL UNIQUE,
72+
password_hash TEXT NOT NULL,
73+
role TEXT NOT NULL,
74+
created_at TIMESTAMP NOT NULL
75+
);
76+
\`\`\``;
77+
}
78+
79+
if (provider === 'mysql') {
80+
return `\`\`\`sql
81+
CREATE TABLE adminuser (
82+
id VARCHAR(191) PRIMARY KEY,
83+
email VARCHAR(191) NOT NULL UNIQUE,
84+
password_hash TEXT NOT NULL,
85+
role VARCHAR(191) NOT NULL,
86+
created_at DATETIME NOT NULL
87+
);
88+
\`\`\``;
89+
}
90+
91+
if (provider === 'sqlite') {
92+
return `\`\`\`sql
93+
CREATE TABLE adminuser (
94+
id TEXT PRIMARY KEY,
95+
email TEXT NOT NULL UNIQUE,
96+
password_hash TEXT NOT NULL,
97+
role TEXT NOT NULL,
98+
created_at DATETIME NOT NULL
99+
);
100+
\`\`\``;
101+
}
102+
103+
if (provider === 'clickhouse') {
104+
return `\`\`\`sql
105+
CREATE TABLE adminuser (
106+
id String,
107+
email String,
108+
password_hash String,
109+
role String,
110+
created_at DateTime
111+
)
112+
ENGINE = MergeTree()
113+
ORDER BY id;
114+
\`\`\``;
115+
}
116+
117+
if (provider === 'mongodb') {
118+
return 'Create an `adminuser` collection with `id`, `email`, `password_hash`, `role`, and `created_at` fields. Keep `email` unique in your own schema/index setup.';
119+
}
120+
121+
return null;
122+
}
123+
64124
export async function promptForMissingOptions(options) {
65125
const questions = [];
66126

@@ -78,7 +138,7 @@ export async function promptForMissingOptions(options) {
78138
type: 'input',
79139
name: 'db',
80140
message: 'Please specify the database URL to use >',
81-
default: 'sqlite://.db.sqlite',
141+
default: DEFAULT_DB_URL,
82142
});
83143
};
84144

@@ -102,25 +162,8 @@ export async function promptForMissingOptions(options) {
102162
db: options.db || answers.db,
103163
useNpm: options.useNpm || answers.useNpm,
104164
};
105-
106-
if (
107-
resolvedOptions.includePrismaMigrations === undefined &&
108-
isPrismaMigrationDbUrl(resolvedOptions.db)
109-
) {
110-
const prismaAnswer = await inquirer.prompt([{
111-
type: 'select',
112-
name: 'includePrismaMigrations',
113-
message: 'Include Prisma migrations? >',
114-
choices: [
115-
{ name: 'Yes', value: true },
116-
{ name: 'No', value: false },
117-
],
118-
default: true,
119-
}]);
120-
resolvedOptions.includePrismaMigrations = prismaAnswer.includePrismaMigrations;
121-
} else {
122-
resolvedOptions.includePrismaMigrations = Boolean(resolvedOptions.includePrismaMigrations);
123-
}
165+
resolvedOptions.existingDb = options.dbProvided || resolvedOptions.db !== DEFAULT_DB_URL;
166+
resolvedOptions.includePrismaMigrations = !resolvedOptions.existingDb && isPrismaMigrationDbUrl(resolvedOptions.db);
124167

125168
return resolvedOptions;
126169
}
@@ -262,7 +305,7 @@ async function scaffoldProject(ctx, options, cwd) {
262305
const prismaDbUrlProd = generateDbUrlForPrismaProd(connectionString);
263306

264307

265-
ctx.skipPrismaSetup = !prismaDbUrl;
308+
ctx.skipPrismaSetup = !options.includePrismaMigrations || !prismaDbUrl;
266309
const appName = options.appName;
267310

268311
const filename = fileURLToPath(import.meta.url);
@@ -287,6 +330,7 @@ async function scaffoldProject(ctx, options, cwd) {
287330
prismaDbUrlProd,
288331
appName,
289332
provider,
333+
existingDb: options.existingDb,
290334
nodeMajor: parseInt(process.versions.node.split('.')[0], 10),
291335
sqliteFile: connectionString.protocol.startsWith('sqlite') ? connectionString.host : null,
292336
});
@@ -310,7 +354,7 @@ function getPackageManagerTemplateData(useNpm, nodeMajor) {
310354

311355
async function writeTemplateFiles(dirname, cwd, useNpm, includePrismaMigrations, options) {
312356
const {
313-
dbUrl, prismaDbUrl, appName, provider, nodeMajor,
357+
dbUrl, prismaDbUrl, appName, provider, existingDb, nodeMajor,
314358
dbUrlProd, prismaDbUrlProd, sqliteFile
315359
} = options;
316360
const packageManagerTemplateData = getPackageManagerTemplateData(useNpm, nodeMajor);
@@ -352,7 +396,14 @@ async function writeTemplateFiles(dirname, cwd, useNpm, includePrismaMigrations,
352396
{
353397
src: 'readme.md.hbs',
354398
dest: 'README.md',
355-
data: { dbUrl, prismaDbUrl: resolvedPrismaDbUrl, appName, sqliteFile },
399+
data: {
400+
dbUrl,
401+
prismaDbUrl: resolvedPrismaDbUrl,
402+
appName,
403+
sqliteFile,
404+
existingDb,
405+
adminUserTableInstructions: existingDb ? generateAdminUserTableInstructions(provider) : null,
406+
},
356407
},
357408
{
358409
src: 'AGENTS.md.hbs',
@@ -519,16 +570,19 @@ async function installDependenciesNpm(ctx, cwd) {
519570

520571
function generateFinalInstructionsPnpm(skipPrismaSetup, options) {
521572
let instruction = '⏭️ Run the following commands to get started:\n';
522-
if (!skipPrismaSetup)
523-
instruction += `
573+
instruction += `
524574
${chalk.dim('// Go to the project directory')}
525575
${chalk.dim('$')}${chalk.cyan(` cd ${options.appName}`)}\n`;
526576

527-
if (options.includePrismaMigrations && !skipPrismaSetup)
577+
if (!skipPrismaSetup)
528578
instruction += `
529579
${chalk.dim('// Generate and apply initial migration')}
530580
${chalk.dim('$')}${chalk.cyan(' pnpm makemigration --name init && pnpm migrate:local')}\n`;
531581

582+
if (options.existingDb)
583+
instruction += `
584+
${chalk.dim('// Create the adminuser table in your database using the README instructions')}\n`;
585+
532586
instruction += `
533587
${chalk.dim('// Start dev server with tsx watch for hot-reloading')}
534588
${chalk.dim('$')}${chalk.cyan(' pnpm dev')}\n
@@ -541,16 +595,19 @@ function generateFinalInstructionsPnpm(skipPrismaSetup, options) {
541595

542596
function generateFinalInstructionsNpm(skipPrismaSetup, options) {
543597
let instruction = '⏭️ Run the following commands to get started:\n';
544-
if (!skipPrismaSetup)
545-
instruction += `
598+
instruction += `
546599
${chalk.dim('// Go to the project directory')}
547600
${chalk.dim('$')}${chalk.cyan(` cd ${options.appName}`)}\n`;
548601

549-
if (options.includePrismaMigrations && !skipPrismaSetup)
602+
if (!skipPrismaSetup)
550603
instruction += `
551604
${chalk.dim('// Generate and apply initial migration')}
552605
${chalk.dim('$')}${chalk.cyan(' npm run makemigration -- --name init && npm run migrate:local')}\n`;
553606

607+
if (options.existingDb)
608+
instruction += `
609+
${chalk.dim('// Create the adminuser table in your database using the README instructions')}\n`;
610+
554611
instruction += `
555612
${chalk.dim('// Start dev server with tsx watch for hot-reloading')}
556613
${chalk.dim('$')}${chalk.cyan(' npm run dev')}\n

adminforth/documentation/docs/tutorial/001-gettingStarted.md

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,38 @@ nvm use 20
2121

2222
## Creating an AdminForth Project
2323

24-
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.
24+
The recommended way to get started with AdminForth is via the **`create-app`** CLI, which scaffolds a basic fully functional back-office application. Apart from boilerplate, it creates one resource for users management.
2525

26-
You can provide options directorly:
26+
There are two common setup paths:
27+
28+
### Path 1: Existing Database
29+
30+
Use this path when you already have a database and your own schema or migrations. Pass your database URL with `--db`, or enter it when the CLI asks `Please specify the database URL to use`:
31+
32+
```bash
33+
npx adminforth create-app --app-name myadmin --db "postgresql://user:password@localhost:5432/dbname"
34+
```
35+
36+
When you provide your own database URL, the CLI treats this as your own database. It does not create Prisma schema or Prisma migration scripts for that database. Instead, the generated project README contains the SQL or schema notes for adding the required `adminuser` table with your own migration tool.
37+
38+
After the project is created, navigate into it and generate resources from your existing tables:
39+
40+
```bash
41+
cd myadmin
42+
npx adminforth resource
43+
```
44+
45+
Resource files are needed for AdminForth to know about your tables and define how to work with them. Use `npx adminforth resource` again when you add new tables or change their schema.
46+
47+
### Path 2: New Database
48+
49+
Use this path when you want AdminForth to scaffold a standalone app with a new local SQLite database. Omit `--db`, or accept the default `sqlite://.db.sqlite` value in the interactive prompt:
2750

2851
```bash
29-
npx adminforth create-app --app-name myadmin --db "sqlite://.db.sqlite"
52+
npx adminforth create-app --app-name myadmin
3053
```
3154

32-
Or omit them to be prompted interactively:
55+
Or omit all options to be prompted interactively:
3356

3457
```bash
3558
npx adminforth create-app
@@ -41,6 +64,8 @@ Once the project is created, navigate into its directory:
4164
cd myadmin # or any other name you provided
4265
```
4366

67+
For the new database path, the CLI can scaffold Prisma files and migration scripts for the default SQLite database.
68+
4469
CLI options:
4570

4671
* **`--app-name`** - name for your project. Used in `package.json`, `index.ts` branding, etc. Default value: **`adminforth-app`**.
@@ -69,7 +94,7 @@ myadmin/
6994
│ └── tsconfig.json # Tsconfig for Vue project (adds completion for AdminForth core components)
7095
├── resources
7196
│ └── adminuser.ts # Example resource file for users management
72-
├── schema.prisma # Prisma schema file for database schema
97+
├── schema.prisma # Prisma schema file, generated only for the new database path
7398
├── index.ts # Main entry point: configures AdminForth & starts the server
7499
├── package.json # Project dependencies
75100
├── pnpm-workspace.yaml
@@ -82,15 +107,15 @@ myadmin/
82107

83108
### Initial Migration & Future Migrations
84109

85-
> ☝️ 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 Admin Users resource to implmenet BackOffice authentication.
110+
For the new database path, the CLI creates Prisma files for managing migrations. Prisma is not required by AdminForth itself, but it is a convenient migration tool for standalone projects that do not have database management yet.
86111

87112
CLI will suggest you a command to initialize the database with Prisma:
88113

89114
```bash
90-
pnpm makemigration --name init
115+
pnpm makemigration --name init && pnpm migrate:local
91116
```
92117

93-
This will create a migration file in `migrations` and apply it to the database.
118+
This will create a migration file and apply it to the database.
94119

95120
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:
96121

@@ -100,6 +125,8 @@ pnpm makemigration --name init ; pnpm migrate:local
100125

101126
Other developers need to pull migration and run `pnpm migrate:local` to apply any unapplied migrations.
102127

128+
For the existing database path, use your own migration tool instead. The generated project README shows how to add the required `adminuser` table to your database.
129+
103130
## Run the Server
104131

105132
Now you can run your app:

0 commit comments

Comments
 (0)