cloud-function-cli 1.1.2 → 1.1.4
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.
- package/dist/cli/create.js +9 -0
- package/dist/cli/files.js +108 -0
- package/dist/index.js +22 -0
- package/dist/services/apiClient.js +2 -0
- package/dist/template/api-template.js +10 -4
- package/dist/template/context-types.js +85 -0
- package/package.json +2 -1
package/dist/cli/create.js
CHANGED
|
@@ -8,6 +8,7 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const logger_1 = require("../utils/logger");
|
|
10
10
|
const api_template_1 = require("../template/api-template");
|
|
11
|
+
const context_types_1 = require("../template/context-types");
|
|
11
12
|
const create = async (name) => {
|
|
12
13
|
const parts = name.split('/');
|
|
13
14
|
let group, api;
|
|
@@ -20,6 +21,14 @@ const create = async (name) => {
|
|
|
20
21
|
api = parts[1];
|
|
21
22
|
}
|
|
22
23
|
const apiRootDir = path_1.default.join(process.cwd(), 'api');
|
|
24
|
+
if (!fs_1.default.existsSync(apiRootDir)) {
|
|
25
|
+
fs_1.default.mkdirSync(apiRootDir, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
const typesPath = path_1.default.join(apiRootDir, '_cf.d.ts');
|
|
28
|
+
if (!fs_1.default.existsSync(typesPath)) {
|
|
29
|
+
fs_1.default.writeFileSync(typesPath, context_types_1.contextTypesDts);
|
|
30
|
+
logger_1.logger.success('Created context types: api/_cf.d.ts');
|
|
31
|
+
}
|
|
23
32
|
const groupDir = path_1.default.join(apiRootDir, group);
|
|
24
33
|
if (!fs_1.default.existsSync(groupDir)) {
|
|
25
34
|
fs_1.default.mkdirSync(groupDir, { recursive: true });
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.filesDelete = exports.filesDownload = exports.filesList = exports.filesUpload = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const form_data_1 = __importDefault(require("form-data"));
|
|
10
|
+
const apiClient_1 = require("../services/apiClient");
|
|
11
|
+
const logger_1 = require("../utils/logger");
|
|
12
|
+
const filesUpload = async (filePaths, options) => {
|
|
13
|
+
const resolvedFiles = filePaths
|
|
14
|
+
.map((p) => path_1.default.resolve(process.cwd(), p))
|
|
15
|
+
.filter((p) => fs_1.default.existsSync(p) && fs_1.default.statSync(p).isFile());
|
|
16
|
+
if (resolvedFiles.length === 0) {
|
|
17
|
+
logger_1.logger.error('No valid files to upload');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const form = new form_data_1.default();
|
|
21
|
+
if (options.path)
|
|
22
|
+
form.append('path', options.path);
|
|
23
|
+
resolvedFiles.forEach((p) => {
|
|
24
|
+
form.append('file', fs_1.default.createReadStream(p), { filename: path_1.default.basename(p) });
|
|
25
|
+
});
|
|
26
|
+
try {
|
|
27
|
+
const res = await apiClient_1.apiClient.postRaw('/api/management/files/upload', form, {
|
|
28
|
+
headers: form.getHeaders()
|
|
29
|
+
});
|
|
30
|
+
const items = Array.isArray(res.data?.items) ? res.data.items : [];
|
|
31
|
+
logger_1.logger.success(`Uploaded ${items.length || resolvedFiles.length} file(s)`);
|
|
32
|
+
items.forEach((it) => logger_1.logger.info(` ${it.filename} -> ${it.fileId}`));
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
logger_1.logger.error(`Upload failed: ${error.response?.data?.error || error.message}`);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
exports.filesUpload = filesUpload;
|
|
39
|
+
const filesList = async (options) => {
|
|
40
|
+
const page = options.page ? Number(options.page) : 1;
|
|
41
|
+
const limit = options.limit ? Number(options.limit) : 20;
|
|
42
|
+
try {
|
|
43
|
+
const res = await apiClient_1.apiClient.get('/api/management/files/list', {
|
|
44
|
+
page,
|
|
45
|
+
limit,
|
|
46
|
+
pathPrefix: options.pathPrefix
|
|
47
|
+
});
|
|
48
|
+
const items = Array.isArray(res.data?.items) ? res.data.items : [];
|
|
49
|
+
if (items.length === 0) {
|
|
50
|
+
logger_1.logger.info('No files found');
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
items.forEach((it) => {
|
|
54
|
+
const id = it?._id?.toString ? it._id.toString() : String(it?._id || '');
|
|
55
|
+
logger_1.logger.info(`${id} ${String(it?.filename || '')}`);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
logger_1.logger.error(`List failed: ${error.response?.data?.error || error.message}`);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
exports.filesList = filesList;
|
|
63
|
+
const filesDownload = async (fileId, output) => {
|
|
64
|
+
const safeId = encodeURIComponent(fileId);
|
|
65
|
+
try {
|
|
66
|
+
const res = await apiClient_1.apiClient.getRaw(`/api/management/files/download/${safeId}`, {
|
|
67
|
+
responseType: 'stream'
|
|
68
|
+
});
|
|
69
|
+
const contentDisposition = String(res.headers?.['content-disposition'] || '');
|
|
70
|
+
const filename = parseFilenameFromContentDisposition(contentDisposition) || `${fileId}.bin`;
|
|
71
|
+
const target = output ? path_1.default.resolve(process.cwd(), output) : path_1.default.resolve(process.cwd(), filename);
|
|
72
|
+
await new Promise((resolve, reject) => {
|
|
73
|
+
const writer = fs_1.default.createWriteStream(target);
|
|
74
|
+
res.data.pipe(writer);
|
|
75
|
+
writer.on('finish', () => resolve());
|
|
76
|
+
writer.on('error', reject);
|
|
77
|
+
});
|
|
78
|
+
logger_1.logger.success(`Saved to ${target}`);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
logger_1.logger.error(`Download failed: ${error.response?.data?.error || error.message}`);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
exports.filesDownload = filesDownload;
|
|
85
|
+
const filesDelete = async (fileId) => {
|
|
86
|
+
const safeId = encodeURIComponent(fileId);
|
|
87
|
+
try {
|
|
88
|
+
await apiClient_1.apiClient.delete(`/api/management/files/${safeId}`);
|
|
89
|
+
logger_1.logger.success('Deleted');
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
logger_1.logger.error(`Delete failed: ${error.response?.data?.error || error.message}`);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
exports.filesDelete = filesDelete;
|
|
96
|
+
const parseFilenameFromContentDisposition = (value) => {
|
|
97
|
+
const m = value.match(/filename\*\s*=\s*UTF-8''([^;]+)/i);
|
|
98
|
+
if (m && m[1]) {
|
|
99
|
+
try {
|
|
100
|
+
return decodeURIComponent(m[1]);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return m[1];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const m2 = value.match(/filename\s*=\s*"?([^"]+)"?/i);
|
|
107
|
+
return m2?.[1] ? m2[1] : '';
|
|
108
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ const get_1 = require("./cli/get");
|
|
|
9
9
|
const history_1 = require("./cli/history");
|
|
10
10
|
const rollback_1 = require("./cli/rollback");
|
|
11
11
|
const list_1 = require("./cli/list");
|
|
12
|
+
const files_1 = require("./cli/files");
|
|
12
13
|
const program = new commander_1.Command();
|
|
13
14
|
program
|
|
14
15
|
.name('cf')
|
|
@@ -48,4 +49,25 @@ program
|
|
|
48
49
|
.description('List groups or APIs in a group')
|
|
49
50
|
.option('-e, --env <env>', 'Environment (dev/prod)')
|
|
50
51
|
.action(list_1.list);
|
|
52
|
+
const files = program.command('files').description('Manage uploaded files');
|
|
53
|
+
files
|
|
54
|
+
.command('upload <paths...>')
|
|
55
|
+
.description('Upload file(s) to server (management files)')
|
|
56
|
+
.option('-p, --path <path>', 'Remote path/folder (metadata.path)')
|
|
57
|
+
.action((paths, options) => (0, files_1.filesUpload)(paths, options));
|
|
58
|
+
files
|
|
59
|
+
.command('list')
|
|
60
|
+
.description('List uploaded files')
|
|
61
|
+
.option('--page <page>', 'Page number', '1')
|
|
62
|
+
.option('--limit <limit>', 'Page size', '20')
|
|
63
|
+
.option('--pathPrefix <pathPrefix>', 'Filter by path prefix (metadata.path)')
|
|
64
|
+
.action((options) => (0, files_1.filesList)(options));
|
|
65
|
+
files
|
|
66
|
+
.command('download <fileId> [output]')
|
|
67
|
+
.description('Download a file by fileId')
|
|
68
|
+
.action((fileId, output) => (0, files_1.filesDownload)(fileId, output));
|
|
69
|
+
files
|
|
70
|
+
.command('delete <fileId>')
|
|
71
|
+
.description('Delete a file by fileId')
|
|
72
|
+
.action((fileId) => (0, files_1.filesDelete)(fileId));
|
|
51
73
|
program.parse(process.argv);
|
|
@@ -30,7 +30,9 @@ const getClient = () => {
|
|
|
30
30
|
};
|
|
31
31
|
exports.apiClient = {
|
|
32
32
|
get: (url, params) => getClient().get(url, { params }),
|
|
33
|
+
getRaw: (url, config) => getClient().get(url, config),
|
|
33
34
|
post: (url, data) => getClient().post(url, data),
|
|
35
|
+
postRaw: (url, data, config) => getClient().post(url, data, config),
|
|
34
36
|
put: (url, data) => getClient().put(url, data),
|
|
35
37
|
delete: (url) => getClient().delete(url)
|
|
36
38
|
};
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.apiTemplate = void 0;
|
|
4
|
-
exports.apiTemplate = `
|
|
5
|
-
|
|
4
|
+
exports.apiTemplate = `
|
|
5
|
+
/** @typedef {import('../_cf').CloudFunctionContext} CloudFunctionContext */
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* @param {CloudFunctionContext} context
|
|
9
|
+
*/
|
|
10
|
+
async function handler(context) {
|
|
11
|
+
const { request, libs, packages, files, mongo, cacher, log } = context
|
|
12
|
+
|
|
13
|
+
const query = request.query
|
|
14
|
+
const data = request.body
|
|
9
15
|
|
|
10
16
|
return { code: 1, data: data || query }
|
|
11
17
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.contextTypesDts = void 0;
|
|
4
|
+
exports.contextTypesDts = `export type RuntimeEnvironment = 'dev' | 'prod'
|
|
5
|
+
|
|
6
|
+
export type UploadedFile = {
|
|
7
|
+
fieldname?: string
|
|
8
|
+
originalname?: string
|
|
9
|
+
encoding?: string
|
|
10
|
+
mimetype?: string
|
|
11
|
+
size?: number
|
|
12
|
+
buffer?: any
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type CloudFunctionRequest = {
|
|
16
|
+
body: any
|
|
17
|
+
query: any
|
|
18
|
+
params?: any
|
|
19
|
+
headers?: Record<string, any>
|
|
20
|
+
method?: string
|
|
21
|
+
path?: string
|
|
22
|
+
originalUrl?: string
|
|
23
|
+
url?: string
|
|
24
|
+
files?: UploadedFile[]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type CloudFunctionResponse = {
|
|
28
|
+
status?: (code: number) => any
|
|
29
|
+
json?: (body: any) => any
|
|
30
|
+
send?: (body: any) => any
|
|
31
|
+
setHeader?: (name: string, value: any) => any
|
|
32
|
+
end?: () => any
|
|
33
|
+
headersSent?: boolean
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type FileService = {
|
|
37
|
+
uploadBuffer: (input: { buffer: any; filename: string; contentType?: string; metadata?: Record<string, any> }) => Promise<string>
|
|
38
|
+
findById: (fileId: string) => Promise<any | null>
|
|
39
|
+
openDownloadStream: (fileId: string) => any
|
|
40
|
+
downloadBuffer: (fileId: string) => Promise<any>
|
|
41
|
+
delete: (fileId: string) => Promise<void>
|
|
42
|
+
listFiles: (opts?: { page?: number; limit?: number; pathPrefix?: string }) => Promise<{ items: any[]; page: number; limit: number; total: number }>
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type LibLoader = {
|
|
46
|
+
use: (name: string) => Promise<any>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type Packages = {
|
|
50
|
+
ensure: (spec: string) => Promise<{ name: string; spec: string }>
|
|
51
|
+
import: (nameOrSpec: string) => any
|
|
52
|
+
importOrInstall: (spec: string) => Promise<any>
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export type CloudFunctionContext = {
|
|
56
|
+
log: {
|
|
57
|
+
info: (...args: any[]) => void
|
|
58
|
+
warn: (...args: any[]) => void
|
|
59
|
+
error: (...args: any[]) => void
|
|
60
|
+
debug: (...args: any[]) => void
|
|
61
|
+
}
|
|
62
|
+
cacher: {
|
|
63
|
+
get: (key: string) => Promise<any>
|
|
64
|
+
set: (key: string, value: any, ttlSeconds?: number) => Promise<void>
|
|
65
|
+
del: (key: string) => Promise<void>
|
|
66
|
+
}
|
|
67
|
+
options: {
|
|
68
|
+
group: string
|
|
69
|
+
apiName: string
|
|
70
|
+
option?: string
|
|
71
|
+
environment?: RuntimeEnvironment
|
|
72
|
+
}
|
|
73
|
+
environment?: RuntimeEnvironment
|
|
74
|
+
mongo: {
|
|
75
|
+
getDB: (dbName?: string) => any
|
|
76
|
+
}
|
|
77
|
+
files: FileService
|
|
78
|
+
libs: LibLoader
|
|
79
|
+
packages: Packages
|
|
80
|
+
http: any
|
|
81
|
+
https: any
|
|
82
|
+
request: CloudFunctionRequest
|
|
83
|
+
response: CloudFunctionResponse
|
|
84
|
+
}
|
|
85
|
+
`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloud-function-cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"description": "CLI for Cloud Function System - Create, deploy, and manage serverless functions",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
"chalk": "^4.1.2",
|
|
51
51
|
"commander": "^11.1.0",
|
|
52
52
|
"dotenv": "^16.4.1",
|
|
53
|
+
"form-data": "^4.0.4",
|
|
53
54
|
"inquirer": "^8.2.6"
|
|
54
55
|
},
|
|
55
56
|
"devDependencies": {
|