Jelajahi Sumber

feat(core): Enable users to specify superadmin credentials

Closes #279
Eduardo 5 tahun lalu
induk
melakukan
0f0a1adaa2

+ 2 - 1
packages/core/src/config/config.service.ts

@@ -21,6 +21,7 @@ import {
     PromotionOptions,
     RuntimeVendureConfig,
     ShippingOptions,
+    SuperadminCredentials,
     TaxOptions,
     VendureConfig,
     WorkerOptions,
@@ -57,7 +58,7 @@ export class ConfigService implements VendureConfig {
     get defaultLanguageCode(): LanguageCode {
         return this.activeConfig.defaultLanguageCode;
     }
-    
+
     get entityIdStrategy(): EntityIdStrategy {
         return this.activeConfig.entityIdStrategy;
     }

+ 9 - 1
packages/core/src/config/default-config.ts

@@ -1,6 +1,10 @@
 import { Transport } from '@nestjs/microservices';
 import { LanguageCode } from '@vendure/common/lib/generated-types';
-import { DEFAULT_AUTH_TOKEN_HEADER_KEY } from '@vendure/common/lib/shared-constants';
+import {
+    DEFAULT_AUTH_TOKEN_HEADER_KEY,
+    SUPER_ADMIN_USER_IDENTIFIER,
+    SUPER_ADMIN_USER_PASSWORD,
+} from '@vendure/common/lib/shared-constants';
 
 import { generatePublicId } from '../common/generate-public-id';
 import { InMemoryJobQueueStrategy } from '../job-queue/in-memory-job-queue-strategy';
@@ -58,6 +62,10 @@ export const defaultConfig: RuntimeVendureConfig = {
         sessionDuration: '7d',
         requireVerification: true,
         verificationTokenDuration: '7d',
+        superadminCredentials: {
+            identifier: SUPER_ADMIN_USER_IDENTIFIER,
+            password: SUPER_ADMIN_USER_PASSWORD,
+        },
     },
     catalogOptions: {
         collectionFilters: defaultCollectionFilters,

+ 22 - 0
packages/core/src/config/vendure-config.ts

@@ -218,6 +218,11 @@ export interface AuthOptions {
      * @default '7d'
      */
     verificationTokenDuration?: string | number;
+    /**
+     * @description
+     * Configures the credentials to be used to create a superadmin
+     */
+    superadminCredentials?: SuperadminCredentials;
 }
 
 /**
@@ -405,6 +410,23 @@ export interface ShippingOptions {
     shippingCalculators?: Array<ShippingCalculator<any>>;
 }
 
+/**
+ * @docsCategory superadmin
+ */
+export interface SuperadminCredentials {
+    /**
+     * @description
+     * The identifier to be used to create a superadmin account
+     */
+    identifier: string;
+
+    /**
+     * @description
+     * The password to be used to create a superadmin account
+     */
+    password: string;
+}
+
 /**
  * @description
  * Defines payment-related options in the {@link VendureConfig}.

+ 7 - 4
packages/core/src/service/services/administrator.service.ts

@@ -1,12 +1,12 @@
 import { Injectable } from '@nestjs/common';
 import { InjectConnection } from '@nestjs/typeorm';
 import { CreateAdministratorInput, UpdateAdministratorInput } from '@vendure/common/lib/generated-types';
-import { SUPER_ADMIN_USER_IDENTIFIER, SUPER_ADMIN_USER_PASSWORD } from '@vendure/common/lib/shared-constants';
 import { ID, PaginatedList } from '@vendure/common/lib/shared-types';
 import { Connection } from 'typeorm';
 
 import { EntityNotFoundError } from '../../common/error/errors';
 import { ListQueryOptions } from '../../common/types/common-types';
+import { ConfigService } from '../../config';
 import { Administrator } from '../../entity/administrator/administrator.entity';
 import { User } from '../../entity/user/user.entity';
 import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder';
@@ -20,6 +20,7 @@ import { UserService } from './user.service';
 export class AdministratorService {
     constructor(
         @InjectConnection() private connection: Connection,
+        private configService: ConfigService,
         private listQueryBuilder: ListQueryBuilder,
         private passwordCipher: PasswordCiper,
         private userService: UserService,
@@ -108,17 +109,19 @@ export class AdministratorService {
      * no longer be possible.
      */
     private async ensureSuperAdminExists() {
+        const { superadminCredentials } = this.configService.authOptions;
+
         const superAdminUser = await this.connection.getRepository(User).findOne({
             where: {
-                identifier: SUPER_ADMIN_USER_IDENTIFIER,
+                identifier: superadminCredentials.identifier,
             },
         });
 
         if (!superAdminUser) {
             const superAdminRole = await this.roleService.getSuperAdminRole();
             const administrator = await this.create({
-                emailAddress: SUPER_ADMIN_USER_IDENTIFIER,
-                password: SUPER_ADMIN_USER_PASSWORD,
+                emailAddress: superadminCredentials.identifier,
+                password: superadminCredentials.password,
                 firstName: 'Super',
                 lastName: 'Admin',
                 roleIds: [superAdminRole.id as string],

+ 1 - 1
packages/create/package.json

@@ -26,13 +26,13 @@
     "@types/handlebars": "^4.1.0",
     "@types/listr": "^0.14.0",
     "@types/semver": "^6.0.0",
-    "@vendure/common": "^0.12.3",
     "@vendure/core": "^0.12.5",
     "rimraf": "^3.0.0",
     "ts-node": "^8.4.1",
     "typescript": "3.8.3"
   },
   "dependencies": {
+    "@vendure/common": "^0.12.3",
     "chalk": "^3.0.0",
     "commander": "^5.0.0",
     "cross-spawn": "^7.0.1",

+ 24 - 4
packages/create/src/gather-user-responses.ts

@@ -1,3 +1,4 @@
+import { SUPER_ADMIN_USER_IDENTIFIER, SUPER_ADMIN_USER_PASSWORD } from '@vendure/common/lib/shared-constants';
 import fs from 'fs-extra';
 import Handlebars from 'handlebars';
 import path from 'path';
@@ -69,7 +70,10 @@ export async function gatherUserResponses(root: string): Promise<UserResponses>
                 type: 'select',
                 name: 'language',
                 message: 'Which programming language will you be using?',
-                choices: [{ title: 'TypeScript', value: 'ts' }, { title: 'JavaScript', value: 'js' }],
+                choices: [
+                    { title: 'TypeScript', value: 'ts' },
+                    { title: 'JavaScript', value: 'js' },
+                ],
                 initial: 0 as any,
             },
             {
@@ -80,6 +84,18 @@ export async function gatherUserResponses(root: string): Promise<UserResponses>
                 active: 'yes',
                 inactive: 'no',
             },
+            {
+                type: 'text',
+                name: 'superadminIdentifier',
+                message: 'What identifier do you want to use for the superadmin user?',
+                initial: SUPER_ADMIN_USER_IDENTIFIER,
+            },
+            {
+                type: 'text',
+                name: 'superadminPassword',
+                message: 'What password do you want to use for the superadmin user?',
+                initial: SUPER_ADMIN_USER_PASSWORD,
+            },
         ],
         {
             onSubmit,
@@ -110,6 +126,8 @@ export async function gatherUserResponses(root: string): Promise<UserResponses>
         usingTs: answers.language === 'ts',
         dbType: answers.dbType,
         populateProducts: answers.populateProducts,
+        superadminIdentifier: answers.superadminIdentifier,
+        superadminPassword: answers.superadminPassword,
     };
 }
 
@@ -126,6 +144,8 @@ export async function gatherCiUserResponses(root: string): Promise<UserResponses
         dbPassword: '',
         language: 'ts',
         populateProducts: true,
+        superadminIdentifier: SUPER_ADMIN_USER_IDENTIFIER,
+        superadminPassword: SUPER_ADMIN_USER_PASSWORD,
     };
     const {
         indexSource,
@@ -143,6 +163,8 @@ export async function gatherCiUserResponses(root: string): Promise<UserResponses
         usingTs: ciAnswers.language === 'ts',
         dbType: ciAnswers.dbType,
         populateProducts: ciAnswers.populateProducts,
+        superadminIdentifier: ciAnswers.superadminIdentifier,
+        superadminPassword: ciAnswers.superadminPassword,
     };
 }
 
@@ -168,9 +190,7 @@ async function generateSources(
         isSQLite: answers.dbType === 'sqlite',
         isSQLjs: answers.dbType === 'sqljs',
         requiresConnection: answers.dbType !== 'sqlite' && answers.dbType !== 'sqljs',
-        sessionSecret: Math.random()
-            .toString(36)
-            .substr(3),
+        sessionSecret: Math.random().toString(36).substr(3),
     };
     const configTemplate = await fs.readFile(assetPath('vendure-config.hbs'), 'utf-8');
     const configSource = Handlebars.compile(configTemplate)(templateContext);

+ 2 - 0
packages/create/src/types.ts

@@ -9,6 +9,8 @@ export interface UserResponses {
     configSource: string;
     migrationSource: string;
     readmeSource: string;
+    superadminIdentifier: string;
+    superadminPassword: string;
 }
 
 export type CliLogLevel = 'silent' | 'info' | 'verbose';

+ 4 - 0
packages/create/templates/vendure-config.hbs

@@ -45,6 +45,10 @@ const path = require('path');
     },
     authOptions: {
         sessionSecret: '{{ sessionSecret }}',
+        superadminCredentials: {
+            identifier: '{{ superadminIdentifier }}',
+            password: '{{ superadminPassword }}',
+        },
     },
     dbConnectionOptions: {
         type: '{{ dbType }}',

+ 5 - 1
packages/testing/src/simple-graphql-client.ts

@@ -146,7 +146,11 @@ export class SimpleGraphQLClient {
      * Logs in as the SuperAdmin user.
      */
     async asSuperAdmin() {
-        await this.asUserWithCredentials(SUPER_ADMIN_USER_IDENTIFIER, SUPER_ADMIN_USER_PASSWORD);
+        const { superadminCredentials } = this.vendureConfig.authOptions;
+        await this.asUserWithCredentials(
+            superadminCredentials?.identifier ?? SUPER_ADMIN_USER_IDENTIFIER,
+            superadminCredentials?.password ?? SUPER_ADMIN_USER_PASSWORD,
+        );
     }
 
     /**