mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
Move api into api subdirectory
This commit is contained in:
151
api/src/cli/commands/create/index.ts
Normal file
151
api/src/cli/commands/create/index.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
import fse from 'fs-extra';
|
||||
import chalk from 'chalk';
|
||||
import inquirer from 'inquirer';
|
||||
import { resolve } from 'path';
|
||||
import { databaseQuestions } from './questions';
|
||||
import { drivers, getDriverForClient } from '../../utils/drivers';
|
||||
import createEnv from '../../utils/create-env';
|
||||
import execa from 'execa';
|
||||
import path from 'path';
|
||||
import { v4 as uuidV4 } from 'uuid';
|
||||
import ora from 'ora';
|
||||
|
||||
import argon2 from 'argon2';
|
||||
|
||||
import runSeed from '../../../database/run-seed';
|
||||
|
||||
import createDBConnection, { Credentials } from '../../utils/create-db-connection';
|
||||
|
||||
export default async function create(directory: string, options: Record<string, any>) {
|
||||
const rootPath = resolve(directory);
|
||||
checkRequirements();
|
||||
|
||||
if (await fse.pathExists(rootPath)) {
|
||||
const stat = await fse.stat(rootPath);
|
||||
|
||||
if (stat.isDirectory() === false) {
|
||||
console.error(
|
||||
`Destination '${chalk.red(directory)}' already exists and is not a directory.`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const files = await fse.readdir(rootPath);
|
||||
|
||||
if (files.length > 0) {
|
||||
console.error(
|
||||
`Destination '${chalk.red(
|
||||
directory
|
||||
)}' already exists and is not an empty directory.`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
await fse.mkdir(rootPath);
|
||||
await fse.mkdir(path.join(rootPath, 'uploads'));
|
||||
await fse.mkdir(path.join(rootPath, 'extensions'));
|
||||
|
||||
console.log('Adding package.json');
|
||||
|
||||
await execa('npm', ['init', '-y'], {
|
||||
cwd: rootPath,
|
||||
stdin: 'ignore',
|
||||
});
|
||||
|
||||
const spinner = ora('Installing Directus').start();
|
||||
|
||||
await execa('npm', ['install', 'directus', '--production', '--no-optional'], {
|
||||
cwd: rootPath,
|
||||
stdin: 'ignore',
|
||||
});
|
||||
|
||||
spinner.stopAndPersist();
|
||||
|
||||
let { client } = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'client',
|
||||
message: 'Choose your database client',
|
||||
choices: Object.values(drivers),
|
||||
},
|
||||
]);
|
||||
|
||||
const dbClient = getDriverForClient(client)!;
|
||||
|
||||
const credentials: Credentials = await inquirer.prompt(
|
||||
(databaseQuestions[dbClient] as any[]).map((question: Function) =>
|
||||
question({ client: dbClient, filepath: rootPath })
|
||||
)
|
||||
);
|
||||
|
||||
console.log(`Installing database...`);
|
||||
|
||||
const db = createDBConnection(dbClient, credentials);
|
||||
|
||||
await runSeed(db, 'system');
|
||||
|
||||
console.log(`Creating the .env file...`);
|
||||
|
||||
await createEnv(dbClient, credentials, rootPath);
|
||||
|
||||
console.log(`Create your first admin user:`);
|
||||
|
||||
const firstUser = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'email',
|
||||
message: 'Email',
|
||||
default: 'admin@example.com',
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'password',
|
||||
message: 'Password',
|
||||
mask: '*',
|
||||
},
|
||||
]);
|
||||
|
||||
firstUser.password = await argon2.hash(firstUser.password);
|
||||
const userID = uuidV4();
|
||||
const roleID = uuidV4();
|
||||
|
||||
await db('directus_roles').insert({
|
||||
id: roleID,
|
||||
name: 'Admin',
|
||||
icon: 'verified_user',
|
||||
admin: true,
|
||||
});
|
||||
|
||||
await db('directus_users').insert({
|
||||
id: userID,
|
||||
status: 'active',
|
||||
email: firstUser.email,
|
||||
password: firstUser.password,
|
||||
first_name: 'Admin',
|
||||
last_name: 'User',
|
||||
role: roleID,
|
||||
});
|
||||
|
||||
db.destroy();
|
||||
|
||||
console.log(`
|
||||
Your project has been created at ${chalk.green(rootPath)}.
|
||||
|
||||
Start Directus by running:
|
||||
${chalk.blue('cd')} ${rootPath}
|
||||
${chalk.blue('npx directus')} start
|
||||
`);
|
||||
}
|
||||
|
||||
function checkRequirements() {
|
||||
const nodeVersion = process.versions.node;
|
||||
const major = +nodeVersion.split('.')[0];
|
||||
|
||||
if (major < 10) {
|
||||
console.error(`You are running Node ${nodeVersion}.`);
|
||||
console.error('Directus requires Node 10 and up.');
|
||||
console.error('Please update your Node version and try again.');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
59
api/src/cli/commands/create/questions.ts
Normal file
59
api/src/cli/commands/create/questions.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import path from 'path';
|
||||
|
||||
const filename = ({ filepath }: { filepath: string }) => ({
|
||||
type: 'input',
|
||||
name: 'filename',
|
||||
message: 'Database File Path:',
|
||||
default: path.join(filepath, 'data.db'),
|
||||
});
|
||||
|
||||
const host = () => ({
|
||||
type: 'input',
|
||||
name: 'host',
|
||||
message: 'Database Host:',
|
||||
default: '127.0.0.1',
|
||||
});
|
||||
|
||||
const port = ({ client }: { client: string }) => ({
|
||||
type: 'input',
|
||||
name: 'port',
|
||||
message: 'Port:',
|
||||
default() {
|
||||
const ports: Record<string, number> = {
|
||||
pg: 5432,
|
||||
mysql: 3306,
|
||||
oracledb: 1521,
|
||||
mssql: 1433,
|
||||
};
|
||||
|
||||
return ports[client];
|
||||
},
|
||||
});
|
||||
|
||||
const database = () => ({
|
||||
type: 'input',
|
||||
name: 'database',
|
||||
message: 'Database Name:',
|
||||
default: 'directus',
|
||||
});
|
||||
|
||||
const user = () => ({
|
||||
type: 'input',
|
||||
name: 'user',
|
||||
message: 'Database User:',
|
||||
});
|
||||
|
||||
const password = () => ({
|
||||
type: 'password',
|
||||
name: 'password',
|
||||
message: 'Database Password:',
|
||||
mask: '*',
|
||||
});
|
||||
|
||||
export const databaseQuestions = {
|
||||
sqlite3: [filename],
|
||||
mysql: [host, port, database, user, password],
|
||||
pg: [host, port, database, user, password],
|
||||
oracledb: [host, port, database, user, password],
|
||||
mssql: [host, port, database, user, password],
|
||||
};
|
||||
14
api/src/cli/commands/start.ts
Normal file
14
api/src/cli/commands/start.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import logger from '../../logger';
|
||||
import getPort from 'get-port';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
export default async function start() {
|
||||
dotenv.config();
|
||||
const app = require('../../app').default;
|
||||
|
||||
const port = process.env.PORT || (await getPort({ port: 3000 }));
|
||||
|
||||
app.listen(port, () => {
|
||||
logger.info(`Server started at port ${port}`);
|
||||
});
|
||||
}
|
||||
17
api/src/cli/index.ts
Normal file
17
api/src/cli/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import program from 'commander';
|
||||
|
||||
const pkg = require('../../package.json');
|
||||
|
||||
import start from './commands/start';
|
||||
import create from './commands/create';
|
||||
|
||||
program.version(pkg.version, '-v, --version');
|
||||
|
||||
program.name('directus').usage('[command] [options]');
|
||||
|
||||
program.command('create <directory>').description('Create a new Directus Project').action(create);
|
||||
program.command('start').description('Start the Directus API').action(start);
|
||||
|
||||
program.parseAsync(process.argv);
|
||||
51
api/src/cli/utils/create-db-connection.ts
Normal file
51
api/src/cli/utils/create-db-connection.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import knex, { Config } from 'knex';
|
||||
import path from 'path';
|
||||
|
||||
export type Credentials = {
|
||||
filename?: string;
|
||||
host?: string;
|
||||
port?: number;
|
||||
database?: string;
|
||||
user?: string;
|
||||
password?: string;
|
||||
};
|
||||
export default function createDBConnection(
|
||||
client: 'sqlite3' | 'mysql' | 'pg' | 'oracledb' | 'mssql',
|
||||
credentials: Credentials
|
||||
) {
|
||||
let connection: Config['connection'] = {};
|
||||
|
||||
if (client === 'sqlite3') {
|
||||
const { filename } = credentials;
|
||||
|
||||
connection = {
|
||||
filename: filename as string,
|
||||
};
|
||||
} else {
|
||||
const { host, port, database, user, password } = credentials as Credentials;
|
||||
|
||||
connection = {
|
||||
host: host,
|
||||
port: port,
|
||||
database: database,
|
||||
user: user,
|
||||
password: password,
|
||||
};
|
||||
}
|
||||
|
||||
const knexConfig: Config = {
|
||||
client: client,
|
||||
connection: connection,
|
||||
seeds: {
|
||||
extension: 'js',
|
||||
directory: path.resolve(__dirname, '../../database/seeds/'),
|
||||
},
|
||||
};
|
||||
|
||||
if (client === 'sqlite3') {
|
||||
knexConfig.useNullAsDefault = true;
|
||||
}
|
||||
|
||||
const db = knex(knexConfig);
|
||||
return db;
|
||||
}
|
||||
34
api/src/cli/utils/create-env/env-stub.liquid
Normal file
34
api/src/cli/utils/create-env/env-stub.liquid
Normal file
@@ -0,0 +1,34 @@
|
||||
####################################################################################################
|
||||
# General
|
||||
|
||||
{{ general }}
|
||||
|
||||
####################################################################################################
|
||||
# Database
|
||||
|
||||
{{ database }}
|
||||
|
||||
####################################################################################################
|
||||
# File Storage
|
||||
|
||||
{{ storage }}
|
||||
|
||||
####################################################################################################
|
||||
# Security
|
||||
|
||||
{{ security }}
|
||||
|
||||
####################################################################################################
|
||||
# SSO (OAuth) Providers
|
||||
|
||||
{{ oauth }}
|
||||
|
||||
####################################################################################################
|
||||
# Extensions
|
||||
|
||||
{{ extensions }}
|
||||
|
||||
####################################################################################################
|
||||
# Email
|
||||
|
||||
{{ email }}
|
||||
79
api/src/cli/utils/create-env/index.ts
Normal file
79
api/src/cli/utils/create-env/index.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { drivers } from '../drivers';
|
||||
import { Credentials } from '../create-db-connection';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { Liquid } from 'liquidjs';
|
||||
import fs from 'fs';
|
||||
import { promisify } from 'util';
|
||||
import path from 'path';
|
||||
|
||||
const readFile = promisify(fs.readFile);
|
||||
const writeFile = promisify(fs.writeFile);
|
||||
|
||||
const liquidEngine = new Liquid({
|
||||
extname: '.liquid',
|
||||
});
|
||||
|
||||
const defaults = {
|
||||
general: {
|
||||
PORT: 3000,
|
||||
PUBLIC_URL: '/',
|
||||
},
|
||||
storage: {
|
||||
STORAGE_LOCATIONS: 'local',
|
||||
STORAGE_LOCAL_PUBLIC_URL: '/uploads',
|
||||
STORAGE_LOCAL_DRIVER: 'local',
|
||||
STORAGE_LOCAL_ROOT: './uploads',
|
||||
},
|
||||
security: {
|
||||
UUID: uuidv4(),
|
||||
SECRET: nanoid(32),
|
||||
ACCESS_TOKEN_TTL: '15m',
|
||||
REFRESH_TOKEN_TTL: '7d',
|
||||
REFRESH_TOKEN_COOKIE_SECURE: false,
|
||||
REFRESH_TOKEN_COOKIE_SAME_SITE: 'lax',
|
||||
},
|
||||
oauth: {
|
||||
OAUTH_PROVIDERS: '',
|
||||
},
|
||||
extensions: {
|
||||
EXTENSIONS_PATH: './extensions',
|
||||
},
|
||||
email: {
|
||||
EMAIL_FROM: 'no-reply@directus.io',
|
||||
EMAIL_TRANSPORT: 'sendmail',
|
||||
EMAIL_SENDMAIL_NEW_LINE: 'unix',
|
||||
EMAIL_SENDMAIL_PATH: '/usr/sbin/sendmail',
|
||||
},
|
||||
};
|
||||
|
||||
export default async function createEnv(
|
||||
client: keyof typeof drivers,
|
||||
credentials: Credentials,
|
||||
directory: string
|
||||
) {
|
||||
const config: Record<string, any> = {
|
||||
...defaults,
|
||||
database: {
|
||||
DB_CLIENT: client,
|
||||
},
|
||||
};
|
||||
|
||||
for (const [key, value] of Object.entries(credentials)) {
|
||||
config.database[`DB_${key.toUpperCase()}`] = value;
|
||||
}
|
||||
|
||||
const configAsStrings: any = {};
|
||||
|
||||
for (const [key, value] of Object.entries(config)) {
|
||||
configAsStrings[key] = '';
|
||||
|
||||
for (const [envKey, envValue] of Object.entries(value)) {
|
||||
configAsStrings[key] += `${envKey}="${envValue}"\n`;
|
||||
}
|
||||
}
|
||||
|
||||
const templateString = await readFile(path.join(__dirname, 'env-stub.liquid'), 'utf8');
|
||||
const text = await liquidEngine.parseAndRender(templateString, configAsStrings);
|
||||
await writeFile(path.join(directory, '.env'), text);
|
||||
}
|
||||
15
api/src/cli/utils/drivers.ts
Normal file
15
api/src/cli/utils/drivers.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export const drivers = {
|
||||
sqlite3: 'SQLite',
|
||||
mysql: 'MySQL (/ MariaDB / Aurora)',
|
||||
pg: 'PostgreSQL (/ Amazon Redshift)',
|
||||
oracledb: 'Oracle Database',
|
||||
mssql: 'Microsoft SQL Server',
|
||||
};
|
||||
|
||||
export function getDriverForClient(client: string): keyof typeof drivers | null {
|
||||
for (const [key, value] of Object.entries(drivers)) {
|
||||
if (value === client) return key as keyof typeof drivers;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user