@appxdigital/appx-core-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,32 @@
1
+ import { Global, Module } from '@nestjs/common';
2
+ import { PrismaClient } from '@prisma/client';
3
+ import { PERMISSIONS_CONFIG_TOKEN, PermissionsConfigType, PrismaService } from '@appxdigital/appx-core';
4
+ import { PermissionsConfig } from '../config/permissions.config';
5
+
6
+ @Global()
7
+ @Module({
8
+ providers: [
9
+ {
10
+ provide: PrismaClient,
11
+ useFactory: () => {
12
+ const prisma = new PrismaClient();
13
+ prisma.$connect();
14
+ return prisma;
15
+ },
16
+ },
17
+ {
18
+ provide: PERMISSIONS_CONFIG_TOKEN,
19
+ useValue: PermissionsConfig,
20
+ },
21
+ {
22
+ provide: PrismaService,
23
+ useFactory: (prismaClient: PrismaClient, permissionsConfig: PermissionsConfigType) => {
24
+ return new PrismaService(prismaClient, permissionsConfig);
25
+ },
26
+ inject: [PrismaClient, PERMISSIONS_CONFIG_TOKEN],
27
+ },
28
+ ],
29
+ exports: [PrismaService, PERMISSIONS_CONFIG_TOKEN],
30
+ })
31
+ export class PrismaModule {
32
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "dependencies": {
3
+ "@prisma/client": "^6.5.0",
4
+ "prisma-nestjs-graphql": "20.0.3",
5
+ "prisma-case-format": "2.2.1",
6
+ "class-validator": "0.14.1",
7
+ "class-transformer": "0.5.1",
8
+ "nestjs-request-context": "4.0.0",
9
+ "passport": "0.7.0",
10
+ "passport-local": "1.0.0",
11
+ "express-session": "1.18.1",
12
+ "express": "4.21.1",
13
+ "argon2": "0.41.1",
14
+ "rxjs": "7.8.1",
15
+ "reflect-metadata": "0.2.0"
16
+ },
17
+ "devDependencies": {
18
+ "@types/express": "5.0.0",
19
+ "prisma": "5.22.0",
20
+ "@types/express-formidable": "1.2.3",
21
+ "@types/inquirer": "9.0.7",
22
+ "@types/multer": "1.4.12",
23
+ "@types/passport-local": "1.0.38",
24
+ "typescript": "5.1.3"
25
+ }
26
+ }
@@ -0,0 +1,169 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import inquirer from 'inquirer';
4
+
5
+ const MimeTypes = {
6
+ image: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
7
+ document: [
8
+ 'application/pdf',
9
+ 'application/msword',
10
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
11
+ ],
12
+ video: ['video/mp4', 'video/avi', 'video/mpeg'],
13
+ audio: ['audio/mpeg', 'audio/wav', 'audio/ogg'],
14
+ };
15
+
16
+ function getEnvVariables(provider, providerOptions) {
17
+ if (provider === 'aws') {
18
+ return {
19
+ AWS_BUCKET_NAME: providerOptions.bucket,
20
+ AWS_REGION: providerOptions.region,
21
+ AWS_ACCESS_KEY_ID: providerOptions.accessKeyId,
22
+ AWS_SECRET_ACCESS_KEY: providerOptions.secretAccessKey,
23
+ };
24
+ } else if (provider === 'gcp') {
25
+ return {
26
+ GCP_BUCKET_NAME: providerOptions.bucket,
27
+ GCP_PROJECT_ID: providerOptions.projectId,
28
+ GCP_KEY_FILE_PATH: providerOptions.keyFilePath,
29
+ };
30
+ } else {
31
+ return {};
32
+ }
33
+ }
34
+
35
+ export async function gatherFileUploadSettings() {
36
+ const config = {};
37
+
38
+ const { provider } = await inquirer.prompt([
39
+ {
40
+ type: 'list',
41
+ name: 'provider',
42
+ message: 'Select the storage provider:',
43
+ choices: ['aws', 'gcp', 'local'],
44
+ },
45
+ ]);
46
+ config.provider = provider;
47
+
48
+ if (provider === 'aws') {
49
+ config.providerOptions = await inquirer.prompt([
50
+ { type: 'input', name: 'bucket', message: 'Enter AWS S3 bucket name:' },
51
+ { type: 'input', name: 'region', message: 'Enter AWS region:' },
52
+ { type: 'input', name: 'accessKeyId', message: 'Enter AWS access key ID:' },
53
+ { type: 'input', name: 'secretAccessKey', message: 'Enter AWS secret access key:' },
54
+ ]);
55
+ } else if (provider === 'gcp') {
56
+ config.providerOptions = await inquirer.prompt([
57
+ { type: 'input', name: 'bucket', message: 'Enter GCP bucket name:' },
58
+ { type: 'input', name: 'projectId', message: 'Enter GCP project ID:' },
59
+ { type: 'input', name: 'keyFilePath', message: 'Enter the path to the GCP service account key file:' },
60
+ ]);
61
+ }
62
+
63
+ const { endpoint, maxSize, fileType, multiple } = await inquirer.prompt([
64
+ {
65
+ type: 'input',
66
+ name: 'endpoint',
67
+ message: 'Enter the endpoint (e.g., "avatar" for /upload/avatar):',
68
+ },
69
+ {
70
+ type: 'input',
71
+ name: 'maxSize',
72
+ message: 'Enter the maximum file size (in MB):',
73
+ validate: (input) =>
74
+ !isNaN(Number(input)) && Number(input) > 0
75
+ ? true
76
+ : 'Please enter a valid positive number',
77
+ filter: (input) => Number(input) * 1024 * 1024,
78
+ },
79
+ {
80
+ type: 'list',
81
+ name: 'fileType',
82
+ message: 'Select file category:',
83
+ choices: ['image', 'document', 'video', 'audio'],
84
+ },
85
+ {
86
+ type: 'confirm',
87
+ name: 'multiple',
88
+ message: 'Allow multiple file uploads?',
89
+ default: false,
90
+ },
91
+ ]);
92
+
93
+ config.endpoint = endpoint;
94
+ config.maxSize = maxSize;
95
+ config.fileType = fileType;
96
+ config.multiple = multiple;
97
+
98
+ if (fileType in MimeTypes) {
99
+ const { selectedMimeTypes } = await inquirer.prompt([
100
+ {
101
+ type: 'checkbox',
102
+ name: 'selectedMimeTypes',
103
+ message: `Select allowed MIME types for ${fileType}:`,
104
+ choices: MimeTypes[fileType],
105
+ validate: (choices) => (choices.length > 0 ? true : 'You must select at least one MIME type.'),
106
+ },
107
+ ]);
108
+ config.allowedTypes = selectedMimeTypes;
109
+ }
110
+
111
+ const { roles } = await inquirer.prompt([
112
+ {
113
+ type: 'input',
114
+ name: 'roles',
115
+ message: 'Enter allowed roles (comma-separated, or "ALL" for unrestricted access):',
116
+ filter: (input) => input.split(',').map((role) => role.trim()),
117
+ },
118
+ ]);
119
+ config.roles = roles;
120
+
121
+ return config;
122
+ }
123
+
124
+ export function insertFileUploadConfiguration(config, projectPath) {
125
+ const providerOptions = config.providerOptions || {};
126
+ const envVariables = getEnvVariables(config.provider, providerOptions);
127
+
128
+ const envContent = Object.entries(envVariables)
129
+ .map(([key, value]) => `${key}=${value}`)
130
+ .join('\n');
131
+
132
+ const devEnvPath = path.resolve(projectPath, '.env');
133
+ if (envContent) {
134
+ fs.appendFileSync(devEnvPath, `\n${envContent}\n`);
135
+ }
136
+
137
+ const configContent = `import { FileUploadModuleOptions } from '@appxdigital/appx-core';
138
+
139
+ export const fileUploadConfig: FileUploadModuleOptions = {
140
+ cloudProvider: '${config.provider}',
141
+ endpoints: [
142
+ {
143
+ endpoint: '/upload/${config.endpoint}',
144
+ aliases: [],
145
+ maxSize: ${config.maxSize},
146
+ allowedTypes: ${JSON.stringify(config.allowedTypes, null, 8)},
147
+ multiple: ${config.multiple},
148
+ roles: ${JSON.stringify(config.roles, null, 8)},
149
+ },
150
+ ],
151
+ };`;
152
+
153
+ const configPath = path.resolve(projectPath, 'src/config/file-upload.config.ts');
154
+ fs.writeFileSync(configPath, configContent);
155
+
156
+ const appModulePath = path.resolve(projectPath, 'src/app.module.ts');
157
+ let appModuleContent = fs.readFileSync(appModulePath, 'utf-8');
158
+
159
+ if (!appModuleContent.includes(`import { fileUploadConfig } from './config/file-upload.config';`)) {
160
+ appModuleContent = `import { fileUploadConfig } from './config/file-upload.config';\n` + appModuleContent;
161
+ }
162
+
163
+ appModuleContent = appModuleContent.replace(
164
+ /AppxCoreModule\.forRoot\(([^)]+)\)/,
165
+ 'AppxCoreModule.forRoot(PermissionsConfig, fileUploadConfig)'
166
+ );
167
+
168
+ fs.writeFileSync(appModulePath, appModuleContent);
169
+ }